Skip to main content

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:
  1. API key (recommended for servers/CLI):
  1. Session JWT (recommended for same-origin apps):
  1. Browser session cookie (recommended for dashboards):
  • stateset_session is an httpOnly cookie 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-token response header on safe requests (GET/HEAD/OPTIONS) and sets a __stateset_csrf cookie. Use that token value for subsequent writes.
  • Requests authenticated via an Authorization header (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:
Response:

POST /auth/login

Authenticates and returns a session payload (also sets stateset_session cookie). Request:
Response:

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. Requires WORKOS_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.com are rejected)
  • organization_id, connection_id, domain_hint, login_hint, screen_hint: WorkOS hints

GET /auth/workos/callback

WorkOS callback. Exchanges code for user identity, then sets a stateset_session cookie and redirects back. 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:
Unlinks WorkOS from the authenticated organization. Auth: cookie session (owner). When AUDIT_LOGS_ENABLED=true, WorkOS link/unlink operations emit audit actions:
  • auth.sso.link
  • auth.sso.unlink

Sandboxes

POST /sandbox/create

Creates a new sandbox. Request:
Notes:
  • isolation supports container, gvisor, microvm, and wasm.
  • wasm_network is valid only when isolation is wasm.
Response:

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:
Response (non-streaming):
Streaming (SSE):
  • Set "stream": true
  • Response Content-Type: text/event-stream
  • Events are emitted as data: {...}\n\n with type values: 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 as application/octet-stream.

REPL Sessions

Interactive Python REPL with persistent session state. Variables and imports survive across execute calls. Requires REPL_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:
Response:

POST /sandbox/:id/repl/sessions/:sessionId/stream

Executes code with streaming output via SSE. Request:
Response 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. Includes auto_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_hours is optional and must be an integer between 1 and 168.

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:
Response example:

Idempotency

POST endpoints support safe retries via the Idempotency-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 with Idempotency-Status: replayed.
  • Body mismatch: If a replay has a different request body hash, the server returns 409 Conflict with error code IDEMPOTENCY_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 browser EventSource API does not support custom headers, this endpoint accepts auth via multiple mechanisms:
  1. Authorization: Bearer <jwt> header
  2. Authorization: ApiKey <key> header
  3. ?token=<jwt> query parameter
  4. ?apiKey=<key> query parameter (requires &orgId=<orgId>)
  5. 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-ID request header (standard SSE reconnect)
  • ?lastEventId=<id> query parameter
The server buffers recent events in memory and replays any events published after the given ID.

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 Requests with error code CONCURRENT_LIMIT_EXCEEDED.
  • With queue mode (QUEUE_MODE_ENABLED=true): 202 Accepted with 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. Requires EGRESS_POLICIES_ENABLED=true.

Policy Model

Domain Matching

  • Exact match: api.github.com matches only api.github.com
  • Wildcard: *.npmjs.org matches registry.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

Endpoints that support pagination: /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
Queue/backpressure notes:
  • When a create/execute request is queued (202), responses include:
    • X-Stateset-Queue-Status: queued
    • X-Stateset-Queue-Position
    • Retry-After
  • Queue poll (GET /requests/:requestId) includes position, queue_depth, and estimated_wait_seconds.
  • Queue health (GET /queue/health) returns per-org queue telemetry for dashboards:
    • queue_depth, processing_count, oldest_queued_age_seconds
    • estimated_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.
Last modified on August 31, 2026