On this page

The official Go SDK for MIOSA. Requires Go 1.21+. All methods accept a context.Context for cancellation and timeouts.

Install

go get github.com/Miosa-osa/miosa-go@v0.4.0

What’s new in v0.4.0

AreaWhat shipped
Sandbox lifecycleSandboxes.Update(), Sandboxes.PreviewToken(), Sandboxes.Fork()
Tenant preview domainTenant.GetPreviewDomain(), SetPreviewDomain(), VerifyPreviewDomain(), DeletePreviewDomain()
Tenant brandingTenant.GetBranding(), SetBranding(), DeleteBranding()
WebhooksWebhooks.Create/List/Get/Delete/Test()
Files advancedSandboxFiles.Tree(), WriteMany(), Watch()
Sandbox envSandboxEnv.List/Set/Delete()
ProcessesSandboxProcesses.Start/List/Get/Stop/Logs/Stream()
Templates + forkSandboxTemplates.*, Sandboxes.Fork()
Usage / quotasUsage.Get(), Quotas.Set/Get/Delete()
SharingSandboxShare.Create/List/Revoke()
Workspace membersWorkspaceMembers.*, WorkspaceInvites.*
Org invitesOrgInvites.*

First request

package main

import (
    "context"
    "fmt"

    miosa "github.com/Miosa-osa/miosa-go"
)

func main() {
    ctx := context.Background()
    client := miosa.NewClient("msk_live_...")

    // Create a sandbox, run a command, delete it
    sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
        Name: "quick-exec",
    })
    if err != nil {
        panic(err)
    }

    result, err := client.Exec.Run(ctx, sandbox.ID, miosa.ExecParams{
        Command: "python3 -c 'print(1 + 1)'",
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(result.Output) // "2\n"

    _ = client.Sandboxes.Delete(ctx, sandbox.ID)
}

Configure

OptionEnv varDefaultDescription
apiKey (positional)MIOSA_API_KEY-Workspace key (msk_live_*)
WithBaseURL(u)-https://api.miosa.ai/api/v1API endpoint
WithTimeout(d)-60sPer-request timeout
WithMaxRetries(n)-3Automatic retries on 429 / 5xx
WithHTTPClient(hc)-default http.ClientCustom HTTP client
import (
    "time"
    miosa "github.com/Miosa-osa/miosa-go"
)

client := miosa.NewClient(
    "msk_live_...",
    miosa.WithBaseURL("https://api.miosa.ai/api/v1"),
    miosa.WithTimeout(60 * time.Second),
    miosa.WithMaxRetries(3),
)

Authentication

Keys are passed as Authorization: Bearer <key>. The SDK validates the key format at construction time.

import "os"

client := miosa.NewClient(os.Getenv("MIOSA_API_KEY"))

Error handling

Use errors.As to match typed errors:

import (
    "errors"
    "fmt"
    miosa "github.com/Miosa-osa/miosa-go"
)

_, err := client.Computers.Get(ctx, "nonexistent-id")
if err != nil {
    var notFound *miosa.NotFoundError
    var rateLimit *miosa.RateLimitError
    switch {
    case errors.As(err, &notFound):
        fmt.Println("Computer not found")
    case errors.As(err, &rateLimit):
        fmt.Printf("Rate limited  -  retry after %.0fs\n", rateLimit.RetryAfter)
    default:
        fmt.Println("Error:", err)
    }
}

Streaming events

stream, err := computer.Events.Subscribe(ctx, miosa.EventSubscribeOptions{
    Subscribe: []miosa.EventProducer{
        miosa.ProducerWindow,
        miosa.ProducerFile,
        miosa.ProducerProcess,
    },
    Paths: []string{"/workspace"},
})
if err != nil {
    panic(err)
}
defer stream.Close()

for ev := range stream.C {
    fmt.Printf("[%s] %v\n", ev.Type, ev.Payload)
}

The stream.C channel is closed when the connection closes or the context is cancelled.

Backend orchestration pattern

Use the SDK from your backend when you are building an agent product. The SDK owns the MIOSA resource lifecycle; your agent runtime receives the prompt and does the work inside the assigned sandbox or computer.

func StartAgentRun(ctx context.Context, client *miosa.Client, workspaceID, userID, prompt string) (*miosa.Sandbox, error) {
    sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
        Name:                "workspace-" + workspaceID,
        ExternalWorkspaceID: workspaceID,
        ExternalUserID:      userID,
        TimeoutSec:          3600,
        IdempotencyKey:      uuid.NewString(),
    })
    if err != nil {
        return nil, err
    }

    run, err := client.AgentRuns.Run(ctx, miosa.CreateAgentRunInput{
        SandboxID:  sandbox.ID,
        TargetKind: miosa.AgentRunTargetSandbox,
        Provider:   "claude-code",
        Cwd:        "/workspace",
        Timeout:    1800,
        Prompt:     prompt,
        Metadata: map[string]interface{}{
            "workspace_id": workspaceID,
            "user_id":      userID,
        },
    })
    if err != nil {
        return nil, err
    }
    _ = run

    return sandbox, nil
}

For the full backend architecture, see Backend orchestration and Build an agent-company platform. For the REST shape behind client.AgentRuns, see Agent Runs.


Phase 1-5 API Reference

Sandbox lifecycle (Phase 1)

client.Sandboxes - *SandboxesService

// Create  -  full input struct
sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
    Name:                "my-sandbox",
    Slug:                "acme-backend",
    ExternalUserID:      "usr_123",
    ExternalProjectID:   "proj_456",
    ExternalWorkspaceID: "ws_789",
    Metadata:            map[string]interface{}{"env": "staging"},
    TimeoutSec:          300,
    AlwaysOn:            false,
    IdempotencyKey:      uuid.NewString(),
})

sandbox, err = client.Sandboxes.Get(ctx, sandboxID)
list, err    := client.Sandboxes.List(ctx, miosa.SandboxListParams{State: "running"})
err          = client.Sandboxes.Delete(ctx, sandboxID)

// Update mutable fields
updated, err := client.Sandboxes.Update(ctx, sandboxID, miosa.UpdateSandboxInput{
    Name:      "updated-name",
    Slug:      "new-slug",
    TimeoutSec: 600,
    AlwaysOn:  true,
})

// Mint a short-lived preview token
token, err := client.Sandboxes.PreviewToken(ctx, sandboxID, miosa.PreviewTokenInput{
    ExpiresIn: 3600,
    Scope:     "read",
})
// token.Token, token.URL, token.ExpiresAt

// Fork from a snapshot or running sandbox
forked, err := client.Sandboxes.Fork(ctx, sandboxID, miosa.ForkSandboxInput{
    SnapshotID:     "snap_abc",
    Name:           "forked-sandbox",
    ExternalUserID: "usr_123",
})
FieldTypeDescription
NamestringDisplay name
SlugstringURL-safe identifier
ExternalUserIDstringWhite-label user attribution
ExternalProjectIDstringWhite-label project attribution
ExternalWorkspaceIDstringWhite-label workspace attribution
Metadatamap[string]interface{}Arbitrary key-value pairs
TimeoutSecintLifetime in seconds
AlwaysOnboolStay alive past idle timeout
IdempotencyKeystringDeduplication key for retries

Tenant preview domain (Phase 1)

client.Tenant - *TenantService

// Get current custom preview domain
info, err := client.Tenant.GetPreviewDomain(ctx)
// info.Domain, info.VerifiedAt, info.CnameTarget

// Set custom preview domain
updated, err := client.Tenant.SetPreviewDomain(ctx, "preview.acme.com")

// Trigger DNS verification
result, err := client.Tenant.VerifyPreviewDomain(ctx)
// result["verified"], result["target"], result["records"]

// Remove custom domain
err = client.Tenant.DeletePreviewDomain(ctx)

Tenant branding (Phase 1)

client.Tenant - *TenantService

// Get current branding
branding, err := client.Tenant.GetBranding(ctx)

// Set branding
updated, err := client.Tenant.SetBranding(ctx, miosa.BrandingData{
    ProductName:     "Acme AI",
    LogoURL:         "https://cdn.acme.com/logo.png",
    SupportURL:      "https://acme.com/support",
    SupportEmail:    "help@acme.com",
    PrimaryColor:    "#ff6600",
    BackgroundColor: "#ffffff",
})

// Reset to platform defaults
err = client.Tenant.DeleteBranding(ctx)

Webhooks (Phase 1)

client.Webhooks - *WebhooksService

// Create a webhook
wh, err := client.Webhooks.Create(ctx, miosa.CreateWebhookInput{
    URL:    "https://example.com/webhooks/miosa",
    Events: []string{"sandbox.created", "sandbox.ready", "sandbox.destroyed"},
})

list, err := client.Webhooks.List(ctx)
wh, err    = client.Webhooks.Get(ctx, whID)
err        = client.Webhooks.Delete(ctx, whID)
err        = client.Webhooks.Test(ctx, whID)

Supported events: sandbox.created, sandbox.ready, sandbox.destroyed, sandbox.error, computer.started, computer.stopped, computer.error

Files advanced (Phase 2)

client.SandboxFiles(sandboxID) - *SandboxFilesService

files := client.SandboxFiles(sandboxID)

// Recursive directory tree
tree, err := files.Tree(ctx, "/workspace", 5) // path, depth

// Batch file write
result, err := files.WriteMany(ctx, []miosa.WriteFileInput{
    {Path: "/workspace/app.go", ContentBase64: base64.StdEncoding.EncodeToString([]byte("package main"))},
})

// Watch for file-system changes (SSE channel)
events, err := files.Watch(ctx)
if err != nil {
    panic(err)
}
for ev := range events {
    fmt.Println(ev.Type, ev.Data)
}

Sandbox environment variables (Phase 2)

client.SandboxEnv(sandboxID) - *SandboxEnvService

env := client.SandboxEnv(sandboxID)

vars, err := env.List(ctx)
_, err     = env.Set(ctx, []miosa.EnvVar{{Key: "DATABASE_URL", Value: "postgres://..."}})
err        = env.Delete(ctx, "DATABASE_URL")

Processes (Phase 2)

client.SandboxProcesses(sandboxID) - *SandboxProcessesService

procs := client.SandboxProcesses(sandboxID)

// Start a long-running background process
proc, err := procs.Start(ctx, miosa.StartProcessInput{
    Command: "npm run dev",
    Env:     map[string]string{"NODE_ENV": "development"},
    Name:    "dev-server",
})

list, err := procs.List(ctx)
one, err   := procs.Get(ctx, proc.PID)
err        = procs.Stop(ctx, proc.PID)
logs, err  := procs.Logs(ctx, proc.PID, 100)

// Streaming process output (SSE channel)
stream, err := procs.Stream(ctx, proc.PID)
for ev := range stream {
    fmt.Print(ev.Data)
}

Usage and quotas (Phase 3)

client.Usage - client.Quotas

// Usage rollup
report, err := client.Usage.Get(ctx, miosa.UsageParams{
    ExternalUserID: "usr_123",
    GroupBy:        "external_user_id",
    Period:         "30d",
})

// Set per-user quotas
quota, err := client.Quotas.Set(ctx, "usr_123", miosa.QuotaInput{
    MaxSandboxes:    5,
    MaxConcurrent:   2,
    MaxStorageGB:    10,
    MaxCreditCents:  10000,
})

quota, err = client.Quotas.Get(ctx, "usr_123")
err        = client.Quotas.Delete(ctx, "usr_123")

Audit log (Phase 3)

client.AuditLog

var after string
for {
    page, err := client.AuditLog.List(ctx, miosa.AuditLogListParams{
        After: after,
        Limit: 50,
    })
    if err != nil {
        break
    }
    for _, entry := range page.Items {
        fmt.Println(entry.Action, entry.CreatedAt)
    }
    if page.NextCursor == "" {
        break
    }
    after = page.NextCursor
}

Sharing (Phase 4)

client.SandboxShare(sandboxID) - *SandboxShareService

share := client.SandboxShare(sandboxID)

// Create a public share URL (no API key required to access)
expiresIn := 3600
data, err := share.Create(ctx, miosa.CreateShareInput{
    ExpiresIn: &expiresIn,
    Scope:     "read",
})
// data.ShareID, data.ShareURL, data.ExpiresAt

shares, err := share.List(ctx)
err          = share.Revoke(ctx, shareID)

White-label integrator flow

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/google/uuid"
    miosa "github.com/Miosa-osa/miosa-go"
)

func main() {
    ctx := context.Background()
    client := miosa.NewClient(os.Getenv("MIOSA_API_KEY"))

    // 1. Configure tenant branding once
    client.Tenant.SetBranding(ctx, miosa.BrandingData{
        ProductName:  "Acme AI",
        LogoURL:      "https://cdn.acme.com/logo.png",
        PrimaryColor: "#ff6600",
    })

    // 2. Set and verify a custom preview domain
    client.Tenant.SetPreviewDomain(ctx, "preview.acme.com")
    client.Tenant.VerifyPreviewDomain(ctx)

    // 3. Register webhook
    client.Webhooks.Create(ctx, miosa.CreateWebhookInput{
        URL:    "https://api.acme.com/webhooks/miosa",
        Events: []string{"sandbox.created", "sandbox.ready", "sandbox.destroyed"},
    })

    // 4. Set per-user quotas
    client.Quotas.Set(ctx, "usr_dr_smith", miosa.QuotaInput{
        MaxSandboxes:  3,
        MaxConcurrent: 1,
    })

    // 5. Create a sandbox attributed to a user + project
    sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
        Name:                "smile-dental",
        ExternalUserID:      "usr_dr_smith",
        ExternalProjectID:   "proj_landing_page",
        ExternalWorkspaceID: "ws_smile_dental",
        Metadata:            map[string]interface{}{"plan": "pro"},
        IdempotencyKey:      uuid.NewString(),
    })
    if err != nil {
        panic(err)
    }

    // 6. Write files
    import64 := func(s string) string {
        return base64.StdEncoding.EncodeToString([]byte(s))
    }
    client.SandboxFiles(sandbox.ID).WriteMany(ctx, []miosa.WriteFileInput{
        {Path: "/workspace/index.html", ContentBase64: import64("<h1>Hello</h1>")},
    })

    // 7. Mint a preview token
    expiresIn := 3600
    token, _ := client.Sandboxes.PreviewToken(ctx, sandbox.ID, miosa.PreviewTokenInput{
        ExpiresIn: expiresIn,
        Scope:     "read",
    })
    fmt.Println("Preview URL:", token.URL)

    // 8. Create a public share link
    shareExpiresIn := 86400
    share, _ := client.SandboxShare(sandbox.ID).Create(ctx, miosa.CreateShareInput{
        ExpiresIn: &shareExpiresIn,
        Scope:     "read",
    })
    fmt.Println("Public URL:", share.ShareURL)

    // 9. Query usage
    report, _ := client.Usage.Get(ctx, miosa.UsageParams{
        ExternalUserID: "usr_dr_smith",
        Period:         "30d",
    })
    fmt.Println(report)

    // 10. Clean up
    client.Sandboxes.Delete(ctx, sandbox.ID)
}

Common patterns

Context-based timeouts

ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()

result, err := client.Exec.Run(ctx, computerID, miosa.ExecParams{
    Command: "npm run build",
    Timeout: 80,
})

Idempotency key

import "github.com/google/uuid"

sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
    Name:           "my-sandbox",
    IdempotencyKey: uuid.NewString(),
})

Custom base URL (self-hosted)

client := miosa.NewClient(
    "msk_live_...",
    miosa.WithBaseURL("https://api.your-domain.com/api/v1"),
)

Resource coverage

Resource groupAvailable
Sandboxes (create, get, list, delete, update, previewToken, fork)
Sandbox files (tree, writeMany, watch)
Sandbox env, processes, share
Computers (CRUD + lifecycle)
Computer sub-resources (terminal, env, ports, volumes, logs, auto-stop, metrics)
Desktop
Files (upload, download, list, delete)
Exec
Deployments
Databases (managed Postgres)
Storage (object storage)
Volumes
Custom domains
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
External keys
MCP
Models / completions / embeddings
Command center / builder sessions / community
OpenComputers (hosts, jobs, files, tunnels, agents, clusters)
Workspace members + invites
Org invites
Admin
Snapshots (standalone)
Credits

Where to next

Was this helpful?