{"openapi":"3.0.3","info":{"title":"Sandbox as a Service API","version":"1.0.0","description":"Create disposable cloud sandboxes, run commands inside them, and tear them down.\n\nEach sandbox is a dedicated virtual machine with its own kernel, filesystem and network stack.\nSandboxes are single-tenant and are destroyed when they expire or when you delete them.\n\nAvailable sizes: `small` (2 vCPU / 4 GB RAM / 40 GB disk), `medium` (4 vCPU / 8 GB RAM / 80 GB disk), `large` (8 vCPU / 16 GB RAM / 160 GB disk).\n\n## Authentication\nSend your API key as a bearer token: `Authorization: Bearer aas_sk_...`.\nCreate and revoke keys in the dashboard. Keys are shown once at creation time.\n\n## Errors\nErrors use standard HTTP status codes and return a JSON body with a stable `error.type`\nplus a `request_id` that identifies the request in our logs.\n\n## Rate limits\n120 requests per minute per account; 40 sandbox creations per minute.\nResponses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` headers.","contact":{"name":"Support","url":"https://sandbox-as-a-service.com/contact"}},"servers":[{"url":"https://sandbox-as-a-service.com/v1","description":"Production"}],"tags":[{"name":"Sandboxes","description":"Create, inspect and destroy sandboxes."},{"name":"Execution","description":"Run commands and read their results."},{"name":"Ports","description":"Expose a port inside a sandbox on a public preview URL."},{"name":"Files","description":"Read, write and list files inside a sandbox."},{"name":"Account","description":"Balance, limits and usage."}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key issued in the dashboard, sent as `Authorization: Bearer aas_sk_...`."}},"schemas":{"Sandbox":{"type":"object","properties":{"object":{"type":"string","enum":["sandbox"]},"id":{"type":"string","example":"sbx_9f2c4a1b8d3e6f0a12"},"name":{"type":"string","nullable":true,"example":"data-cleaning-job"},"status":{"type":"string","enum":["provisioning","running","deleting","deleted","failed"],"description":"Lifecycle state. Commands can only run while the sandbox is `running`."},"size":{"type":"string","enum":["small","medium","large"]},"resources":{"type":"object","properties":{"vcpu":{"type":"integer","example":2},"memory_gb":{"type":"integer","example":4},"disk_gb":{"type":"integer","example":40}}},"timeout_minutes":{"type":"integer","example":15},"created_at":{"type":"string","format":"date-time"},"ready_at":{"type":"string","format":"date-time","nullable":true},"expires_at":{"type":"string","format":"date-time","nullable":true},"deleted_at":{"type":"string","format":"date-time","nullable":true}}},"Execution":{"type":"object","properties":{"object":{"type":"string","enum":["execution"]},"id":{"type":"string","example":"exec_4b1d2e3f4a5b6c7d"},"sandbox_id":{"type":"string","example":"sbx_9f2c4a1b8d3e6f0a12"},"status":{"type":"string","enum":["running","completed","timeout","failed"]},"exit_code":{"type":"integer","nullable":true,"example":0},"stdout":{"type":"string","example":"hello from the sandbox\n"},"stderr":{"type":"string","example":""},"truncated":{"type":"boolean","description":"True when output exceeded 1 MiB per stream and was cut off."},"duration_ms":{"type":"integer","nullable":true,"example":412},"started_at":{"type":"string","format":"date-time","nullable":true},"finished_at":{"type":"string","format":"date-time","nullable":true}}},"Error":{"type":"object","required":["error","request_id"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","description":"Stable machine-readable error code.","example":"insufficient_credits"},"message":{"type":"string","example":"Insufficient credit balance — add credits to create sandboxes."}}},"request_id":{"type":"string","example":"a2f1c0de-6b1a-4a0e-9c2f-2f4d0f0a1b23"}}}}},"paths":{"/sandboxes":{"post":{"tags":["Sandboxes"],"summary":"Create a sandbox","description":"Provisions a sandbox and returns once it is ready to accept commands. Pass an `Idempotency-Key` header to make retries safe: repeating a request with the same key returns the original sandbox instead of creating a second one.","operationId":"createSandbox","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Client-generated key that makes this creation request safe to retry."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"size":{"type":"string","enum":["small","medium","large"],"default":"small","description":"small: 2 vCPU, 4 GB RAM, 40 GB disk; medium: 4 vCPU, 8 GB RAM, 80 GB disk; large: 8 vCPU, 16 GB RAM, 160 GB disk"},"name":{"type":"string","maxLength":100,"description":"Optional label for your own reference."},"timeout_minutes":{"type":"integer","minimum":1,"maximum":1440,"default":15,"description":"The sandbox is destroyed automatically after this many minutes."}}},"examples":{"default":{"summary":"Smallest sandbox, default timeout","value":{}},"named":{"summary":"Named sandbox with a longer lifetime","value":{"size":"medium","name":"nightly-etl","timeout_minutes":60}}}}}},"responses":{"200":{"description":"An existing sandbox was returned because the idempotency key was reused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}}},"201":{"description":"Sandbox created and ready.","headers":{"Location":{"schema":{"type":"string"},"description":"URL of the created sandbox."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"},"example":{"object":"sandbox","id":"sbx_9f2c4a1b8d3e6f0a12","name":null,"status":"running","size":"small","resources":{"vcpu":2,"memory_gb":4,"disk_gb":40},"timeout_minutes":15,"created_at":"2026-08-22T10:04:11.220Z","ready_at":"2026-08-22T10:04:44.980Z","expires_at":"2026-08-22T10:19:44.980Z","deleted_at":null}}}},"400":{"description":"Invalid request parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credit balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Account suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit or concurrent sandbox limit reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Capacity temporarily unavailable — retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Sandboxes"],"summary":"List sandboxes","operationId":"listSandboxes","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100}},{"name":"starting_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Cursor from a previous response’s `next_cursor`."},{"name":"include_deleted","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"A page of sandboxes, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Sandbox"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Sandboxes"],"summary":"Get a sandbox","operationId":"getSandbox","responses":{"200":{"description":"The sandbox.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Sandboxes"],"summary":"Destroy a sandbox","description":"Destroys the sandbox and stops billing for it. Data inside the sandbox is lost.","operationId":"deleteSandbox","responses":{"200":{"description":"Sandbox destroyed.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string"},"id":{"type":"string"},"status":{"type":"string","enum":["deleted"]},"deleted_at":{"type":"string","format":"date-time"}}}}}},"202":{"description":"Teardown accepted and completing in the background."},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}/exec":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["Execution"],"summary":"Run a command","description":"Runs a shell command inside the sandbox as an unprivileged user and waits for it to finish. Output is capped at 1 MiB per stream; anything beyond that sets `truncated`.","operationId":"execCommand","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["command"],"properties":{"command":{"type":"string","maxLength":100000,"example":"python3 -c \"print(6*7)\""},"timeout_ms":{"type":"integer","minimum":1000,"maximum":600000,"default":60000},"cwd":{"type":"string","default":"/workspace"},"env":{"type":"object","additionalProperties":{"type":"string"},"description":"Environment variables for this command."}}},"examples":{"python":{"summary":"Run Python","value":{"command":"python3 -c \"print(6*7)\""}},"node":{"summary":"Run Node.js","value":{"command":"node -e \"console.log(process.version)\""}},"install":{"summary":"Install a package, then use it","value":{"command":"pip install --quiet --break-system-packages --user cowsay && python3 -c \"import cowsay; cowsay.cow('hi')\"","timeout_ms":120000}}}}}},"responses":{"200":{"description":"The command finished. A non-zero `exit_code` is still a 200 response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Execution"},"example":{"object":"execution","id":"exec_4b1d2e3f4a5b6c7d","sandbox_id":"sbx_9f2c4a1b8d3e6f0a12","status":"completed","exit_code":0,"stdout":"42\n","stderr":"","truncated":false,"duration_ms":412}}}},"400":{"description":"Invalid request parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Credit balance exhausted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Sandbox is not running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}/ports":{"post":{"tags":["Ports"],"summary":"Expose a port on a public preview URL","description":"Returns an https URL that reaches the given port inside the sandbox. The server does not need to bind a public interface — listening on localhost is enough, exactly as on a laptop. The URL carries an unguessable token and stops working the moment the sandbox does. Asking twice for the same port returns the same URL.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["port"],"properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"example":3000}}}}}},"responses":{"201":{"description":"The preview URL.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","example":"port"},"sandbox_id":{"type":"string"},"port":{"type":"integer","example":3000},"url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"}}}}}},"400":{"description":"The port is not a valid, exposable port.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Ports"],"summary":"List the preview URLs open on a sandbox","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Open previews.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}/ports/{port}":{"delete":{"tags":["Ports"],"summary":"Close a preview URL","description":"The URL stops working immediately. The server inside the sandbox is left alone.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"port","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Closed.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No open preview for that port.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}/files":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"put":{"tags":["Files"],"summary":"Write a file","description":"Writes a file into the sandbox. Relative paths resolve inside `/workspace`. Content is transferred verbatim — it is never interpreted by a shell — so any bytes are safe. Use `encoding: \"base64\"` for binary. Maximum 8 MiB.","operationId":"writeFile","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","example":"analysis.py"},"content":{"type":"string","example":"print(\"hello\")\n"},"encoding":{"type":"string","enum":["utf8","base64"],"default":"utf8"}}}}}},"responses":{"201":{"description":"File written.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["file"]},"sandbox_id":{"type":"string"},"path":{"type":"string"},"bytes":{"type":"integer"}}}}}},"400":{"description":"Invalid path or content.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Sandbox is not running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File exceeds the size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Files"],"summary":"Read a file or list a directory","description":"With `path` pointing at a file, returns its contents. With no `path`, or with `list=true`, returns a directory listing. Use `encoding=base64` to read binary files.","operationId":"readFile","parameters":[{"name":"path","in":"query","schema":{"type":"string"},"description":"Defaults to /workspace."},{"name":"encoding","in":"query","schema":{"type":"string","enum":["utf8","base64"]}},{"name":"list","in":"query","schema":{"type":"boolean"},"description":"Force a directory listing."}],"responses":{"200":{"description":"The file contents, or a directory listing.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"object":{"type":"string","enum":["file"]},"path":{"type":"string"},"bytes":{"type":"integer"},"encoding":{"type":"string"},"content":{"type":"string"}}},{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"path":{"type":"string"},"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["file","dir","symlink"]},"size_bytes":{"type":"integer","nullable":true}}}}}}]}}}},"404":{"description":"No such file, or no sandbox with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Sandbox is not running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File exceeds the read limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Files"],"summary":"Delete a file or directory","operationId":"deleteFile","parameters":[{"name":"path","in":"query","required":true,"schema":{"type":"string"}},{"name":"recursive","in":"query","schema":{"type":"boolean"},"description":"Required to delete a directory."}],"responses":{"200":{"description":"Deleted."},"400":{"description":"Invalid path.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such file, or no sandbox with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Sandbox is not running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sandboxes/{id}/extend":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["Sandboxes"],"summary":"Extend a sandbox lifetime","description":"Pushes the expiry further out, up to 1440 minutes after creation.","operationId":"extendSandbox","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"additional_minutes":{"type":"integer","minimum":1,"maximum":1440,"default":15}}}}}},"responses":{"200":{"description":"Updated sandbox.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}}},"400":{"description":"Invalid request parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No sandbox with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Sandbox is not running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/executions/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Execution"],"summary":"Get an execution result","operationId":"getExecution","responses":{"200":{"description":"The execution record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Execution"}}}},"404":{"description":"No execution with that id for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/usage":{"get":{"tags":["Account"],"summary":"Get usage summary","operationId":"getUsage","parameters":[{"name":"days","in":"query","schema":{"type":"integer","default":30,"maximum":365}}],"responses":{"200":{"description":"Usage and spend for the period.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["usage"]},"period_days":{"type":"integer"},"balance_usd":{"type":"number"},"spent_usd":{"type":"number"},"added_usd":{"type":"number"},"sandboxes_created":{"type":"integer"},"runtime_minutes":{"type":"integer"}}}}}}}}},"/account":{"get":{"tags":["Account"],"summary":"Get account details","operationId":"getAccount","responses":{"200":{"description":"Account, balance, limits and pricing.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["account"]},"id":{"type":"string"},"email":{"type":"string"},"balance_usd":{"type":"number"},"limits":{"type":"object","properties":{"concurrent_sandboxes":{"type":"integer"},"max_timeout_minutes":{"type":"integer"}}},"pricing_usd_per_hour":{"type":"object","additionalProperties":{"type":"number"}}}}}}}}}}}}