API Reference

ConnectRPC over HTTP/1.1 with JSON. All endpoints require a Bearer token unless noted.

Base URL: https://api.upstream.build
Auth: Authorization: Bearer sk-...

Sandbox lifecycle

CreateSandbox

Provision a Firecracker microVM.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/CreateSandbox \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"template": "claude-code", "context_id": "my-project"}'
FieldTypeDescription
templatestringTemplate name: claude-code, python, nodejs
context_idstringCustomer context ID for persistence across sessions
envmap<string,string>Environment variables to inject
cwdstringWorking directory inside the sandbox
client_public_keystringX25519 public key for E2E encryption (base64)

Response:

{
  "sandbox_id": "sb-a1b2c3d4",
  "status": "creating",
  "server_public_key": "base64...",
  "auth_token": "hmac-token..."
}

GetSandbox

Check sandbox status.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/GetSandbox \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4"}'

Response:

{
  "sandbox_id": "sb-a1b2c3d4",
  "status": "ready",
  "template": "claude-code",
  "vcpu": 2,
  "memory_mib": 2048,
  "created_at": "2026-04-03T10:00:00Z"
}

Status values: creating, ready, running, stopped, failed.

ListSandboxes

List all active sandboxes for the current tenant.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/ListSandboxes \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{}'

DeleteSandbox

Destroy a sandbox and release resources.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/DeleteSandbox \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4"}'

Execution

Exec

Execute a command and wait for the result.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/Exec \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4", "command": ["python", "-c", "print(42)"]}'
FieldTypeDescription
sandbox_idstringTarget sandbox
commandstring[]Command and arguments
working_dirstringWorking directory (default: /workspace)
envmap<string,string>Additional environment variables

Response:

{
  "exit_code": 0,
  "stdout": "NDI=",
  "stderr": "",
  "duration_ms": 42
}
Note: stdout and stderr are base64-encoded bytes in the ConnectRPC response. The Python SDK decodes them automatically.

ExecStream

Execute a command with streaming output. Returns a server-sent event stream.

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/ExecStream \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4", "command": ["tail", "-f", "/var/log/syslog"]}'

Each event contains one of: stdout (bytes), stderr (bytes), or exit_code (int32).

Filesystem

ReadFile

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/ReadFile \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4", "path": "/workspace/main.py"}'

WriteFile

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/WriteFile \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4", "path": "/workspace/main.py", "content": "cHJpbnQoJ2hlbGxvJyk="}'

Content is base64-encoded bytes.

ListFiles

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/ListFiles \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"sandbox_id": "sb-a1b2c3d4", "path": "/workspace"}'

Response:

{
  "entries": [
    { "name": "main.py", "is_dir": false, "size": 1024 },
    { "name": "src", "is_dir": true, "size": 0 }
  ]
}

Usage & billing

GetUsage

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/GetUsage \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"since": "2026-04-01T00:00:00Z", "until": "2026-04-03T23:59:59Z"}'

Response:

{
  "total_sessions": 142,
  "total_vcpu_seconds": 86400.5,
  "total_gib_seconds": 172800.0,
  "total_duration_seconds": 43200.25
}

Templates

ListTemplates

$ curl -X POST https://api.upstream.build/upstream.platform.v1.Platform/ListTemplates \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{}'

Response:

{
  "templates": [
    { "name": "claude-code", "default_vcpu": 2, "default_memory_mib": 2048 },
    { "name": "python", "default_vcpu": 2, "default_memory_mib": 512 },
    { "name": "nodejs", "default_vcpu": 2, "default_memory_mib": 512 }
  ]
}

REST endpoints

The following REST endpoints are also available alongside ConnectRPC.

Health & readiness

$ curl https://api.upstream.build/healthz
{"status": "ok"}

$ curl https://api.upstream.build/readyz
{"status": "ready"}

No authentication required.

API keys

# List keys (metadata only)
$ curl https://api.upstream.build/v1/keys \
  -H "Authorization: Bearer sk-..."

# Create a new key
$ curl -X POST https://api.upstream.build/v1/keys \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-pipeline"}'

# Revoke a key
$ curl -X DELETE https://api.upstream.build/v1/keys/42 \
  -H "Authorization: Bearer sk-..."

Error codes

CodeHTTPDescription
AUTH_REQUIRED401No authorization header
AUTH_INVALID403Invalid or revoked API key
RATE_LIMITED429Too many requests. Check Retry-After header.
QUOTA_EXCEEDED429Monthly compute quota reached
SANDBOX_NOT_FOUND404Sandbox ID does not exist or was destroyed
INVALID_PIPELINE400Markdown pipeline parse error
NO_STEPS400Pipeline contains no executable code blocks
INTERNAL_ERROR500Server error. Retry with backoff.