---
title: Sandboxes
description: "Create, manage, and destroy isolated sandbox environments."
---

# Sandboxes

## Create Sandbox

<ParamField body="template" type="string" required>
  Base image template. See [Available Templates](#available-templates) below.
</ParamField>
<ParamField body="cpus" type="integer">
  Number of vCPUs (1-8, default: 2).
</ParamField>
<ParamField body="memory_mb" type="integer">
  Memory in MB (128-8192, default: 512).
</ParamField>
<ParamField body="disk_gb" type="integer">
  Disk in GB (1-50, default: 5).
</ParamField>
<ParamField body="network_mode" type="string">
  Network access: `full`, `none`, or `allowlist`.
</ParamField>
<ParamField body="allowlist" type="string[]">
  Allowed hosts when network_mode is `allowlist`.
</ParamField>
<ParamField body="env" type="object">
  Environment variables as key-value pairs.
</ParamField>
<ParamField body="repo" type="string">
  Git repository URL to clone (HTTPS).
</ParamField>
<ParamField body="repo_token" type="string">
  PAT or OAuth token for private repositories. GitHub: `ghp_...`, GitLab: personal access token.
</ParamField>
<ParamField body="repo_branch" type="string">
  Branch to checkout (default: main/master).
</ParamField>
<ParamField body="repo_commit" type="string">
  Specific commit SHA to checkout (overrides repo_branch).
</ParamField>
<ParamField body="repo_subdir" type="string">
  Monorepo subdirectory to extract into /workspace.
</ParamField>
<ParamField body="budget" type="number">
  Maximum spend in USD.
</ParamField>
<ParamField body="init_script" type="string">
  Shell command to run after creation.
</ParamField>
<ParamField body="timeout" type="integer">
  Idle timeout in seconds before auto-sleep.
</ParamField>
<ParamField body="packages" type="string[]">
  Packages to pre-install (npm/pip packages).
</ParamField>
<ParamField body="agents" type="string[]">
  AI agents to install: `claude`, `aider`, `codex`.
</ParamField>

```bash
curl -X POST https://api.caged.dev/v1/sandboxes \
  -H "Authorization: Bearer caged_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "template": "node-20",
    "cpus": 2,
    "memory_mb": 1024,
    "repo": "https://github.com/user/project",
    "budget": 5.00,
    "init_script": "npm install"
  }'
```

**Private Repository Example**

```bash
curl -X POST https://api.caged.dev/v1/sandboxes \
  -H "Authorization: Bearer caged_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "template": "node-20",
    "repo": "https://github.com/your-org/private-repo",
    "repo_token": "ghp_xxxxxxxxxxxx",
    "repo_branch": "develop",
    "agents": ["claude"],
    "budget": 10.00
  }'
```

**Response** `201 Created`

```json
{
  "id": "cage-a1b2c3d4",
  "status": "running",
  "template": "node-20",
  "ip": "10.0.1.42",
  "cpus": 2,
  "memory_mb": 1024,
  "disk_gb": 5,
  "network_mode": "full",
  "created_at": "2026-06-08T10:00:00Z"
}
```

## List Sandboxes

```bash
curl https://api.caged.dev/v1/sandboxes \
  -H "Authorization: Bearer caged_sk_..."
```

**Response** `200 OK`

```json
[
  {
    "id": "cage-a1b2c3d4",
    "status": "running",
    "template": "node-20",
    "cpus": 2,
    "memory_mb": 1024,
    "created_at": "2026-06-08T10:00:00Z"
  }
]
```

## Get Sandbox

```bash
curl https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4 \
  -H "Authorization: Bearer caged_sk_..."
```

## Execute a Command

Run a shell command inside a sandbox and get the output back. Supports pipes, redirects, and environment variables. This is how you interact with sandboxes programmatically — including prompting installed AI agents.

`POST /v1/sandboxes/{id}/exec`

<ParamField body="command" type="string" required>
  Shell command to execute. Runs via `/bin/sh -c` inside the sandbox with the sandbox's environment (including any `env` vars set at creation).
</ParamField>

```bash
curl -X POST https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/exec \
  -H "Authorization: Bearer caged_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"command": "python --version"}'
```

**Response** `200 OK`

```json
{
  "output": "Python 3.12.3\n",
  "exit_code": 0
}
```

**Prompting an agent** — if the sandbox was created with `"agents": ["claude"]`, you can prompt Claude Code non-interactively:

```bash
curl -X POST https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/exec \
  -H "Authorization: Bearer caged_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"command": "cd /workspace && claude -p \"Summarize what this repo does\""}'
```

**Error semantics** — the three outcomes are distinguishable:

| Case | `exit_code` | `error` | Meaning |
|------|------------|---------|---------|
| Success | `0` | empty | Command ran and succeeded |
| Command failed | non-zero | empty | Command ran; its stderr is in `output` |
| Infrastructure failure | `1` | set | Sandbox unreachable, not running, etc. |

<Note>
A failed command still returns HTTP `200` — the API call succeeded; the command result is data. Exec requests can run for up to 5 minutes, so long agent prompts are fine.
</Note>

For a fully interactive TTY session (e.g. the Claude Code TUI), use the [terminal WebSocket](/api-reference/sessions) instead: `wss://api.caged.dev/v1/sandboxes/{id}/terminal`.

## Pause Sandbox

Freezes the sandbox in memory. No compute charges while paused. An auto-snapshot is created.

```bash
curl -X POST https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/pause \
  -H "Authorization: Bearer caged_sk_..."
```

## Resume Sandbox

Unfreezes a paused sandbox.

```bash
curl -X POST https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/resume \
  -H "Authorization: Bearer caged_sk_..."
```

## Destroy Sandbox

Permanently destroys a sandbox and all its data.

<Warning>
This action is irreversible. Create a snapshot first if you need to preserve the state.
</Warning>

```bash
curl -X DELETE https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4 \
  -H "Authorization: Bearer caged_sk_..."
```

**Response** `204 No Content`

## Available Templates

Pre-configured sandbox environments with common runtimes and tools.

### JavaScript / TypeScript

| Template | Runtime | Includes |
|----------|---------|----------|
| `node-22` | Node.js 22.x LTS | npm, yarn, pnpm, typescript |
| `node-20` | Node.js 20.x LTS | npm, yarn, pnpm, typescript |

### Python

| Template | Runtime | Includes |
|----------|---------|----------|
| `python-312` | Python 3.12 | pip, poetry, virtualenv, black, ruff |
| `python-311` | Python 3.11 | pip, poetry, virtualenv, black, ruff |

### Base

| Template | Description |
|----------|-------------|
| `minimal` | Ubuntu 24.04 with SSH, git, curl, build-essential. Install any runtime yourself. |

### Desktop

| Template | Description |
|----------|-------------|
| `desktop` | Full virtual desktop (Xvfb, Openbox, x11vnc, Chromium). For screen-grounded agents. |

Aliases: `gui`, `computer`

<Tip>
Using `minimal`? You can install any runtime manually. The sandbox persists your changes until destroyed.
</Tip>

## Screen Endpoints (Desktop Sandboxes)

These endpoints are available for sandboxes created with the `desktop` template.

### Screenshot

Get a screenshot of the virtual desktop.

`GET /v1/sandboxes/{id}/screen/screenshot`

<ParamField query="format" type="string">
  Response format: `base64` (JSON) or omit for raw PNG binary.
</ParamField>

```bash
# Get base64 JSON
curl "https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/screen/screenshot?format=base64" \
  -H "Authorization: Bearer caged_sk_..."
```

**Response** `200 OK`

```json
{
  "image_base64": "iVBORw0KGgo...",
  "format": "png"
}
```

```bash
# Get raw PNG
curl "https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/screen/screenshot" \
  -H "Authorization: Bearer caged_sk_..." \
  -o screenshot.png
```

### Screen WebSocket

Real-time screen interaction via WebSocket.

`GET /v1/sandboxes/{id}/screen` (WebSocket upgrade)

**Connect:**
```javascript
const ws = new WebSocket(
  "wss://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/screen",
  { headers: { Authorization: "Bearer caged_sk_..." } }
);
```

**Message Types:**

| Type | Direction | Description |
|------|-----------|-------------|
| `screenshot` | send/receive | Request/receive screenshot |
| `click` | send | Click at coordinates |
| `type` | send | Type text |
| `key` | send | Press key |
| `scroll` | send | Scroll at coordinates |
| `drag` | send | Drag between coordinates |
| `launch` | send | Launch application |
| `info` | send/receive | Get screen info |

**Example messages:**

```json
// Request screenshot
{"type": "screenshot"}

// Click
{"type": "click", "x": 640, "y": 400, "button": "left"}

// Type text
{"type": "type", "text": "Hello, world!"}

// Press key
{"type": "key", "key": "Return"}

// Scroll down
{"type": "scroll", "x": 640, "y": 400, "direction": "down", "amount": 3}

// Launch app
{"type": "launch", "command": "chromium-browser https://example.com"}
```

### VNC Info

Get VNC connection information for the desktop sandbox.

`GET /v1/sandboxes/{id}/screen/vnc`

```bash
curl "https://api.caged.dev/v1/sandboxes/cage-a1b2c3d4/screen/vnc" \
  -H "Authorization: Bearer caged_sk_..."
```

**Response** `200 OK`

```json
{
  "sandbox_id": "cage-a1b2c3d4",
  "vnc_port": 5900,
  "status": "available",
  "note": "Use port forwarding to access VNC. The VNC server runs on port 5900 inside the sandbox with no password."
}
```

Use with [port forwarding](/api-reference/ports) for external VNC client access.
