> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackshift.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox REST API and SDKs

> Authenticate, create resources idempotently, wait for durable operations, stream events, and use TypeScript, Python, or Go clients.

The canonical Sandbox API is versioned `2026-08-01` and served below `https://api.stackshift.cloud/api/v1`. The dashboard, CLI, and SDKs consume this same contract.

## HTTP conventions

Send a bearer token over HTTPS:

```http theme={null}
Authorization: Bearer <token>
Accept: application/json
Content-Type: application/json
```

Successful JSON may be returned directly or in a `data` envelope; official SDKs unwrap it. Cursor collections contain `items`, `has_more`, and, when more data exists, `next_cursor`. The maximum page limit is 200.

Every mutating request that declares `Idempotency-Key` requires a stable non-empty value up to 200 characters. Generate one per logical action and reuse it only for an identical retry. Do not generate a new key after a timeout until you have checked the returned/stored operation.

Updates and sandbox destroy also require `If-Match` with the current quoted resource version. This prevents a stale client from overwriting or destroying a resource changed by another actor.

## Create a sandbox over REST

The REST request supplies the normalized resource, lifecycle, and network contract. Profiles provide policy defaults but do not remove server-side validation.

```bash theme={null}
export STACKSHIFT_API_URL='https://api.stackshift.cloud'
export IDEMPOTENCY_KEY='<stable-uuid>'

curl --fail-with-body --silent --show-error \
  --request POST "$STACKSHIFT_API_URL/api/v1/sandboxes" \
  --header "Authorization: Bearer $STACKSHIFT_TOKEN" \
  --header 'Content-Type: application/json' \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data @- <<'JSON'
{
  "name": "agent-session-42",
  "profile": "coding_agent",
  "template": "ghcr.io/acme/polyglot-agent@sha256:<64-hex-digest>",
  "source": {"kind": "blank"},
  "resources": {
    "cpu": {"reserved": 1, "limit": 2},
    "memory_mb": {"reserved": 2048, "limit": 4096},
    "disk_mb": {"root": 8192, "workspace": 20480, "ephemeral": 2048},
    "pids": 512,
    "swap_bytes": 0
  },
  "lifecycle": {
    "idle_timeout_seconds": 900,
    "idle_action": "sleep",
    "auto_resume": true,
    "max_runtime_seconds": 21600,
    "ttl_seconds": 86400,
    "on_exit": "sleep",
    "activity_mode": "both"
  },
  "network": {
    "egress": "allowlist",
    "allowed_hosts": ["api.github.com", "registry.npmjs.org"],
    "allowed_cidrs": [],
    "denied_hosts": [],
    "denied_cidrs": [],
    "inbound": "none"
  },
  "metadata": {"product": "coding-agent", "session_id": "42"},
  "tags": ["agent", "customer-acme"]
}
JSON
```

The response is an operation, not a completed sandbox. Poll `GET /api/v1/operations/{operationId}` until terminal, or subscribe to events. Operation terminal states are `succeeded`, `failed`, and `cancelled`; clients also defensively recognize `timed_out`.

## TypeScript SDK

```ts theme={null}
import {
  StackshiftOperationError,
  StackshiftSandboxClient,
} from '@stackshift-cloud/sandbox'

const client = new StackshiftSandboxClient({
  baseUrl: process.env.STACKSHIFT_API_URL ?? 'https://api.stackshift.cloud',
  token: process.env.STACKSHIFT_TOKEN,
})

const operation = await client.sandboxes.create({
  name: 'agent-session-42',
  profile: 'coding_agent',
  template: 'ghcr.io/acme/polyglot-agent@sha256:<64-hex-digest>',
  tags: ['agent'],
}, 'session-42-create')

try {
  await client.operations.wait(operation.id)
  const sandbox = client.sandbox(operation.sandbox_id)
  const execution = await sandbox.exec({
    argv: ['npm', 'test'],
    cwd: '/workspace',
    timeout_seconds: 1200,
    stdout_mode: 'capture',
    stderr_mode: 'capture',
  }, 'session-42-tests')
  console.log(execution.id)
} catch (error) {
  if (error instanceof StackshiftOperationError) {
    console.error(error.operation.failure_code, error.operation.id)
  }
  throw error
}
```

SDK mutation methods generate UUID idempotency keys when omitted. Supply your own when the logical action may be retried across processes or jobs.

## Python SDK

```python theme={null}
import os

from stackshift_sandbox import (
    StackshiftOperationError,
    StackshiftSandboxClient,
)

client = StackshiftSandboxClient(
    os.getenv("STACKSHIFT_API_URL", "https://api.stackshift.cloud"),
    token=os.environ["STACKSHIFT_TOKEN"],
    timeout=30,
)

operation = client.sandboxes.create(
    {
        "name": "interpreter-42",
        "profile": "interpreter",
        "template": "ghcr.io/acme/python@sha256:<64-hex-digest>",
        "tags": ["interpreter"],
    },
    idempotency_key="interpreter-42-create",
)

try:
    client.operations.wait(operation["id"], timeout=600)
    sandbox = client.sandbox(operation["sandbox_id"])
    execution = sandbox.exec(
        {"argv": ["python", "-V"], "cwd": "/workspace"},
        key="interpreter-42-version",
    )
    print(execution["id"])
except StackshiftOperationError as error:
    print(error.operation.get("failure_code"), error.operation["id"])
    raise
```

## Go SDK

```go theme={null}
client, err := sandbox.New(sandbox.Options{
    BaseURL: "https://api.stackshift.cloud",
    Token:   os.Getenv("STACKSHIFT_TOKEN"),
})
if err != nil {
    return err
}

operation, err := client.Sandboxes.Create(ctx, sandbox.CreateSandboxInput{
    Name:     "ci-42",
    Profile:  "ci",
    Template: "ghcr.io/acme/ci@sha256:<64-hex-digest>",
    Tags:     []string{"ci"},
}, "ci-42-create")
if err != nil {
    return err
}

operation, err = client.Operations.Wait(ctx, operation.ID, sandbox.WaitOptions{
    Timeout: 10 * time.Minute,
})
if err != nil {
    return err
}
```

The Go client requires an absolute HTTPS base URL, except HTTP localhost for tests, and defaults to a 30-second HTTP timeout.

## Endpoint families

| Family          | Paths and responsibility                                                           |
| --------------- | ---------------------------------------------------------------------------------- |
| Admission/fleet | `/sandboxes/catalog`, `/sandboxes/fleet-summary`, `/sandboxes/admission-preflight` |
| Lifecycle       | `/sandboxes`, `/sandboxes/{id}`, lifecycle actions, `/operations/{id}`             |
| Runtime         | executions, processes, terminals, interpreter contexts/runs                        |
| Workspace       | files, file actions/watch/transfers, writer lease                                  |
| Access/output   | ports, port tokens/access logs, artifacts                                          |
| Persistence     | sandbox/global snapshots and volumes, restore, fork, grow, attach                  |
| Dependencies    | per-sandbox services and lifecycle actions                                         |
| Credentials     | secrets, versions, audit, and per-sandbox bindings                                 |
| Observe/govern  | events/SSE, webhooks/deliveries/replay, usage/export, audit events                 |
| Supply chain    | template versions, SBOM, activation, and template builds                           |

Use resource IDs as opaque UUIDs. URL-encode every path segment and never construct object-storage or WebSocket credentials yourself; request the corresponding transfer/download/ticket resource.

## Events and webhooks

Sandbox SSE is `GET /api/v1/sandboxes/{id}/events/stream?after_sequence=N`. Persist the last fully processed sequence, reconnect after it, tolerate duplicates, and refetch current resources after a gap.

Webhook endpoint creation returns its signing secret only at creation or explicit rotation. Store it immediately in a secret manager. Verify signature, timestamp tolerance, endpoint ID, and delivery ID against the raw body before processing; deduplicate by event/delivery ID. Replay is a new delivery attempt, not a new lifecycle event.

## Error object

```json theme={null}
{
  "error": {
    "code": "capacity_unavailable",
    "message": "No qualified capacity is currently available",
    "retryable": true,
    "request_id": "req_...",
    "operation_id": "..."
  }
}
```

Always log `code`, `request_id`, and `operation_id` when present. Never log bearer tokens, secret request bodies, terminal tickets, port tokens, signed URLs, or volatile service credentials.

<CardGroup cols={2}>
  <Card title="CLI reference" href="/ai-sandboxes/cli-reference">Use the supported command surface.</Card>
  <Card title="Troubleshooting" href="/ai-sandboxes/troubleshooting">Handle each stable error class safely.</Card>
</CardGroup>
