On this page

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

AreaWhat shipped
Sandbox lifecycleSandboxesResource.create() with attribution, update(), previewToken(), fork(), waitUntilReady()
Tenant preview domainTenantResource.getPreviewDomain(), setPreviewDomain(), verifyPreviewDomain(), deletePreviewDomain()
Tenant brandingTenantResource.getBranding(), setBranding(), deleteBranding()
WebhooksWebhooksResource.create/list/get/delete/test()
Files advancedSandboxFilesResource.tree(), writeMany(), watch()
Sandbox envSandboxEnvResource.list/set/delete()
ProcessesSandboxProcessesResource.start/list/get/stop/logs()
Templates + forkSandboxTemplatesResource.*, SandboxesResource.fork()
Usage / quotasUsageResource.get(), QuotasResource.set/get/delete()
SharingSandboxShareResource.create/list/revoke()
Workspace membersWorkspaceMembersResource.*, WorkspaceInvitesResource.*
Org invitesOrgInvitesResource.*

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

OptionEnv varDefaultDescription
apiKey (constructor or builder)MIOSA_API_KEY-Workspace key (msk_live_*)
baseUrl(String)-https://api.miosa.ai/api/v1API endpoint
timeout(Duration)-30sPer-request timeout
maxRetries(int)-3Automatic 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()
);
ArgumentTypeDescription
nameStringDisplay name
sizeString"xs", "small", "medium", "large", "xl"
metadataMap<String, String>Arbitrary key-value pairs
attributionMap<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();
KeyDescription
product_nameWhite-label product name shown in UI
logo_urlURL to your logo image
support_urlSupport page URL
support_emailSupport email address
primary_colorHex color for primary UI accents
background_colorHex 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 groupAvailable
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

Where to next

Was this helpful?