Skip to content
Get Started

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.

POST /v3/sandboxes create a sandbox
POST /v3/sandboxes/{id}/exec run a shell command
POST /v3/sandboxes/{id}/files write a file
GET /v3/sandboxes/{id}/files?path= read a file
GET /v3/sandboxes list sandboxes + usage + agent connect prompt
DELETE /v3/sandboxes/{id} destroy a sandbox

Sandboxes 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_ID
sb-api-secret-key: YOUR_API_SECRET_KEY

Both values are in your dashboard’s API settings. The same pair works for the whole Sendblue API.

PlanCompute allowanceActive sandboxes
free_api$100 (one-time)1
Paid plans$5005

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:

  1. Your primary number must be verified: send any message from that phone to your Sendblue number.
  2. 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.
  3. 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.

For a fresh free API account, the quickest path is CLI phone setup:

Terminal window
npx -y @sendblue/cli setup --phone +15551234567 --company my-agent

The 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 example SB 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.

POST https://api.sendblue.co/v3/sandboxes
Terminal window
curl -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.

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.

POST https://api.sendblue.co/v3/sandboxes/{sandbox_id}/exec
ParameterTypeRequiredDescription
commandstringYesShell command to run (max 8,192 chars)
cwdstringNoWorking directory (max 1,024 chars)
envobjectNoExtra environment variables (max 64; keys ≤128 chars, values ≤4,096)
timeout_msnumberNo1,000–120,000. Default 60,000
Terminal window
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.

POST https://api.sendblue.co/v3/sandboxes/{sandbox_id}/files # write
GET https://api.sendblue.co/v3/sandboxes/{sandbox_id}/files # read

Write (path + content, content up to 65,536 characters — fetch bigger payloads from inside the sandbox with exec + curl; parent directories are created automatically):

Terminal window
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):

Terminal window
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.)

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 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.

StatusCodeMeaningWhat your agent should do
401unauthorizedMissing or invalid API credentialsStop; verify both headers and key values with the account owner
400invalid_requestBody/params failed validationFix the request per details; don’t retry as-is
403sandbox_primary_phone_missingAccount has no primary phone. Run phone setup or add one during onboardingStop and tell the human
403sandbox_access_deniedPlan doesn’t include sandboxes, or the primary number isn’t verified yetIf 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
403sandbox_credit_claimedThis phone number already claimed free credits on another accountStop and tell the human
403sandbox_line_type_ineligibleVoIP/toll-free/virtual numbers can’t claim free creditsStop and tell the human (paid plans aren’t screened)
402balance_exhaustedCompute balance used up; you can still list and destroy sandboxesStop and tell the human to upgrade
409sandbox_limit_reachedToo many active sandboxes for the planDestroy one, then retry
404not_foundUnknown or already-destroyed sandbox idDon’t retry; create a new sandbox if needed
404file_not_foundRead of a nonexistent fileCheck the path — use absolute paths, and remember the filesystem resets when the sandbox sleeps
413file_too_largeFile read over 1 MiBSplit or compress it inside the sandbox first
408command_timeoutCommand didn’t finish within timeout_msRaise timeout_ms (≤120000) or background the work; completed side effects persist
429rate_limit_exceededOver 60 requests / 10 sBack off briefly and retry
429free_tier_pausedFree tier temporarily paused globallyWait or tell the human to upgrade
503 / 504service_unavailable / sandbox_timeoutTransient sandbox-service issueRetry once after a short wait, then stop
LimitValue
Machine2 vCPU / 8 GiB RAM / 16 GB disk (x86_64 Linux)
Idle sleep10 minutes with no API calls to that sandbox (billing stops; filesystem resets on wake)
Command timeout120 s max per exec — run longer work in background processes
File write / read65,536 chars / 1 MiB via API (unlimited inside the sandbox)
Rate limit60 requests per 10 seconds