The official Java SDK for MIOSA. Requires Java 11+. Blocking methods have async *Async() variants that return CompletableFuture.
Install
What’s new in 0.4.0
| Area | What shipped |
|---|---|
| Sandbox lifecycle | SandboxesResource.create() with attribution, update(), previewToken(), fork(), waitUntilReady() |
| Tenant preview domain | TenantResource.getPreviewDomain(), setPreviewDomain(), verifyPreviewDomain(), deletePreviewDomain() |
| Tenant branding | TenantResource.getBranding(), setBranding(), deleteBranding() |
| Webhooks | WebhooksResource.create/list/get/delete/test() |
| Files advanced | SandboxFilesResource.tree(), writeMany(), watch() |
| Sandbox env | SandboxEnvResource.list/set/delete() |
| Processes | SandboxProcessesResource.start/list/get/stop/logs() |
| Templates + fork | SandboxTemplatesResource.*, SandboxesResource.fork() |
| Usage / quotas | UsageResource.get(), QuotasResource.set/get/delete() |
| Sharing | SandboxShareResource.create/list/revoke() |
| Workspace members | WorkspaceMembersResource.*, WorkspaceInvitesResource.* |
| Org invites | OrgInvitesResource.* |
First request
import ai.miosa.sdk.MiosaClient;
import ai.miosa.sdk.resources.Computer;
import ai.miosa.sdk.types.ExecResult;
public class QuickStart {
public static void main(String[] args) {
MiosaClient client = new MiosaClient("msk_live_...");
// Create a sandbox, run a command, destroy it
Computer sandbox = client.sandboxes().create("quick-exec");
sandbox.start();
ExecResult result = sandbox.bash("python3 -c 'print(1 + 1)'");
System.out.println(result.getOutput()); // "2\n"
sandbox.destroy();
}
} Configure
| Option | Env var | Default | Description |
|---|---|---|---|
apiKey (constructor or builder) | MIOSA_API_KEY | - | Workspace key (msk_live_*) |
baseUrl(String) | - | https://api.miosa.ai/api/v1 | API endpoint |
timeout(Duration) | - | 30s | Per-request timeout |
maxRetries(int) | - | 3 | Automatic retries on 429 / 5xx |
import ai.miosa.sdk.MiosaClient;
import java.time.Duration;
// Builder pattern
MiosaClient client = MiosaClient.builder()
.apiKey("msk_live_...")
.baseUrl("https://api.miosa.ai/api/v1")
.timeout(Duration.ofSeconds(60))
.maxRetries(3)
.build();
// Convenience constructor (equivalent to builder with defaults)
MiosaClient client2 = new MiosaClient("msk_live_..."); Authentication
Keys are sent as Authorization: Bearer <key>. The client validates the key format at construction time.
// From environment variable (recommended for production)
String apiKey = System.getenv("MIOSA_API_KEY");
MiosaClient client = new MiosaClient(apiKey);
// With additional default headers
MiosaClient client2 = MiosaClient.builder()
.apiKey("msk_live_...")
.defaultHeader("X-Tenant-ID", "acme-corp")
.build(); Error handling
import ai.miosa.sdk.exceptions.*;
try {
client.computers().get("nonexistent-id");
} catch (NotFoundException e) {
System.out.println("Computer not found");
} catch (AuthException e) {
System.out.println("Invalid API key");
} catch (PermissionException e) {
System.out.println("Access denied");
} catch (RateLimitException e) {
System.out.printf("Rate limited - retry after %ds%n", e.getRetryAfter());
} catch (ValidationException e) {
System.out.println("Validation failed: " + e.getMessage());
} catch (InsufficientCreditsException e) {
System.out.println("Top up credits at miosa.ai/billing");
} catch (ServerException e) {
System.out.printf("Server error %d%n", e.getStatusCode());
} catch (MiosaException e) {
System.out.printf("API error %d: %s%n", e.getStatusCode(), e.getMessage());
} Streaming events
In-VM events use a WebSocket subscription bound to a computer:
import ai.miosa.sdk.resources.Events.EventSubscriptionOptions;
import ai.miosa.sdk.resources.Events.EventsSubscription;
import java.util.List;
Computer computer = client.computers().get("comp_abc123");
EventsSubscription sub = computer.events().subscribe(
EventSubscriptionOptions.builder()
.subscribe(List.of("file", "process", "window"))
.paths(List.of("/workspace"))
.build()
);
sub.onEvent(event -> {
System.out.printf("[%s] %s%n", event.getType(), event.getPayload());
});
// Close when done
sub.close(); Phase 1-5 API Reference
Sandbox lifecycle (Phase 1)
client.sandboxes() - SandboxesResource
import ai.miosa.sdk.resources.Computer;
import java.util.Map;
import java.util.UUID;
// Create - simple (defaults to miosa-sandbox template, small size)
Computer sandbox = client.sandboxes().create("my-sandbox");
// Create with explicit size
sandbox = client.sandboxes().create("my-sandbox", "medium");
// Create with white-label attribution
Map<String, String> attribution = Map.of(
"external_workspace_id", "ws_smile_dental",
"external_user_id", "usr_dr_smith",
"external_project_id", "proj_landing_page"
);
Map<String, String> metadata = Map.of("plan", "pro");
sandbox = client.sandboxes().create(
"smile-dental", "small", metadata, attribution
);
// Get, list, delete
Computer found = client.sandboxes().get(sandboxId);
var list = client.sandboxes().list();
client.sandboxes().delete(sandboxId);
// Wait until ready (streams SSE, falls back to polling)
boolean ready = client.sandboxes().waitUntilReady(
sandboxId,
SandboxesResource.WaitOptions.builder()
.timeout(Duration.ofSeconds(30))
.stream(true)
.build()
);
// Update mutable fields
Map<String, Object> updates = Map.of(
"name", "updated-name",
"timeout_sec", 600,
"always_on", true
);
var updated = client.sandboxes().update(sandboxId, updates);
// Mint a short-lived preview token
Map<String, Object> tokenInfo = client.sandboxes().previewToken(
sandboxId, 3600, "read"
);
// tokenInfo.get("token"), tokenInfo.get("url"), tokenInfo.get("expires_at")
// Fork from a snapshot or running sandbox
Computer forked = client.sandboxes().fork(sandboxId,
ForkOptions.builder()
.snapshotId("snap_abc")
.name("forked-sandbox")
.externalUserId("usr_123")
.build()
); | Argument | Type | Description |
|---|---|---|
name | String | Display name |
size | String | "xs", "small", "medium", "large", "xl" |
metadata | Map<String, String> | Arbitrary key-value pairs |
attribution | Map<String, String> | External IDs for white-label attribution |
Tenant preview domain (Phase 1)
client.tenant() - TenantResource
// Get current custom preview domain
Map<String, Object> info = client.tenant().getPreviewDomain();
// info.get("domain"), info.get("verified_at"), info.get("cname_target")
// Set custom preview domain
client.tenant().setPreviewDomain("preview.acme.com");
// Trigger DNS verification
Map<String, Object> result = client.tenant().verifyPreviewDomain();
// result.get("verified"), result.get("target"), result.get("records")
// Remove custom domain
client.tenant().deletePreviewDomain(); Tenant branding (Phase 1)
client.tenant() - TenantResource
// Get current branding
Map<String, Object> branding = client.tenant().getBranding();
// Set branding
client.tenant().setBranding(Map.of(
"product_name", "Acme AI",
"logo_url", "https://cdn.acme.com/logo.png",
"support_url", "https://acme.com/support",
"support_email", "help@acme.com",
"primary_color", "#ff6600",
"background_color","#ffffff"
));
// Reset to platform defaults
client.tenant().deleteBranding(); | Key | Description |
|---|---|
product_name | White-label product name shown in UI |
logo_url | URL to your logo image |
support_url | Support page URL |
support_email | Support email address |
primary_color | Hex color for primary UI accents |
background_color | Hex color for desktop background |
Webhooks (Phase 1)
client.webhooks() - WebhooksResource
// Create a webhook
var wh = client.webhooks().create(Map.of(
"url", "https://example.com/webhooks/miosa",
"events", List.of("sandbox.created", "sandbox.ready", "sandbox.destroyed")
));
var list = client.webhooks().list();
var one = client.webhooks().get(whId);
client.webhooks().delete(whId);
// Send a test ping to verify the endpoint is reachable
client.webhooks().test(whId); Supported events: sandbox.created, sandbox.ready, sandbox.destroyed, sandbox.error, computer.started, computer.stopped, computer.error
Files advanced (Phase 2)
client.sandboxFiles(sandboxId) - SandboxFilesResource
var files = client.sandboxFiles(sandboxId);
// Recursive directory tree
Map<String, Object> tree = files.tree("/workspace", true);
// Batch file write
files.writeMany(List.of(
new FileWrite("/workspace/App.java", "public class App {}"),
new FileWrite("/workspace/config.json", "{}")
));
// Watch for file-system changes
files.watch(event -> {
System.out.println(event.getType() + " " + event.getPath());
return true; // return false to stop watching
}); Sandbox environment variables (Phase 2)
client.sandboxEnv(sandboxId) - SandboxEnvResource
var env = client.sandboxEnv(sandboxId);
List<Map<String, Object>> vars = env.list();
env.set("DATABASE_URL", "postgres://...");
env.delete("DATABASE_URL"); Processes (Phase 2)
client.sandboxProcesses(sandboxId) - SandboxProcessesResource
var procs = client.sandboxProcesses(sandboxId);
// Start a long-running background process
var proc = procs.start("npm run dev", Map.of("NODE_ENV", "development"), "dev-server");
var list = procs.list();
var one = procs.get(proc.getPid());
procs.stop(proc.getPid());
String logs = procs.logs(proc.getPid(), 100); Usage and quotas (Phase 3)
client.usage() - client.quotas()
// Usage rollup
var report = client.usage().get(Map.of(
"external_user_id", "usr_123",
"group_by", "external_user_id",
"period", "30d"
));
// Set per-user quotas
client.quotas().set("usr_123", Map.of(
"max_sandboxes", 5,
"max_concurrent", 2,
"max_storage_gb", 10,
"max_credit_cents",10000
));
var quota = client.quotas().get("usr_123");
client.quotas().delete("usr_123"); // revert to tenant default Audit log (Phase 3)
client.auditLog() - AuditLogResource
String after = null;
while (true) {
var page = client.auditLog().list(
b -> b.after(after).limit(50)
);
for (var entry : page.getItems()) {
System.out.println(entry.getAction() + " " + entry.getCreatedAt());
}
if (page.getNextCursor() == null) break;
after = page.getNextCursor();
} Sharing (Phase 4)
client.sandboxShare(sandboxId) - SandboxShareResource
var share = client.sandboxShare(sandboxId);
// Create a public share URL (no API key required to access)
var shareData = share.create(3600, "read");
// shareData.get("share_id"), shareData.get("share_url"), shareData.get("expires_at")
var shares = share.list();
share.revoke(shareId); White-label integrator flow
import ai.miosa.sdk.MiosaClient;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class WhiteLabelExample {
public static void main(String[] args) {
MiosaClient client = new MiosaClient(System.getenv("MIOSA_API_KEY"));
// 1. Configure tenant branding once at startup
client.tenant().setBranding(Map.of(
"product_name", "Acme AI",
"logo_url", "https://cdn.acme.com/logo.png",
"primary_color","#ff6600"
));
// 2. Set and verify a custom preview domain
client.tenant().setPreviewDomain("preview.acme.com");
client.tenant().verifyPreviewDomain();
// 3. Register webhook for lifecycle events
client.webhooks().create(Map.of(
"url", "https://api.acme.com/webhooks/miosa",
"events", List.of("sandbox.created", "sandbox.ready", "sandbox.destroyed")
));
// 4. Cap per-user resources
client.quotas().set("usr_dr_smith", Map.of(
"max_sandboxes", 3,
"max_concurrent", 1
));
// 5. Create a sandbox attributed to a user + project
Map<String, String> attribution = Map.of(
"external_workspace_id", "ws_smile_dental",
"external_user_id", "usr_dr_smith",
"external_project_id", "proj_landing_page"
);
Computer sandbox = client.sandboxes().create(
"smile-dental", "small",
Map.of("plan", "pro"),
attribution
);
// 6. Wait until ready
boolean ready = client.sandboxes().waitUntilReady(
sandbox.getId(),
SandboxesResource.WaitOptions.builder()
.timeout(Duration.ofSeconds(30))
.build()
);
// 7. Write files
client.sandboxFiles(sandbox.getId()).writeMany(List.of(
new FileWrite("/workspace/index.html", "<h1>Hello</h1>")
));
// 8. Mint a preview token for the end user
var tokenInfo = client.sandboxes().previewToken(sandbox.getId(), 3600, "read");
String previewUrl = (String) tokenInfo.get("url");
System.out.println("Preview URL: " + previewUrl);
// 9. Create a public share link
var shareData = client.sandboxShare(sandbox.getId()).create(86400, "read");
String publicUrl = (String) shareData.get("share_url");
System.out.println("Public URL: " + publicUrl);
// 10. Query usage
var report = client.usage().get(Map.of(
"external_user_id", "usr_dr_smith",
"period", "30d"
));
// 11. Clean up
sandbox.destroy();
}
} Common patterns
Async operations
import java.util.concurrent.CompletableFuture;
CompletableFuture<Computer> future = client.sandboxes().createAsync("async-agent");
future
.thenAccept(sandbox -> System.out.println("Created: " + sandbox.getId()))
.exceptionally(err -> {
System.out.println("Failed: " + err.getMessage());
return null;
}); Idempotency key
import java.util.UUID;
Computer sandbox = client.sandboxes().create(
builder -> builder
.name("my-sandbox")
.idempotencyKey(UUID.randomUUID().toString())
); Pagination
String after = null;
while (true) {
var page = client.auditLog().list(
b -> b.after(after).limit(50)
);
for (var entry : page.getItems()) {
System.out.println(entry.getAction() + " " + entry.getCreatedAt());
}
if (page.getNextCursor() == null) break;
after = page.getNextCursor();
} Custom base URL (self-hosted)
MiosaClient client = MiosaClient.builder()
.apiKey("msk_live_...")
.baseUrl("https://api.your-domain.com/api/v1")
.build(); Webhook verification
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;
boolean verifyWebhook(byte[] payload, String signature, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
String expected = HexFormat.of().formatHex(mac.doFinal(payload));
return MessageDigest.isEqual(expected.getBytes(), signature.getBytes());
} Resource coverage
| Resource group | Available |
|---|---|
| Sandboxes (create, get, list, delete, update, previewToken, fork, waitUntilReady) | ✓ |
| Sandbox files (tree, writeMany, watch) | ✓ |
| Sandbox env, processes, share | ✓ |
| Computers (CRUD + lifecycle) | ✓ |
| Computer sub-resources (terminal, env, ports, volumes, logs, auto-stop, metrics, inbox, OSA) | ✓ |
| Desktop (screenshot, click, type, drag, scroll, key, windows) | ✓ |
| Files (upload, download, list, delete) | ✓ |
| Exec | ✓ |
| Deployments (publish, versions, rollback, domains) | ✓ |
| Databases (managed Postgres) | ✓ |
| Storage (object storage) | ✓ |
| Volumes | ✓ |
| Custom domains (flat + per-deployment) | ✓ |
| Functions / cron jobs / health checks / webhooks | ✓ |
| Sandbox templates | ✓ |
| API keys | ✓ |
| Tenant (plan, preview domain, branding) | ✓ |
| Regions / settings | ✓ |
| Dashboard / analytics / audit log / usage / quotas | ✓ |
| Channels / integrations / project integrations | ✓ |
| External keys | ✓ |
| OpenComputers (hosts, jobs, files, tunnels, agents, clusters) | ✓ |
| Admin | ✓ |
| Workspace members + invites | ✓ |
| Org invites | ✓ |
| Snapshots (standalone) | ✓ |
| Builder sessions / community | ✓ |
| Models / completions / embeddings | ✓ |
| MCP | ✓ |
| Credits | ✓ |