API Reference (Node Controller)
Base URL:https://api.sandbox.stateset.app/api/v1
The Node controller is the default deployed by k8s/deployment.yaml.
This API is served from
api.sandbox.stateset.app, which is a different host
from the commerce and agent APIs on api.stateset.com, and it takes a
sandbox key rather than a platform key. A key that works against one will
return 401 against the other.Authentication
This API supports three auth modes:- API key (recommended for servers/CLI):
- Session JWT (recommended for same-origin apps):
- Browser session cookie (recommended for dashboards):
stateset_sessionis anhttpOnlycookie set by registration/login/invite accept/SSO callback.- For state-changing requests authenticated via the session cookie (POST/PUT/PATCH/DELETE), include
X-CSRF-Token: <token>. - After you have a session cookie, the controller returns an
x-csrf-tokenresponse header on safe requests (GET/HEAD/OPTIONS) and sets a__stateset_csrfcookie. Use that token value for subsequent writes. - Requests authenticated via an
Authorizationheader (API key or Bearer JWT) do not require CSRF.
Errors
Errors are returned as:Health & Monitoring
GET /health
No auth.GET /ready
No auth. Returns 503 if not ready.GET /health/detailed
No auth.GET /metrics
Internal auth in production (X-Internal-Token or Authorization: Bearer <internal-key>).
Authentication Endpoints
POST /register
Creates an organization + user + API key and sets a session cookie. Request:POST /auth/login
Authenticates and returns a session payload (also setsstateset_session cookie).
Request:
POST /auth/logout
Clears the session cookie.GET /auth/providers
Returns which optional auth providers are configured. Response shape:GET /auth/session
Returns the current session payload (same shape as/auth/login).
Auth: cookie session (stateset_session) or Authorization: Bearer <token>.
GET /auth/workos/authorize
Starts WorkOS AuthKit SSO. Redirects to WorkOS and sets short-lived PKCE cookies. RequiresWORKOS_API_KEY, WORKOS_CLIENT_ID, WORKOS_REDIRECT_URI.
Query params:
return_to: redirect destination after successful login (same-origin relative paths only; protocol-relative values like//example.comare rejected)organization_id,connection_id,domain_hint,login_hint,screen_hint: WorkOS hints
GET /auth/workos/callback
WorkOS callback. Exchangescode for user identity, then sets a stateset_session cookie and redirects back.
GET /auth/workos/link
Returns the current WorkOS linking status for the authenticated organization. Auth: cookie session (admin/owner).POST /auth/workos/link
Links a WorkOS Organization ID to the authenticated organization. Auth: cookie session (owner). Request:DELETE /auth/workos/link
Unlinks WorkOS from the authenticated organization. Auth: cookie session (owner). WhenAUDIT_LOGS_ENABLED=true, WorkOS link/unlink operations emit audit actions:
auth.sso.linkauth.sso.unlink
Sandboxes
POST /sandbox/create
Creates a new sandbox. Request:isolationsupportscontainer,gvisor,microvm, andwasm.wasm_networkis valid only whenisolationiswasm.
GET /sandboxes?limit=20&offset=0
Lists sandboxes for the authenticated organization.GET /sandbox/:id
Returns sandbox details.GET /sandbox/:id/status
Returns status + expiry (lightweight).POST /sandbox/:id/stop
Stops/deletes the sandbox. Alias:DELETE /sandbox/:id
Command Execution
POST /sandbox/:id/execute
Executes a command in the sandbox. Request:- Set
"stream": true - Response
Content-Type: text/event-stream - Events are emitted as
data: {...}\n\nwithtypevalues:stdout,stderr,exit,done,error
File Operations
File contents are base64 encoded.POST /sandbox/:id/files
Writes one or more files. Request:GET /sandbox/:id/files?path=/workspace/main.py
Reads a file (JSON response containing base64 content).GET /sandbox/:id/files/download?path=/workspace/main.py
Downloads a file asapplication/octet-stream.
REPL Sessions
Interactive Python REPL with persistent session state. Variables and imports survive across execute calls. RequiresREPL_ENABLED=true.
POST /sandbox/:id/repl/sessions
Creates a new REPL session. Request:language is optional and defaults to python.
Response:
GET /sandbox/:id/repl/sessions
Lists REPL sessions for a sandbox. Response:POST /sandbox/:id/repl/sessions/:sessionId/execute
Executes code in a REPL session. Variables and imports persist across calls. Request:POST /sandbox/:id/repl/sessions/:sessionId/stream
Executes code with streaming output via SSE. Request:Content-Type: text/event-stream. Events are emitted as output is produced:
POST /sandbox/:id/repl/sessions/:sessionId/interrupt
Interrupts a running execution in the session. Response:DELETE /sandbox/:id/repl/sessions/:sessionId
Destroys a REPL session and its kernel. Response:Session Status Values
Output Types
Templates
GET /templates
Lists templates (summaries).GET /templates/categories
Lists categories.GET /templates/:id
Returns a template including file contents.POST /sandbox/create-from-template
Creates a sandbox based on a template.Secrets (BYOK)
Secrets are organization-scoped and injected into sandbox pods as environment variables.POST /secrets
Creates a secret.GET /secrets
Lists secrets (values are never returned).PUT /secrets/:name
Updates a secret.DELETE /secrets/:name
Deletes a secret.API Keys
GET /api-keys
Lists API keys for the organization.POST /api-keys
Creates a new API key (the plaintext key is only returned once).DELETE /api-keys/:id
Revokes an API key.POST /api-keys/:id/regenerate
Regenerates a key (returns a new plaintext key).GitHub Integrations
These routes are available when GitHub integration routes are enabled.GET /github/installations
Lists linked/pending GitHub App installations for the authenticated organization.POST /github/installations/:id/connect
Links an installation to the authenticated organization.DELETE /github/installations/:id
Removes a linked installation from the authenticated organization.GET /github/settings
Returns GitHub automation settings for the organization. Includesauto_pr_comment_on_sandbox to control whether PR-triggered sandboxes post an automatic GitHub comment with dashboard/sandbox details.
Includes auto_stop_sandbox_on_pr_close to control whether PR-triggered sandboxes are automatically stopped when the pull request is closed.
PUT /github/settings
Updates GitHub automation settings.GET /github/events
Returns recent GitHub webhook processing events for the organization.GET /github/health?window_hours=24
Returns integration health summary (installations, webhook success/failure counts, status, latest event/failure).window_hoursis optional and must be an integer between1and168.
Internal Snapshot API (/internal)
These endpoints require internal auth (X-Internal-Token or Authorization: Bearer <internal-token>) in production.
GET /internal/snapshots
List all snapshot profiles. Response:GET /internal/snapshots/status
Snapshot service health and configuration.POST /internal/snapshots
Create a snapshot profile. Request example:GET /internal/snapshots/:profileKey
Get metadata for a single profile.DELETE /internal/snapshots/:profileKey
Delete a snapshot profile.POST /internal/snapshots/:profileKey/restore
Restore a snapshot profile into a mock Firecracker runtime.POST /internal/snapshots/warm
Warm cache, with optional body{ "count": 3 }.
POST /internal/snapshots/cleanup
Remove old snapshots by age and/or keep policy. Request example:Idempotency
POST endpoints support safe retries via theIdempotency-Key header. When enabled (IDEMPOTENCY_ENABLED=true, default), the controller caches the response for each unique (orgId, key, route) tuple for 24 hours.
Usage
Behavior
- First request: Executes normally. Response is stored and returned with
Idempotency-Status: stored. - Replay: If the same
(orgId, key, route)is seen again within 24h, the cached response is returned withIdempotency-Status: replayed. - Body mismatch: If a replay has a different request body hash, the server returns
409 Conflictwith error codeIDEMPOTENCY_KEY_REUSE_MISMATCH. - Scope: POST requests only. The header is ignored on GET/PUT/PATCH/DELETE.
- TTL: 24 hours. After expiry the key can be reused.
Response Headers
Real-time Events (SSE)
GET /events/stream provides a Server-Sent Events stream of sandbox lifecycle events for the authenticated organization.
Authentication
Because the browserEventSource API does not support custom headers, this endpoint accepts auth via multiple mechanisms:
Authorization: Bearer <jwt>headerAuthorization: ApiKey <key>header?token=<jwt>query parameter?apiKey=<key>query parameter (requires&orgId=<orgId>)- Session cookie (
stateset_session)
Event Format
Event Types
sandbox.created, sandbox.stopped, sandbox.deleted, artifact.uploaded, checkpoint.created, checkpoint.restored, mcp.tool_called, security.violation, resources.limit_warning
Resumable Streams
On reconnect, include the last received event ID to replay missed events:Last-Event-IDrequest header (standard SSE reconnect)?lastEventId=<id>query parameter
Heartbeat
A:heartbeat comment is sent every 20 seconds to keep the connection alive.
Concurrency Budgets & Queue Mode
Each organization has per-plan concurrency budgets for sandboxes and command executions.Per-Plan Budgets
Limits are configurable via
ORG_BUDGET_<PLAN>_MAX_CONCURRENT_SANDBOXES and ORG_BUDGET_<PLAN>_MAX_CONCURRENT_EXECUTES environment variables.
Exceeding Limits
When a request would exceed the budget:- Without queue mode:
429 Too Many Requestswith error codeCONCURRENT_LIMIT_EXCEEDED. - With queue mode (
QUEUE_MODE_ENABLED=true):202 Acceptedwith a queued request ID.
Queued Response Format
Queued Response Headers
Polling
GET /requests/:requestId returns the current status of a queued request, including position, queue_depth, and estimated_wait_seconds. Once completed, response_body and response_status are included.
Queue Health
GET /queue/health returns per-org queue telemetry: queue_depth, processing_count, oldest_queued_age_seconds, estimated_wait_seconds, and rolling-window statistics (completed_count, failed_count, expired_count, average_wait_ms, p95_wait_ms).
Error Codes
Egress Policies
Per-organization outbound network policies. RequiresEGRESS_POLICIES_ENABLED=true.
Policy Model
Domain Matching
- Exact match:
api.github.commatches onlyapi.github.com - Wildcard:
*.npmjs.orgmatchesregistry.npmjs.org,www.npmjs.org, etc.
Enforcement Modes (EGRESS_PROXY_MODE)
Error Code
Pagination
List endpoints support standard offset-based pagination.Query Parameters
Response Format
/sandboxes, /artifacts, /checkpoints, /webhooks, /audit, /egress/audit, /requests.
Optional Features
Some routes are only registered when corresponding services are enabled and the database is configured.- REPL sessions:
/sandbox/:id/repl/sessions, execute, stream, interrupt, destroy (REPL_ENABLED=true) - Checkpoints:
/sandbox/:id/checkpoints,/checkpoints, … - Artifacts:
/sandbox/:id/artifacts/upload,/artifacts, multipart uploads at/artifacts/uploads/initiate,/artifacts/uploads/:upload_id/parts/:part_number,/artifacts/uploads/:upload_id/complete,/artifacts/uploads/:upload_id/abort - Egress audit:
/egress/audit(org query),/internal/egress/audit(proxy/internal ingest) - Egress authz adapter:
/internal/egress/authorize(proxy authorization check) - Egress policy snapshot:
/internal/egress/policies(proxy polling/sync) - Webhooks:
/webhooks, … - Tunnels:
/sandbox/:id/tunnels, … - Audit:
/audit,/sandbox/:id/audit/summary
- When a create/execute request is queued (
202), responses include:X-Stateset-Queue-Status: queuedX-Stateset-Queue-PositionRetry-After
- Queue poll (
GET /requests/:requestId) includesposition,queue_depth, andestimated_wait_seconds. - Queue health (
GET /queue/health) returns per-org queue telemetry for dashboards:queue_depth,processing_count,oldest_queued_age_secondsestimated_wait_seconds,seconds_per_queue_position- rolling-window counts and latency stats (
completed_count,failed_count,expired_count,average_wait_ms,p95_wait_ms)
Next steps
Sandbox quickstart
Create and run your first sandbox against these endpoints.
Node SDK
The typed client over this API.
Python SDK
The same, for Python.
Operations
Health, metrics and runbooks for the controller serving this API.