Skip to main content

Base URL & Authentication

OpenComputer exposes two API surfaces:
SurfaceBase URLAuth HeaderUse
Control planehttps://app.opencomputer.dev/apiX-API-Key: <key>Create/list/kill sandboxes, templates, hibernation
Worker directconnectURL from create/get responseAuthorization: Bearer <jwt>Exec, files, agents, PTY, timeout
The connectURL and token fields are returned when you create or get a sandbox. Worker-direct routes have no /api prefix — for example, POST {connectURL}/sandboxes/:id/exec/run. SDK users only need the API key. The SDK resolves connectURL and manages the JWT automatically.

Route Availability

Most operational routes are available on both surfaces. Exceptions:
RouteControl PlaneWorker
POST /api/sandboxes (create)Yes
GET /api/sandboxes (list all)Yes
DELETE /api/sandboxes/:id (kill)Yes
POST .../hibernate, .../wakeYes
Checkpoint routesYes
Preview URL routesYes
Template routesYes
POST .../token/refreshYes
POST .../timeoutYes
Exec, filesystem, agent, PTY routesYesYes

Token Refresh

Worker JWTs are short-lived. To refresh:
curl -X POST {connectURL}/sandboxes/{id}/token/refresh \
  -H "Authorization: Bearer $TOKEN"
Returns a new JWT. The SDKs handle this automatically.

Sandbox Lifecycle

POST /api/sandboxes — Create

Create a new sandbox. Request body:
FieldTypeRequiredDescription
templateIDstringNoTemplate name (default: "base")
timeoutintNoIdle timeout in seconds (default: 300)
cpuCountintNoCPU cores, 1–4 (default: 1)
memoryMBintNoMemory in MB, up to 2048 (default: 512)
envsobjectNoEnvironment variables
metadataobjectNoArbitrary key-value pairs
Response 201:
{
  "sandboxID": "sb-abc123",
  "templateID": "base",
  "status": "running",
  "startedAt": "2025-01-15T10:30:00Z",
  "endAt": "2025-01-15T10:35:00Z",
  "cpuCount": 1,
  "memoryMB": 512,
  "metadata": {},
  "connectURL": "https://worker-xyz.opencomputer.dev",
  "token": "eyJhbG..."
}
curl -X POST https://app.opencomputer.dev/api/sandboxes \
  -H "X-API-Key: $OPENCOMPUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateID": "base", "timeout": 600}'

GET /api/sandboxes — List

List all running sandboxes. Response 200: Array of sandbox objects.

GET /api/sandboxes/:id — Get

Get sandbox details by ID. Available on both control plane and worker. Response 200: Sandbox object (same shape as create response).

DELETE /api/sandboxes/:id — Kill

Terminate and remove a sandbox. Response 204: No content.

POST /api/sandboxes/:id/timeout — Set Timeout

Update the idle timeout. Worker-direct only — must be sent to the worker via connectURL with a Bearer token. The control plane will reject this request. Request body:
FieldTypeRequiredDescription
timeoutintYesNew timeout in seconds (must be > 0)
Response 204: No content.

POST /api/sandboxes/:id/hibernate — Hibernate

Snapshot VM state and stop the sandbox. Control plane only. Response 200:
{
  "sandboxID": "sb-abc123",
  "hibernationKey": "checkpoints/sb-abc123/1234567890.tar.zst",
  "sizeBytes": 134217728,
  "status": "hibernated"
}

POST /api/sandboxes/:id/wake — Wake

Resume a hibernated sandbox. Control plane only. Request body:
FieldTypeRequiredDescription
timeoutintNoIdle timeout after wake (default: 300)
Response 200: Sandbox object with updated status.

Commands (Exec)

POST /api/sandboxes/:id/exec/run — Run Command

Execute a command synchronously and return the result. Available on both surfaces. Request body:
FieldTypeRequiredDescription
cmdstringYesCommand to execute
argsstring[]NoCommand arguments
envsobjectNoEnvironment variables
cwdstringNoWorking directory
timeoutintNoTimeout in seconds (default: 60)
Response 200:
{
  "exitCode": 0,
  "stdout": "Hello, World!\n",
  "stderr": ""
}
curl -X POST {connectURL}/sandboxes/{id}/exec/run \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cmd": "echo", "args": ["Hello, World!"]}'

POST /api/sandboxes/:id/exec — Create Exec Session

Start a long-running command as a session. Attach via WebSocket to stream I/O. Request body:
FieldTypeRequiredDescription
cmdstringYesCommand to execute
argsstring[]NoCommand arguments
envsobjectNoEnvironment variables
cwdstringNoWorking directory
timeoutintNoTimeout in seconds
maxRunAfterDisconnectintNoSeconds to keep running after all clients disconnect
Response 201:
{
  "sessionID": "es-abc123",
  "sandboxID": "sb-abc123",
  "command": "node",
  "args": ["server.js"],
  "running": true,
  "exitCode": null,
  "startedAt": "2025-01-15T10:30:00Z",
  "attachedClients": 0
}

GET /api/sandboxes/:id/exec — List Sessions

Response 200: Array of ExecSessionInfo objects (same shape as create response).

GET /api/sandboxes/:id/exec/:sessionID — WebSocket Attach

Upgrade to WebSocket. Streams I/O using the binary protocol. Pass auth via query parameter: ?token=<jwt> On connect, the server replays the scrollback buffer, sends a 0x04 end marker, then streams live output.

POST /api/sandboxes/:id/exec/:sessionID/kill — Kill Session

Request body:
FieldTypeRequiredDescription
signalintNoSignal number (default: 9 / SIGKILL)
Response 204: No content.

Agent Sessions

Agent sessions run the Claude Agent SDK inside the sandbox. Events stream over the exec session WebSocket transport — there is no separate agent WebSocket endpoint.

POST /api/sandboxes/:id/agent — Create

Request body:
FieldTypeRequiredDescription
promptstringNoInitial prompt
modelstringNoClaude model name
systemPromptstringNoSystem prompt
allowedToolsstring[]NoRestrict available tools
permissionModestringNoPermission mode
maxTurnsintNoMaximum turns
cwdstringNoWorking directory
mcpServersobjectNoMCP server configuration
resumestringNoSession ID to resume
Response 201:
{
  "sessionID": "as-abc123",
  "sandboxID": "sb-abc123",
  "running": true,
  "startedAt": "2025-01-15T10:30:00Z",
  "claudeSessionID": ""
}
curl -X POST {connectURL}/sandboxes/{id}/agent \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Create a hello world app", "model": "claude-sonnet-4-20250514"}'

GET /api/sandboxes/:id/agent — List

Response 200: Array of AgentSessionInfo objects.

POST /api/sandboxes/:id/agent/:sid/prompt — Send Follow-up

Request body:
FieldTypeRequiredDescription
textstringYesFollow-up prompt text
Response 200: Empty object.

POST /api/sandboxes/:id/agent/:sid/interrupt — Interrupt

Stop the agent’s current turn. Response 200: Empty object.

POST /api/sandboxes/:id/agent/:sid/kill — Kill

Terminate the agent session. Response 200: Empty object.

Filesystem

GET /api/sandboxes/:id/files?path=... — Read File

Returns file content as plain text (not JSON). The path query parameter is required.
curl {connectURL}/sandboxes/{id}/files?path=/app/index.js \
  -H "Authorization: Bearer $TOKEN"
Response 200: Raw file content with Content-Type: text/plain.

PUT /api/sandboxes/:id/files?path=... — Write File

The request body is written as raw file content (not JSON). The path query parameter specifies the destination.
curl -X PUT {connectURL}/sandboxes/{id}/files?path=/app/hello.txt \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  -d "Hello, World!"
Response 204: No content.

GET /api/sandboxes/:id/files/list?path=... — List Directory

Query parameter: path (default: /) Response 200:
[
  { "name": "app", "isDir": true, "size": 4096, "path": "/app" },
  { "name": "README.md", "isDir": false, "size": 1234, "path": "/README.md" }
]
Each entry has: name (string), isDir (boolean), size (int64), path (string).

POST /api/sandboxes/:id/files/mkdir?path=... — Create Directory

The path query parameter specifies the directory to create. Response 204: No content.

DELETE /api/sandboxes/:id/files?path=... — Remove

Delete a file or directory. The path query parameter is required. Response 204: No content.

Checkpoints

Maximum 10 checkpoints per sandbox.

POST /api/sandboxes/:id/checkpoints — Create

Request body:
FieldTypeRequiredDescription
namestringYesCheckpoint name (unique per sandbox)
Response 201:
{
  "id": "cp-abc123",
  "sandboxID": "sb-abc123",
  "name": "before-migration",
  "status": "processing",
  "sizeBytes": 0,
  "createdAt": "2025-01-15T10:30:00Z"
}
Status transitions: processingready or failed.

GET /api/sandboxes/:id/checkpoints — List

Response 200: Array of CheckpointInfo objects.

POST /api/sandboxes/:id/checkpoints/:checkpointId/restore — Restore

Revert the sandbox in-place to a checkpoint. All changes since the checkpoint are lost. Response 200: Empty object.

POST /api/sandboxes/from-checkpoint/:checkpointId — Fork

Create a new sandbox from a checkpoint. Request body:
FieldTypeRequiredDescription
timeoutintNoIdle timeout for the new sandbox (default: 300)
Response 201: Sandbox object (same shape as create).
curl -X POST https://app.opencomputer.dev/api/sandboxes/from-checkpoint/cp-abc123 \
  -H "X-API-Key: $OPENCOMPUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"timeout": 600}'

DELETE /api/sandboxes/:id/checkpoints/:checkpointId — Delete

Response 204: No content.

Checkpoint Patches

Patches are scripts that run when a sandbox is spawned from a checkpoint. They apply in sequence order.

POST /api/sandboxes/checkpoints/:checkpointId/patches — Create

Request body:
FieldTypeRequiredDescription
scriptstringYesBash script to execute on spawn
descriptionstringNoHuman-readable description
Response 201:
{
  "patch": {
    "id": "pa-abc123",
    "checkpointId": "cp-abc123",
    "script": "apt install -y curl",
    "description": "Install curl",
    "strategy": "on_wake",
    "sequence": 1,
    "createdAt": "2025-01-15T10:30:00Z"
  }
}

GET /api/sandboxes/checkpoints/:checkpointId/patches — List

Response 200: Array of PatchInfo objects.

DELETE /api/sandboxes/checkpoints/:checkpointId/patches/:patchId — Delete

Response 204: No content.

Preview URLs

POST /api/sandboxes/:id/preview — Create

Request body:
FieldTypeRequiredDescription
portintYesContainer port (1–65535)
domainstringNoCustom domain
authConfigobjectNoAuthentication configuration
Response 201:
{
  "id": "pv-abc123",
  "sandboxId": "sb-abc123",
  "hostname": "sb-abc123-p3000.preview.opencomputer.dev",
  "port": 3000,
  "sslStatus": "active",
  "createdAt": "2025-01-15T10:30:00Z"
}

GET /api/sandboxes/:id/preview — List

Response 200: Array of preview URL objects.

DELETE /api/sandboxes/:id/preview/:port — Delete

Response 204: No content.

Templates

Templates are built from Dockerfiles. The Docker build produces a filesystem image that Firecracker boots from directly.

POST /api/templates — Build

Request body:
FieldTypeRequiredDescription
namestringYesTemplate name
dockerfilestringYesDockerfile content
tagstringNoImage tag (default: "latest")
Response 201:
{
  "id": "tpl-abc123",
  "name": "my-template",
  "tag": "latest",
  "status": "ready",
  "createdAt": "2025-01-15T10:30:00Z"
}
Dockerfile templates return status: "ready" immediately. Sandbox-snapshot templates transition processingready.

GET /api/templates — List

Response 200: Array of template objects.

GET /api/templates/:name — Get

Response 200: Template object.

DELETE /api/templates/:name — Delete

Response 204: No content.

PTY

Terminal sessions for interactive shell access.

POST /api/sandboxes/:id/pty — Create

Request body:
FieldTypeRequiredDescription
colsintNoTerminal columns (default: 80)
rowsintNoTerminal rows (default: 24)
shellstringNoShell path (default: /bin/bash)
Response 201:
{
  "sessionID": "ps-abc123",
  "sandboxID": "sb-abc123"
}

GET /api/sandboxes/:id/pty/:sessionID — WebSocket

Upgrade to WebSocket. Raw binary I/O — data from the PTY is sent as-is, data received is written to PTY stdin. Pass auth via query parameter: ?token=<jwt>

POST /api/sandboxes/:id/pty/:sessionID/resize — Resize

Request body:
FieldTypeRequiredDescription
colsintYesNew column count
rowsintYesNew row count
Response 200: Empty object.
PTY resize is HTTP-only — not exposed in the TypeScript or Python SDKs. The SDKs handle resize automatically for interactive sessions.

DELETE /api/sandboxes/:id/pty/:sessionID — Kill

Terminate a PTY session. Response 204: No content.

WebSocket Binary Protocol

Exec and PTY WebSocket sessions use binary frames with a 1-byte stream prefix:
ByteDirectionMeaning
0x00Client → Serverstdin data
0x01Server → Clientstdout data
0x02Server → Clientstderr data
0x03Server → ClientExit code (4-byte big-endian int32)
0x04Server → ClientScrollback end marker
Connection flow:
  1. Client opens WebSocket with ?token=<jwt>
  2. Server replays scrollback buffer (historical output)
  3. Server sends 0x04 to mark end of scrollback
  4. Live output streams as 0x01/0x02 frames
  5. When the process exits, server sends 0x03 with the exit code
  6. Server closes the connection
Sending input: Prefix your data with 0x00 and send as a binary frame. PTY sessions use the same binary framing but without stream prefixes — raw bidirectional terminal data.

Error Format

All errors use a consistent envelope:
{
  "error": "descriptive error message"
}
Status CodeMeaning
400Invalid request (missing fields, bad values)
401Missing or invalid authentication
403Insufficient permissions
404Resource not found
409Conflict (duplicate resource, e.g. checkpoint name)
429Quota exceeded
500Internal server error
503Feature unavailable in current deployment mode