Agent Sandboxes API
Give your AI agent an isolated cloud Linux machine — create, run commands, read/write files, and destroy sandboxes via the Sendblue API
Every free_api and paid-plan Sendblue account can spin up sandboxes: isolated Linux machines in the cloud that your AI agent controls over HTTP. Run shell commands, install packages, clone repos, start dev servers — each sandbox is a real container with its own filesystem, isolated from every other account.
Free API accounts get $100 of sandbox compute free once their phone number is verified. Paid plans get a larger allowance and more concurrent sandboxes.
How it works
Section titled “How it works”POST /v3/sandboxes create a sandboxPOST /v3/sandboxes/{id}/exec run a shell commandPOST /v3/sandboxes/{id}/files write a fileGET /v3/sandboxes/{id}/files?path= read a fileGET /v3/sandboxes list sandboxes + usage + agent connect promptDELETE /v3/sandboxes/{id} destroy a sandboxSandboxes sleep automatically after 10 minutes idle and wake transparently on the next exec or file operation — you’re only billed while a sandbox is awake. Destroying a sandbox refunds the unused portion of its final awake window.
Sandboxes are ephemeral across sleep. Waking gives you the same sandbox id on a fresh filesystem — anything you wrote is gone. Treat every awake window as a complete unit of work: finish, then read your results out via the files API (or push them somewhere with curl) before going idle. Keeping a sandbox awake preserves state for as long as you keep working — any operation on that sandbox (exec or a file read/write) resets the idle timer. Listing sandboxes with GET /v3/sandboxes does not.
All endpoints use your standard API credentials:
sb-api-key-id: YOUR_API_KEY_IDsb-api-secret-key: YOUR_API_SECRET_KEYBoth values are in your dashboard’s API settings. The same pair works for the whole Sendblue API.
Pricing and the free tier
Section titled “Pricing and the free tier”| Plan | Compute allowance | Active sandboxes |
|---|---|---|
free_api | $100 (one-time) | 1 |
| Paid plans | $500 | 5 |
Compute is metered at $0.33 per awake-hour. A sandbox that runs a command and then sleeps 10 minutes later costs about $0.055. Sleeping sandboxes cost nothing.
Free-tier eligibility. The $100 free tier is tied to your account’s primary phone number — the number you add during onboarding:
- Your primary number must be verified: send any message from that phone to your Sendblue number.
- Each phone number can claim the free tier once, ever, across all of Sendblue. A number that has already claimed on another account gets
403 sandbox_credit_claimed. - The number must be a standard mobile line. VoIP, toll-free, and other virtual/non-mobile numbers aren’t eligible for free credits (
403 sandbox_line_type_ineligible) — but work fine on paid plans.
Phone verification during setup
Section titled “Phone verification during setup”For a fresh free API account, the quickest path is CLI phone setup:
npx -y @sendblue/cli setup --phone +15551234567 --company my-agentThe CLI requests a temporary Sendblue verification route and shows the human two values:
sharedNumber: the Sendblue number to text.challenge: the exact message body to send, for exampleSB SETUP 123456.
The human must send the challenge from the same phone passed with --phone to sharedNumber. If you render a convenience SMS link in an agent, build it from those response fields:
sms:<sharedNumber>?&body=<url-encoded challenge>The ?&body= form is deliberate, not a typo: iOS parses &body and Android parses ?body, and the combined form works on both — don’t “fix” it to ?body=. For example: sms:+15559876543?&body=SB%20SETUP%20123456. The phoneNumber is the sender being verified; it is not the SMS recipient.
Wait for setup to finish and return API credentials before creating a sandbox. Phone setup verifies the primary phone for sandbox free-tier eligibility, so a fresh phone-onboarded account should not need a second verification text before its first sandbox request.
When your balance runs out you’ll get 402 balance_exhausted. You can still list and destroy your sandboxes; upgrade to a paid plan to keep computing.
Create a sandbox
Section titled “Create a sandbox”POST https://api.sendblue.co/v3/sandboxescurl -X POST 'https://api.sendblue.co/v3/sandboxes' \ -H 'sb-api-key-id: YOUR_API_KEY_ID' \ -H 'sb-api-secret-key: YOUR_API_SECRET_KEY'{ "data": { "id": "sbx_a9c7253d-8567-4fff-bf81-08a25076e50f", "object": "sandbox", "status": "running", "created_at": "2026-07-19T18:03:14.399Z" }}Returns 201 once the sandbox has actually booted (typically a few seconds) — the id is immediately usable for exec, no polling needed.
What’s inside
Section titled “What’s inside”Ubuntu 22.04 (x86_64), running as root, with full outbound internet access. Preinstalled: bash, git, curl, wget, Node 22 (node/npm/npx), bun, jq, unzip. Python is not preinstalled — install it in ~10 seconds when you need it:
{"command": "apt-get update -qq && apt-get install -y -qq python3 python3-pip", "timeout_ms": 120000}Anything else is an apt-get install -y away.
Run a command
Section titled “Run a command”POST https://api.sendblue.co/v3/sandboxes/{sandbox_id}/exec| Parameter | Type | Required | Description |
|---|---|---|---|
command | string | Yes | Shell command to run (max 8,192 chars) |
cwd | string | No | Working directory (max 1,024 chars) |
env | object | No | Extra environment variables (max 64; keys ≤128 chars, values ≤4,096) |
timeout_ms | number | No | 1,000–120,000. Default 60,000 |
curl -X POST 'https://api.sendblue.co/v3/sandboxes/sbx_YOUR_ID/exec' \ -H 'sb-api-key-id: YOUR_API_KEY_ID' \ -H 'sb-api-secret-key: YOUR_API_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{"command": "git clone https://github.com/you/repo && cd repo && npm install && npm test", "timeout_ms": 120000}'{ "data": { "object": "sandbox.command", "sandbox_id": "sbx_...", "command": "git clone https://github.com/you/repo && cd repo && npm install && npm test", "success": true, "exit_code": 0, "stdout": "...test output...\n", "stderr": "", "duration_ms": 48210 }}Execs share one persistent shell while the sandbox is awake. cd, exported variables, and activated virtualenvs carry over between calls. That shell state resets along with the filesystem when the sandbox sleeps — don’t depend on it across awake windows. You can also pass cwd/env explicitly per call.
Waking is transparent. If the sandbox is asleep, the first request simply takes a few extra seconds while it wakes — no polling or retry needed.
Long-running work: commands are capped at 120 s. For anything longer, start a background process and poll it:
{"command": "nohup npm run build > /root/build.log 2>&1 & echo started"}then check on it with {"command": "tail -5 /root/build.log"}. Background processes keep running while the sandbox is awake (each exec or file read/write on the sandbox resets the 10-minute idle timer — polling the log this way also keeps it awake); when the sandbox sleeps, processes stop and the filesystem resets — collect results before going idle. On a 408 command_timeout the command is terminated, but work it completed first — files written, packages installed — remains on disk; check state before re-running.
stdout/stderr are capped at 262,144 characters (~256 KiB) each (stdout_truncated: true flags a cut). Commands that outlive timeout_ms return 408 command_timeout.
Read and write files
Section titled “Read and write files”POST https://api.sendblue.co/v3/sandboxes/{sandbox_id}/files # writeGET https://api.sendblue.co/v3/sandboxes/{sandbox_id}/files # readWrite (path + content, content up to 65,536 characters — fetch bigger payloads from inside the sandbox with exec + curl; parent directories are created automatically):
curl -X POST 'https://api.sendblue.co/v3/sandboxes/sbx_YOUR_ID/files' \ -H 'sb-api-key-id: YOUR_API_KEY_ID' \ -H 'sb-api-secret-key: YOUR_API_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{"path": "/root/script.py", "content": "print(\"hello\")"}'{ "data": { "object": "sandbox.file", "sandbox_id": "sbx_...", "path": "/root/script.py", "size_bytes": 14 }}Read (files up to 1 MiB; URL-encode the path query param):
curl 'https://api.sendblue.co/v3/sandboxes/sbx_YOUR_ID/files?path=/root/script.py' \ -H 'sb-api-key-id: YOUR_API_KEY_ID' \ -H 'sb-api-secret-key: YOUR_API_SECRET_KEY'{ "data": { "object": "sandbox.file", "sandbox_id": "sbx_...", "path": "/root/script.py", "size_bytes": 14, "content": "print(\"hello\")" }}content is the raw UTF-8 text of the file. For binary files, base64 them inside the sandbox first (exec + base64 file.bin) and read that.
List sandboxes, check usage, connect your agent
Section titled “List sandboxes, check usage, connect your agent”GET https://api.sendblue.co/v3/sandboxes{ "data": { "object": "list", "sandboxes": [ { "id": "sbx_...", "object": "sandbox", "status": "sleeping", "created_at": "..." } ], "usage": { "object": "sandbox.usage", "plan": "free_api", "used_usd": 0.055, "cap_usd": 100, "remaining_usd": 99.945, "rate_usd_per_hour": 0.33, "max_active_sandboxes": 1 }, "connect": { "object": "sandbox.connect", "prompt": "You have access to a Sendblue sandbox: an isolated Linux container you control over HTTP. ..." } }}connect.prompt is the fastest way to put a sandbox in your agent’s hands: it’s a ready-made system-prompt snippet that teaches any AI agent the full sandbox API. Substitute your real keys for the YOUR_API_KEY_ID / YOUR_API_SECRET_KEY placeholders, paste it into your agent’s instructions, and the agent can drive the sandbox on its own. (If your account lives on a non-default Sendblue host, also swap the base URL in the prompt.)
Destroy a sandbox
Section titled “Destroy a sandbox”DELETE https://api.sendblue.co/v3/sandboxes/{sandbox_id}{ "data": { "id": "sbx_...", "object": "sandbox", "deleted": true } }Destroys the container and its disk immediately, and refunds the unused portion of the current awake window — destroy sandboxes you’re done with to conserve your balance. Listing and destroying always work, even if your free-tier eligibility lapses.
Errors
Section titled “Errors”Errors use the standard v3 envelope: { "error": { "code", "message", "request_id", "details?" } } — details carries specifics like used_usd/cap_usd on a 402 or active_sandboxes/max_active_sandboxes on a 409.
| Status | Code | Meaning | What your agent should do |
|---|---|---|---|
| 401 | unauthorized | Missing or invalid API credentials | Stop; verify both headers and key values with the account owner |
| 400 | invalid_request | Body/params failed validation | Fix the request per details; don’t retry as-is |
| 403 | sandbox_primary_phone_missing | Account has no primary phone. Run phone setup or add one during onboarding | Stop and tell the human |
| 403 | sandbox_access_denied | Plan doesn’t include sandboxes, or the primary number isn’t verified yet | If this is a fresh phone setup, wait for setup to finish before retrying. Otherwise ask the human to text their Sendblue number from the primary phone, wait a few seconds, then retry |
| 403 | sandbox_credit_claimed | This phone number already claimed free credits on another account | Stop and tell the human |
| 403 | sandbox_line_type_ineligible | VoIP/toll-free/virtual numbers can’t claim free credits | Stop and tell the human (paid plans aren’t screened) |
| 402 | balance_exhausted | Compute balance used up; you can still list and destroy sandboxes | Stop and tell the human to upgrade |
| 409 | sandbox_limit_reached | Too many active sandboxes for the plan | Destroy one, then retry |
| 404 | not_found | Unknown or already-destroyed sandbox id | Don’t retry; create a new sandbox if needed |
| 404 | file_not_found | Read of a nonexistent file | Check the path — use absolute paths, and remember the filesystem resets when the sandbox sleeps |
| 413 | file_too_large | File read over 1 MiB | Split or compress it inside the sandbox first |
| 408 | command_timeout | Command didn’t finish within timeout_ms | Raise timeout_ms (≤120000) or background the work; completed side effects persist |
| 429 | rate_limit_exceeded | Over 60 requests / 10 s | Back off briefly and retry |
| 429 | free_tier_paused | Free tier temporarily paused globally | Wait or tell the human to upgrade |
| 503 / 504 | service_unavailable / sandbox_timeout | Transient sandbox-service issue | Retry once after a short wait, then stop |
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Machine | 2 vCPU / 8 GiB RAM / 16 GB disk (x86_64 Linux) |
| Idle sleep | 10 minutes with no API calls to that sandbox (billing stops; filesystem resets on wake) |
| Command timeout | 120 s max per exec — run longer work in background processes |
| File write / read | 65,536 chars / 1 MiB via API (unlimited inside the sandbox) |
| Rate limit | 60 requests per 10 seconds |