# Contexts (Process Runtime)

Contexts are execution units within a sandbox. Each context runs a process and can be either a **REPL** (stateful) or **Cmd** (stateless) type.

<Callout variant="info">
Use [Supervised Sessions](/docs/sandbox/session-supervisor) when a process needs a stable identity, replayable output, explicit EOF, reconnect-safe attachments, or recovery as a new attempt after sandbox runtime replacement.
</Callout>

## Context Types

| Type | Description | Use Case |
|------|-------------|----------|
| **REPL** | Stateful, interactive process | Code evaluation, interactive shells, persistent sessions |
| **Cmd** | Stateless, one-shot command | Single commands, scripts, non-interactive execution |

### REPL vs Cmd

<Callout variant="info">
**REPL contexts preserve process state** between calls while the same REPL remains alive. Each high-level **Cmd call starts a fresh process**; variables and working-directory changes in that process do not carry into the next call. Both types share the sandbox filesystem, so file changes can persist. Restarting a context or using filesystem-only pause/resume discards its process state.
</Callout>

```text
# REPL: State preserved between calls
_S0_> x = 2
_S0_> print(x)
2

# Cmd: Each command is independent
$ x=2
$ echo $x    # Empty - variable not preserved
```

---

## High-Level: Run and Cmd

For common use cases, use the convenience methods `Run` (REPL) and `Cmd` (command). The snippets assume an existing sandbox handle. Later options and control snippets reuse the results and context IDs created earlier; run each operation while that context still exists.

The SDKs split Cmd strings into an executable and arguments; shell expansion, pipes, and redirects require an explicit shell such as `sh -c`. Use the `command` option for an exact argument array. Contexts remain subject to their own cleanup policy and the sandbox lifecycle.

If a process cannot start because the filesystem returns an I/O error, context creation returns HTTP `503` with code `storage_unavailable`. A missing executable can mask this failure: the gateway makes one read-only directory check against the same runtime, bounded to one second, and reports a storage failure only when that check also observes an I/O error. A successful or inconclusive check preserves the original command error. The diagnostic never replays the command or restarts the sandbox. Restoring object-store access does not by itself recover a filesystem that has already shut down; preserve the current runtime and committed filesystem evidence for recovery.

### Run (REPL)

Execute code in a REPL context. State is preserved between calls.

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// First call - set variable
result, err := sandbox.Run(ctx, "python", "x = 2")
if err != nil {
    log.Fatal(err)
}
fmt.Print(result.OutputRaw)

// Second call - variable preserved
result, err = sandbox.Run(ctx, "python", "print(x)")
if err != nil {
    log.Fatal(err)
}
fmt.Print(result.OutputRaw)`
    },
    {
      label: "Python",
      language: "python",
      code: `# First call - set variable
result = sandbox.run("python", "x = 2")
print(result.output_raw, end="")

# Second call - variable preserved
result = sandbox.run("python", "print(x)")
print(result.output_raw, end="")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// First call - set variable
let runResult = await sandbox.run('python', 'x = 2');
process.stdout.write(runResult.outputRaw);

// Second call - variable preserved
runResult = await sandbox.run('python', 'print(x)');
process.stdout.write(runResult.outputRaw);`
    }
  ]}
/>

### Run with Options

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Create a custom context with specific cwd, env vars, and TTL
customCtx, err := sandbox.CreateContext(ctx, apispec.CreateContextRequest{
    Type: apispec.NewOptProcessType(apispec.ProcessTypeRepl),
    Repl: apispec.NewOptCreateREPLContextRequest(apispec.CreateREPLContextRequest{
        Alias: apispec.NewOptString("python"),
    }),
    Cwd:            apispec.NewOptString("/workspace"),
    EnvVars:        apispec.NewOptCreateContextRequestEnvVars(map[string]string{"GREETING": "hello from workspace"}),
    TTLSec:         apispec.NewOptInt32(120),
    IdleTimeoutSec: apispec.NewOptInt32(60),
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Custom context ID: %s\\n", customCtx.ID)

// Run using the custom context
result, err = sandbox.Run(
    ctx,
    "python",
    \`import os, pathlib;
print(pathlib.Path.cwd());
print(os.getenv("GREETING"))\`,
    sandbox0.WithContextID(customCtx.ID),
)
if err != nil {
    log.Fatal(err)
}
fmt.Print(result.OutputRaw)`
    },
    {
      label: "Python",
      language: "python",
      code: `# Create a custom context with specific cwd, env vars, and TTL
from sandbox0 import RunOptions
from sandbox0.apispec.models.create_context_request import CreateContextRequest
from sandbox0.apispec.models.create_context_request_env_vars import CreateContextRequestEnvVars
from sandbox0.apispec.models.create_repl_context_request import CreateREPLContextRequest
from sandbox0.apispec.models.process_type import ProcessType

custom_ctx = sandbox.create_context(
    request=CreateContextRequest(
        type_=ProcessType.REPL,
        repl=CreateREPLContextRequest(alias="python"),
        cwd="/workspace",
        env_vars=CreateContextRequestEnvVars.from_dict({"GREETING": "hello from workspace"}),
        ttl_sec=120,
        idle_timeout_sec=60,
    )
)
print(f"Custom context ID: {custom_ctx.id}")

# Run using the custom context
result = sandbox.run(
    "python",
    """import os, pathlib;
print(pathlib.Path.cwd());
print(os.getenv("GREETING"))""",
    options=RunOptions(context_id=custom_ctx.id),
)
print(result.output_raw, end="")
print()`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Create a custom context with specific cwd, env vars, and TTL
const customCtx = await sandbox.createContext({
    type: 'repl',
    repl: { alias: 'python' },
    cwd: '/workspace',
    envVars: { GREETING: 'hello from workspace' },
    ttlSec: 120,
    idleTimeoutSec: 60,
});
console.log('Custom context ID:', customCtx.id);

// Run using the custom context
runResult = await sandbox.run('python', \`import os, pathlib;
print(pathlib.Path.cwd());
print(os.getenv("GREETING"))\`, {
    contextId: customCtx.id,
});
process.stdout.write(runResult.outputRaw);`
    }
  ]}
/>

### Cmd (One-shot Command)

Execute a stateless command.

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// One-shot command - stateless
result2, err := sandbox.Cmd(ctx, \`/bin/sh -c "echo hi"\`)
if err != nil {
    log.Fatal(err)
}
fmt.Print(result2.Stdout)
fmt.Fprint(os.Stderr, result2.Stderr)

// Variables NOT preserved between Cmd calls
sandbox.Cmd(ctx, \`/bin/sh -c "x=3"\`)
result2, err = sandbox.Cmd(ctx, \`/bin/sh -c "echo $x"\`)
if err != nil {
    log.Fatal(err)
}
fmt.Print(result2.Stdout)
fmt.Fprint(os.Stderr, result2.Stderr)`
    },
    {
      label: "Python",
      language: "python",
      code: `import sys

# One-shot command - stateless
result = sandbox.cmd('echo "hello from cmd"')
print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)

# Variables NOT preserved between cmd calls
sandbox.cmd('/bin/sh -c "x=3"')
result = sandbox.cmd('/bin/sh -c "echo $x"')
print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// One-shot command - stateless
let cmdResult = await sandbox.cmd('echo "hello from cmd"');
process.stdout.write(cmdResult.stdout);
process.stderr.write(cmdResult.stderr);

// Variables NOT preserved between cmd calls
await sandbox.cmd('/bin/sh -c "x=3"');
cmdResult = await sandbox.cmd('/bin/sh -c "echo $x"');
process.stdout.write(cmdResult.stdout);
process.stderr.write(cmdResult.stderr);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox exec sb_abc123 -- /bin/sh -c "echo hello"
# Output: hello`
    }
  ]}
/>

When a non-streaming Cmd waits for completion, the response includes `output_raw`,
`stdout`, `stderr`, `exit_code`, and `state`. SDK `Cmd` helpers expose the split
streams directly. `output_raw` remains the merged raw output for compatibility
and as a fallback for PTY-style output. `s0 sandbox exec` writes command stdout
to local stdout and command stderr to local stderr when split streams are
available, falls back to `output_raw` otherwise, and exits locally with the
remote command exit code when it is non-zero.

When supporting older Sandbox0 servers, fall back to `output_raw` / `outputRaw`
if both split stream fields are empty.

### Cmd with Options

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `result2, err = sandbox.Cmd(
    ctx,
    "bash -c 'echo $GREETING && pwd'",
    sandbox0.WithCmdCWD("/tmp"),
    sandbox0.WithCmdEnvVars(map[string]string{"GREETING": "hello"}),
    sandbox0.WithCmdTTL(120),
    sandbox0.WithCmdIdleTimeout(60),
)
if err != nil {
    log.Fatal(err)
}
fmt.Print(result2.Stdout)
fmt.Fprint(os.Stderr, result2.Stderr)`
    },
    {
      label: "Python",
      language: "python",
      code: `import sys

from sandbox0 import CmdOptions

result = sandbox.cmd(
    "bash -c 'echo $GREETING && pwd'",
    options=CmdOptions(
        cwd="/tmp",
        env_vars={"GREETING": "hello from tmp"},
        ttl_sec=120,
        idle_timeout_sec=60,
    ),
)
print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `cmdResult = await sandbox.cmd("bash -c 'echo $GREETING && pwd'", {
    cwd: '/tmp',
    envVars: { GREETING: 'hello from tmp' },
    ttlSec: 120,
    idleTimeoutSec: 60,
});
process.stdout.write(cmdResult.stdout);
process.stderr.write(cmdResult.stderr);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox exec sb_abc123 \\
  --cwd /tmp \\
  --env GREETING=hello \\
  -- bash -c 'echo $GREETING && pwd'`
    }
  ]}
/>

### Streaming Cmd Output

Use the streaming helpers when you want command output as it is produced instead of waiting for a final `output_raw` response. The output stream ends automatically when the command emits its terminal process-level `done` event.

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `stream, err := sandbox.CmdStream(
    ctx,
    "sh -c 'echo hello && echo warn >&2'",
    sandbox0.WithCommand([]string{"sh", "-c", "echo hello && echo warn >&2"}),
)
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for {
    output, err := stream.Recv()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    fmt.Print(output.Data)
}

done, err := stream.Wait()
if err != nil {
    log.Fatal(err)
}
if done.ExitCode == nil {
    log.Fatal("stream closed without terminal exit metadata")
}
fmt.Printf("exit=%d state=%s\\n", *done.ExitCode, done.State)`
    },
    {
      label: "Python",
      language: "python",
      code: `from sandbox0 import CmdOptions

stream = sandbox.cmd_stream(
    "sh -c 'echo hello && echo warn >&2'",
    CmdOptions(command=["sh", "-c", "echo hello && echo warn >&2"]),
)

for output in stream.iter_outputs():
    print(output.data, end="")

done = stream.wait()
print(f"exit={done.exit_code} state={done.state}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `const stream = await sandbox.cmdStream("sh -c 'echo hello && echo warn >&2'", {
    command: ['sh', '-c', 'echo hello && echo warn >&2'],
});

for await (const output of stream.outputs()) {
    process.stdout.write(output.data);
}

const done = await stream.wait();
console.log("exit=" + done.exitCode + " state=" + done.state);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox exec sb_abc123 --stream -- sh -c 'echo hello && echo warn >&2; exit 7'
# exits locally with status 7`
    }
  ]}
/>

---

## Create Context

Create a new context in a sandbox.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts
</Endpoint>

### Request Body

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | Context type: `repl` or `cmd` |
| `repl` | object | REPL configuration (optional for `type=repl`; defaults to Python when omitted) |
| `cmd` | object | Command configuration (`cmd.command` required if `type=cmd`) |
| `cwd` | string | Working directory |
| `env_vars` | object | Context environment variables; override sandbox-level `env_vars` for this context |
| `pty_size` | object | Terminal size `{rows, cols}` |
| `ttl_sec` | integer | Context time-to-live in seconds |
| `idle_timeout_sec` | integer | Idle timeout in seconds |
| `wait_until_done` | boolean | Wait for completion on the create request (default: false) |

For Cmd contexts created with `wait_until_done: true`, the response includes
`output_raw`, `stdout`, `stderr`, `exit_code`, and `state` after the process
exits. `stdout` and `stderr` are populated for non-PTY Cmd contexts when
available; PTY and REPL contexts may only expose `output_raw`.

### Long-running commands

Do not keep the create request open for a command that may run for a long time.
Create the Cmd context with `wait_until_done: false` and a bounded `ttl_sec`,
attach to its [WebSocket stream](#websocket-streaming), and then fetch the
context after the terminal `done` event to read the final `stdout`, `stderr`,
`exit_code`, and `state`.

```bash
# 1. Start the command and save data.id from the response.
curl -X POST "$SANDBOX0_BASE_URL/api/v1/sandboxes/$SANDBOX_ID/contexts" \
    -H "Authorization: Bearer $SANDBOX0_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
        "type": "cmd",
        "cmd": {"command": ["sh", "-lc", "run-my-long-job"]},
        "wait_until_done": false,
        "ttl_sec": 3600
    }'

# 2. Follow /contexts/{context_id}/ws until its terminal done event.
# 3. Read the final result.
curl "$SANDBOX0_BASE_URL/api/v1/sandboxes/$SANDBOX_ID/contexts/$CONTEXT_ID" \
    -H "Authorization: Bearer $SANDBOX0_TOKEN"
```

The Context WebSocket is live rather than replayable. Output produced before attachment may be missed, including by Cmd streaming helpers; use the final context response for retained output or a supervised session when complete replay is required. If the caller must
disconnect and later resume output from a cursor, retain process identity across
retries, or recover execution after sandbox runtime replacement, use a
[Supervised Session](/docs/sandbox/session-supervisor) instead.

### REPL Configuration

| Field | Type | Description |
|-------|------|-------------|
| `alias` | string | REPL alias/language (python, bash, node, etc.) |
| `input` | string | REPL input executed when `wait_until_done` is true. Otherwise use the input or exec endpoint after creation. |
| `repl_config` | object | Custom REPL config (optional; advanced use) |


### Cmd Configuration

| Field | Type | Description |
|-------|------|-------------|
| `command` | array | Command and arguments |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Create REPL context
ctxResp, err := sandbox.CreateContext(ctx, apispec.CreateContextRequest{
    Type: apispec.NewOptProcessType(apispec.ProcessTypeRepl),
    Repl: apispec.NewOptCreateREPLContextRequest(apispec.CreateREPLContextRequest{
        Alias: apispec.NewOptString("python"),
    }),
    Cwd:     apispec.NewOptString("/workspace"),
    EnvVars: apispec.NewOptCreateContextRequestEnvVars(map[string]string{"DEBUG": "true"}),
    TTLSec:  apispec.NewOptInt32(120),
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Context ID: %s\\n", ctxResp.ID)`
    },
    {
      label: "Python",
      language: "python",
      code: `# Create REPL context
context = sandbox.create_context(
    request=CreateContextRequest(
        type_=ProcessType.REPL,
        repl=CreateREPLContextRequest(alias="python"),
        cwd="/workspace",
        env_vars=CreateContextRequestEnvVars.from_dict({"DEBUG": "true"}),
        ttl_sec=120,
    )
)
print(f"Context ID: {context.id}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Create REPL context
const context = await sandbox.createContext({
    type: 'repl',
    repl: { alias: 'python' },
    cwd: '/workspace',
    envVars: { DEBUG: 'true' },
    ttlSec: 120,
});
console.log('Context ID:', context.id);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context create --type repl --alias python -s sb_abc123`
    }
  ]}
/>

### Custom REPL (`repl_config`)

Use `repl_config` when you need to run a non-built-in REPL, customize startup arguments, or tune readiness detection.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Cond. | REPL identifier; may be inferred from `alias`. If both are set, they must match. |
| `display_name` | string | No | Human-readable name. |
| `description` | string | No | Description for this REPL config. |
| `candidates` | array | Yes | Ordered executable candidates. Each item: `{ name, args }`. |
| `env` | array | No | Environment variables. Each item: `{ name, value }` or `{ name, value_from }`. |
| `default_term` | string | No | Default `TERM` value when no terminal value is provided. |
| `prompt.custom_prompt` | string | No | Custom prompt string used by REPLs that support prompt env injection. |
| `ready.mode` | string | No | Readiness strategy: `prompt_token` or `startup_delay`. |
| `ready.token` | string | Cond. | Required when `ready.mode=prompt_token`. |
| `ready.startup_delay_ms` | integer | Cond. | Used when `ready.mode=startup_delay` (must be `>= 0`). |

**Notes:**

- `candidates` must contain at least one executable.
- If `ready.mode` is omitted, runtime picks a default strategy based on whether `ready.token` is provided. A startup delay of `0` selects the runtime default of 200 ms.
- Common `env.value_from` values are `term` and `prompt`.

Example request:

```json
{
    "type": "repl",
    "repl": {
        "alias": "myrepl",
        "repl_config": {
            "name": "myrepl",
            "display_name": "My REPL",
            "candidates": [
                {
                    "name": "myrepl",
                    "args": ["--interactive"]
                },
                {
                    "name": "python3",
                    "args": ["-q", "-i"]
                }
            ],
            "env": [
                { "name": "TERM", "value_from": "term" }
            ],
            "ready": {
                "mode": "startup_delay",
                "startup_delay_ms": 300
            }
        }
    }
}
```


---

## List Contexts

List all contexts in a sandbox.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/contexts
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `contexts, err := sandbox.ListContext(ctx)
if err != nil {
    log.Fatal(err)
}
for _, ctx := range contexts {
    fmt.Printf("- %s (type: %s, running: %v)\\n", ctx.ID, ctx.Type, ctx.Running)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `contexts = sandbox.list_contexts()
for ctx in contexts:
    print(f"- {ctx.id} (type: {ctx.type_}, running: {ctx.running})")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `const contexts = await sandbox.listContexts();
for (const ctx of contexts) {
    console.log(\`- \${ctx.id} (type: \${ctx.type}, running: \${ctx.running})\`);
}`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context list -s sb_abc123`
    }
  ]}
/>

---

## Get Context

Get details of a specific context.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `ctxResp, err = sandbox.GetContext(ctx, ctxResp.ID)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Type: %s\\n", ctxResp.Type)
fmt.Printf("Running: %v\\n", ctxResp.Running)`
    },
    {
      label: "Python",
      language: "python",
      code: `ctx = sandbox.get_context(context.id)
print(f"type: {ctx.type_}")
print(f"running: {ctx.running}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `let ctx = await sandbox.getContext(context.id);
console.log('Type:', ctx.type);
console.log('Running:', ctx.running);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context get ctx-xyz789 -s sb_abc123`
    }
  ]}
/>

---

## Context Control Operations

### Restart Context

Restart a context process.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/restart
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `_, err = sandbox.RestartContext(ctx, ctxResp.ID)
if err != nil {
    log.Fatal(err)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.restart_context(context.id)
print(f"Context restarted: {result.running}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `await sandbox.restartContext(context.id);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context restart ctx-xyz789 -s sb_abc123`
    }
  ]}
/>

### Send Input

Send input to a context's stdin. The data is written exactly as provided. For REPL contexts, include a trailing newline when you want the REPL to execute the line.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/input
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `_, err = sandbox.ContextInput(ctx, ctxResp.ID, "print('hello')\\n")
if err != nil {
    log.Fatal(err)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.context_input(context.id, "print('hello')\\n")
print(f"Context input: {result.success}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `await sandbox.contextInput(context.id, "print('hello')\\n");`
    }
  ]}
/>

### Resize PTY

Resize the terminal dimensions.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/resize
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `_, err = sandbox.ContextResize(ctx, ctxResp.ID, 24, 80)
if err != nil {
    log.Fatal(err)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.context_resize(context.id, 24, 80)
print(f"Context resized: {result.success}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `await sandbox.contextResize(context.id, 24, 80);`
    }
  ]}
/>

### Send Signal

Send a signal to the context process.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/signal
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Send SIGTERM
_, err = sandbox.ContextSignal(ctx, ctxResp.ID, "TERM")
if err != nil {
    log.Fatal(err)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.context_signal(context.id, "TERM")
print(f"Context signaled: {result.success}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Send SIGTERM
await sandbox.contextSignal(context.id, 'TERM');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context signal ctx-xyz789 INT -s sb_abc123`
    }
  ]}
/>

## Delete Context

Delete a context.

<Endpoint method="DELETE">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}
</Endpoint>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `_, err = sandbox.DeleteContext(ctx, ctxResp.ID)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Context deleted")`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.delete_context(context.id)
print(f"Context deleted: {result.success}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `await sandbox.deleteContext(context.id);
console.log('Context deleted');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context delete ctx-xyz789 -s sb_abc123`
    }
  ]}
/>


---

## WebSocket Streaming

For real-time I/O, connect to a context via WebSocket. WebSocket input is a byte stream: `data` is written exactly as provided, so include `\n` when submitting a REPL line.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/ws
</Endpoint>

### WebSocket Protocol

**Client Messages:**

| Type | Description | Example |
|------|-------------|---------|
| `input` | Send input to stdin | `{"type": "input", "data": "ls\n", "request_id": "req-1"}` |
| `resize` | Resize terminal | `{"type": "resize", "rows": 24, "cols": 80}` |
| `signal` | Send signal | `{"type": "signal", "signal": "INT"}` |

**Server Messages:**

| Type | Description | Example |
|------|-------------|---------|
| `output` | Process output (`stdout` / `stderr` / `pty`) | `{"type": "output", "source": "stdout", "data": "hello\n"}` |
| `done` | Completion marker for a specific request identified by `request_id` | `{"type": "done", "request_id": "req-1"}` |
| `done` | Terminal process completion event with exit metadata | `{"type": "done", "exit_code": 0, "state": "stopped"}` |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Create context first
ctxResp, err = sandbox.CreateContext(ctx, apispec.CreateContextRequest{
    Type: apispec.NewOptProcessType(apispec.ProcessTypeRepl),
    Repl: apispec.NewOptCreateREPLContextRequest(apispec.CreateREPLContextRequest{
        Alias: apispec.NewOptString("python"),
    }),
})
if err != nil {
    log.Fatal(err)
}

// Connect WebSocket
conn, _, err := sandbox.ConnectWSContext(ctx, ctxResp.ID)
if err != nil {
    log.Fatal(err)
}
defer conn.Close()

// Send input
msg := map[string]any{
    "type":       "input",
    "data":       "print('hello')\\n",
    "request_id": "req-1",
}
if err := conn.WriteJSON(msg); err != nil {
    log.Fatal(err)
}
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
    log.Fatal(err)
}

// Read output
for {
    _, message, err := conn.ReadMessage()
    if err != nil {
        break
    }
    var msg struct {
        Type   string \`json:"type"\`
        Source string \`json:"source"\`
        Data   string \`json:"data"\`
    }
    if err := json.Unmarshal(message, &msg); err != nil {
        log.Fatal(err)
    }
    if msg.Type != "output" {
        continue
    }
    fmt.Print(msg.Data)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `# Create context first
import threading
from sandbox0.apispec.models.create_context_request import CreateContextRequest
from sandbox0.apispec.models.create_repl_context_request import CreateREPLContextRequest
from sandbox0.apispec.models.process_type import ProcessType

ctx = sandbox.create_context(
    request=CreateContextRequest(
        type_=ProcessType.REPL,
        repl=CreateREPLContextRequest(alias="python"),
    )
)
# Connect WebSocket
stream = sandbox.connect_ws_context(ctx.id)
outputs = []

def reader():
    for output in stream.iter_outputs():
        outputs.append(output.data)

thread = threading.Thread(target=reader, daemon=True)
thread.start()
# Send input
stream.send_input("print('hello from ws')\\n")
# Wait for outputs with timeout
thread.join(timeout=3)
stream.close()
thread.join(timeout=1)

for chunk in outputs:
    print(chunk, end="")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Create context first
ctx = await sandbox.createContext({
    type: 'repl',
    repl: { alias: 'python' },
});
// Connect WebSocket
const stream = await sandbox.connectWsContext(ctx.id);
// Consume outputs in background
const outputPromise = (async () => {
    for await (const output of stream.outputs()) {
        process.stdout.write(output.data);
    }
})();
// Send input
stream.sendInput("print('hello from ws')\\n");
// Wait briefly then close
await new Promise((r) => setTimeout(r, 1000));
stream.close();
await outputPromise;`
    }
  ]}
/>

---

## Exec (Synchronous Execution)

Execute input and wait for completion. For REPL contexts, the server appends a trailing newline when the input does not already end in `\n` or `\r`.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/contexts/{'{ctx_id}'}/exec
</Endpoint>

The response always includes `output_raw`. For non-PTY Cmd contexts it also
includes `stdout` and `stderr` when available. It includes `exit_code` and
`state` when the underlying process has exited.

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `execResult, err := sandbox.ContextExec(ctx, ctxResp.ID, "print(1 + 2)")
if err != nil {
    log.Fatal(err)
}
fmt.Print(execResult.OutputRaw)`
    },
    {
      label: "Python",
      language: "python",
      code: `result = sandbox.context_exec(ctx.id, "print(1 + 2)")
print(result.output_raw, end="")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `const execResult = await sandbox.contextExec(ctx.id, "print(1 + 2)");
process.stdout.write(execResult.outputRaw);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox context exec ctx-xyz789 "print(1+2)" -s sb_abc123`
    }
  ]}
/>

---

## Next Steps

<CardGroup>
  <Card title="Files" href="/docs/sandbox/files" cta="Continue">
    Read, write, watch, and manage files inside a sandbox workspace.
  </Card>

  <Card title="SSH" href="/docs/sandbox/ssh" cta="Continue">
    Connect to sandboxes with standard SSH clients for shell and file-copy workflows.
  </Card>
</CardGroup>
