---
title: Drive Agents Programmatically
description: "Create a sandbox via the API or SDKs, prompt an AI agent inside it, and read the results — no CLI or dashboard needed."
---

# Drive Agents Programmatically

Everything the Caged CLI and dashboard do goes through the public API — so you can script the entire loop: create a sandbox with an agent pre-installed, send it prompts, inspect the output, and tear it down. This is the foundation for building agent pipelines, batch jobs, and custom tooling.

## Prerequisites

- A Caged API key (`caged_sk_...`) — create one in the [dashboard](https://caged.dev/dashboard) or with `caged keys create`
- An `ANTHROPIC_API_KEY` for Claude Code (the agent talks to Anthropic with **your** key — Caged never proxies or resells tokens)

## 1. Create a Sandbox with an Agent

Create a Python sandbox that clones your repo, installs Claude Code, and caps spend at $5:

<CodeGroup>
```bash title="curl"
curl -X POST https://api.caged.dev/v1/sandboxes \
  -H "Authorization: Bearer $CAGED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "python-312",
    "memory_mb": 1024,
    "repo": "https://github.com/your-org/your-project",
    "agents": ["claude"],
    "budget": 5.00,
    "env": {"ANTHROPIC_API_KEY": "sk-ant-..."}
  }'
```

```python title="Python"
from caged import Caged
import os

caged = Caged(api_key=os.environ["CAGED_API_KEY"])

sandbox = caged.sandboxes.create(
    template="python-312",
    memory_mb=1024,
    repo="https://github.com/your-org/your-project",
    agents=["claude"],
    budget=5.00,
    env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
)
print(f"{sandbox.id} is {sandbox.status}")
```

```typescript title="TypeScript"
import { Caged } from "@caged-dev/sdk";

const caged = new Caged({ apiKey: process.env.CAGED_API_KEY! });

const sandbox = await caged.sandboxes.create({
  template: "python-312",
  memory_mb: 1024,
  repo: "https://github.com/your-org/your-project",
  agents: ["claude"],
  budget: 5.0,
  env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
console.log(`${sandbox.id} is ${sandbox.status}`);
```
</CodeGroup>

The create call returns once the repo is cloned and the agent is installed (allow up to a few minutes).

<Note>
Sandboxes that install agents are automatically provisioned with at least 1024 MB of memory — agent installers need the headroom.
</Note>

## 2. Prompt the Agent

Use the exec endpoint to run `claude -p` (print mode) with any prompt. The command runs inside the VM with the repo at `/workspace`:

<CodeGroup>
```bash title="curl"
curl -X POST https://api.caged.dev/v1/sandboxes/$SANDBOX_ID/exec \
  -H "Authorization: Bearer $CAGED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "cd /workspace && claude -p \"Summarize what this repo does in 3 bullets\""}'
```

```python title="Python"
result = caged.sandboxes.exec(
    sandbox.id,
    'cd /workspace && claude -p "Summarize what this repo does in 3 bullets"',
)
print(result.output)
```

```typescript title="TypeScript"
const result = await caged.sandboxes.exec(
  sandbox.id,
  'cd /workspace && claude -p "Summarize what this repo does in 3 bullets"'
);
console.log(result.output);
```
</CodeGroup>

**Response**

```json
{
  "output": "- A Python library for parsing blockchain transactions...\n- ...\n- ...\n",
  "exit_code": 0
}
```

## 3. Let the Agent Make Changes

Claude Code can edit files, run tests, and commit — give it permission with `--dangerously-skip-permissions` (safe here: the blast radius is the disposable VM, which is exactly what Caged is for):

```python
result = caged.sandboxes.exec(
    sandbox.id,
    'cd /workspace && claude -p --dangerously-skip-permissions '
    '"Fix the failing tests and run pytest to verify"',
    timeout=600,  # agent work can take a while
)
print(result.output)
```

Continue the same conversation across calls with `-c`:

```python
followup = caged.sandboxes.exec(
    sandbox.id,
    'cd /workspace && claude -c -p "Now add a changelog entry for the fix"',
)
```

## 4. Inspect the Results

Read what the agent actually changed — without trusting its summary:

```python
# Diff of all changes in the workspace
diff = caged.files.git_diff(sandbox.id)
print(diff)

# Read a specific file
content = caged.files.read(sandbox.id, "/workspace/CHANGELOG.md")

# Verify tests pass yourself
check = caged.sandboxes.exec(sandbox.id, "cd /workspace && python -m pytest -q")
print("tests pass" if check.ok else f"failed: {check.output}")
```

## 5. Handle Failures Properly

Exec distinguishes *the command failed* from *the platform failed*:

```python
result = caged.sandboxes.exec(sandbox.id, "cd /workspace && python -m pytest -q")

if result.error:
    # Infrastructure problem: sandbox died, network issue, etc.
    raise RuntimeError(f"sandbox failure: {result.error}")
elif result.exit_code != 0:
    # Command ran and failed — stderr is in output
    print(f"tests failed (exit {result.exit_code}):\n{result.output}")
else:
    print(result.output)
```

## 6. Clean Up

```python
# Snapshot first if you want to keep the agent's work
snapshot = caged.snapshots.create(sandbox.id, name="agent-run-1")

caged.sandboxes.destroy(sandbox.id)
```

## Full Script

A complete, runnable batch job — point an agent at a repo, collect its answer, destroy the sandbox:

```python
import os
from caged import Caged

caged = Caged(api_key=os.environ["CAGED_API_KEY"])

sandbox = caged.sandboxes.create(
    template="python-312",
    memory_mb=1024,
    repo="https://github.com/your-org/your-project",
    agents=["claude"],
    budget=5.00,
    env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
)

try:
    result = caged.sandboxes.exec(
        sandbox.id,
        'cd /workspace && claude -p "Review this codebase for security issues. '
        'List the top 3 with file and line references."',
        timeout=600,
    )
    if result.ok:
        print(result.output)
    else:
        print(f"agent failed (exit {result.exit_code}): {result.output or result.error}")
finally:
    caged.sandboxes.destroy(sandbox.id)
```

## Interactive Sessions

`exec` is one-shot and non-interactive. For a live TTY (e.g. the full Claude Code TUI), connect to the terminal WebSocket the dashboard IDE uses:

```
wss://api.caged.dev/v1/sandboxes/{id}/terminal?rows=40&cols=120&token=caged_sk_...
```

Send raw keystrokes, receive raw terminal output — any WebSocket client works. Sessions stay open while idle and are recorded for [replay](/guides/trust-scoring).

## See Also

- [Exec API reference](/api-reference/sandboxes#execute-a-command)
- [CI Pipeline Agent Testing](/recipes/ci-pipeline) — run this pattern in CI
- [Budget-Controlled Batch Jobs](/recipes/budget-batch) — fan out across many sandboxes
- [Multi-Agent Collaboration](/recipes/multi-agent)
