getReady, waitUntilReady), error hierarchies, and convenience helpers, so you don’t have to hand-roll any of it.
The three SDKs are deliberately near-identical in surface: every method on one has a same-named counterpart on the others, every input shape matches, every default lines up. Pick the runtime that fits your stack and the rest of the page applies.
Install
Authenticate
All three SDKs read the same environment variable,BRIMBLE_SANDBOX_KEY, set it to your account-level API key from the profile drawer (avatar → API key) in the dashboard. You can also pass the key explicitly to the client constructor. See API keys for how to generate, rotate, and rate-limit.
baseUrl/base_url/BaseURLsets the API root. Defaults tohttps://sandbox.brimble.io.timeoutMs/timeout_ms/Timeoutsets the per-request HTTP timeout. Defaults to 30 seconds.retry/retry/Retrylets you override the retry policy (see Retries and idempotency).- TypeScript-only:
fetchImplsupplies a customfetchfor tests. - Python-only:
sessionsupplies arequests.Sessionfor connection pooling. - Go-only:
HTTPClientsupplies a custom*http.Client.
The client surface
Every client exposes three resource groups:
Go also exposes
client.Ping(ctx), a one-shot connectivity and auth check.
Quickstart presets
The shortest path to a running sandbox is a quickstart helper. They preconfigure the template, persistent disk, and ready-wait so you can get to work in a single call.create directly (next section).
Create
create provisions a sandbox and blocks until it is ready (~2–3s typical). The HTTP request does not return until the container is up — set a client timeout of at least 90 seconds.
region is optional — omit it (or pass "auto") to let the server pick. Pass a specific region ID from listRegions() to pin one.
getReady(id) fetches an existing sandbox and waits until it is ready — use after resume or when reconnecting to a sandbox whose state you do not know.
waitUntilReady() on a handle is for edge cases (resume, long-poll fallback). You do not need it after a fresh create():
Create a sandbox alongside a fresh volume
withVolume provisions both in a single call. Use it when you want a per-sandbox persistent workspace and don’t already have a volume.
region and the sandbox’s region must resolve to the same region. Passing "auto" on both is the simplest path.
The sandbox handle
create(), get(), getReady(), and withVolume() all return a handle, an object that bundles the sandbox ID with runtime helpers. Most of what you do with a sandbox happens through the handle, not the client.
You can also grab a handle for any sandbox by ID:
Auto-wait on runtime ops
Every runtime method (exec, runCode, putFile, getFile, stats, createSnapshot, listSnapshots) accepts an optional waitUntilReady flag. When set, the SDK waits until the sandbox is ready before sending the actual call — useful after resume or when you fetched a handle with get() and aren’t sure of its state.
ready. After create(), the handle is already ready — call exec directly. Use waitUntilReady when operating on a handle you fetched with get() and the sandbox may still be resuming.
Sandboxes resource (full surface)
Create
region is required (use "auto" to let the server pick); everything else is optional.
cpu 1 to 2000 (MHz units), memory 1 to 2048 MB, disk 1 to 5 GB ephemeral.
List, get, destroy
destroy is idempotent: calling it on an already-destroyed sandbox is a no-op.
Iterate across pages
For walking every sandbox without hand-rolling pagination:Pause and resume
handle.status reflects the new value immediately.
Update egress
Change outbound network policy on a running sandbox. Returns the updated sandbox record, includingnetwork_updated when the underlying network profile changed.
Runtime operations
Once the sandbox isready (or you’ve passed waitUntilReady):
Exec
exec buffers, you get stdout, stderr, exit_code, and duration_ms once the command finishes. For long-running commands where you want output as it arrives, see Stream exec output below.
Run code
Per-call environment variables
Bothexec and runCode accept an env object that’s layered on top of the sandbox’s existing environment for a single call. Same-named keys override the sandbox-level value for that invocation only; the next call starts from the sandbox defaults again. Cleaner than shelling out export FOO=bar && in front of every command, and stops secrets from showing up in argv / shell history.
Stream exec output
Bothexec and runCode support three output modes:
When streaming is active, the server responds with
Content-Type: text/event-stream. Each event is a JSON object on a data: line; comment lines (: open, : ping) are keepalives and can be ignored. The stream always ends with a done event (or error on transport failure).
In Go, pass live output through
ExecStream / RunCodeStream, not Stream: true on the input struct. Setting stream: true on ExecInput returns an error directing you to the stream methods.Live stream (ExecStream)
Callback streaming (buffered result, live chunks)
Stream runCode
Same patterns on the language-aware variant:
SSE frame shape
Frame payloads (typed asExecStreamFrame in TS, dict in Python, parsed in Go):
{ "type": "stdout", "data": "..." }{ "type": "stderr", "data": "..." }{ "type": "done", "exit_code": 0, "duration_ms": 142 }{ "type": "error", "message": "..." }
ExecStream, parseSseFrames, and consumeExecStream. Python exports ExecStream, parse_sse_frames, and consume_exec_stream for lower-level use.
Upload and download files
400.
Batch file uploads
For seeding many small files in one round trip (a fresh repo checkout, a set of config files, a directory of fixtures), use the batch upload. It sends the files as base64-encoded JSON in a single POST and returns a per-file success/failure summary.putFile instead.
Stats
Snapshots
Per-sandbox snapshot ops live on the handle. Account-wide ops live onclient.snapshots.
^[a-z0-9-]{1,40}$. Creation is async: the response returns status: "creating" and the snapshot flips to ready (or failed) a few minutes later. Poll list to see the transition.
Restore from a snapshot
PassfromSnapshot at sandbox create time to seed the new sandbox with the snapshot’s filesystem:
Volumes
The SDKs only create sandbox-type volumes (type: "sandbox"); the web type is reserved for the dashboard’s persistent-disk toggle on a project. All three SDKs enforce this client-side and reject any other value before the HTTP call.
volumeId on create. See the Volumes doc for the full attach model.
Network egress
Control outbound network access when creating or updating a sandbox. Three modes:
At create time, pass
egress: { mode, allow? }. allow is an array of IPv4 addresses, CIDR ranges, or hostnames (up to 50 entries). Required when updating to restricted; optional at create (defaults to an empty list).
At runtime, call updateEgress on the handle or sandboxes resource. The response includes network_updated: true when the sandbox’s network profile was switched; allow a few seconds before testing connectivity from inside the sandbox.
blockOutbound: true at create maps to deny_all. Do not combine egress and blockOutbound on the same request.
See Sandboxes overview → Network egress for the product-level model and Update sandbox egress for the REST contract.
Discovery
Both templates and regions are first-class on the client. No need to hit a separate API.listTemplates() is the authoritative way to see what’s currently available.
Pagination
Alllist endpoints accept { page, limit } (plus teamId on team-scoped calls). Defaults are page = 1, limit = 15, max limit = 100. Responses include totalCount, currentPage, totalPages, limit, and a data array.
For walking every result, prefer the iterate / iterate_all / Iterate helpers shown above; they handle pagination internally.
Errors
Every SDK exposes a typed error hierarchy. All subclasses extend the base type so a singlecatch / except / errors.As block can still handle “anything from the API,” but you can narrow when you want to.
Every instance carries
status, message, endpoint, responseBody, and requestId.
- Python:
wait_until_ready()raisesTimeoutErroron deadline.Sandbox(api_key=...)raisesValueErrorif no key is found.volumes.create()raisesValueErrorfor size or type violations before the HTTP call. - TypeScript: the constructor throws a plain
Errorif no key is found. - Go:
context.DeadlineExceeded/context.Canceledfor caller-driven cancellation; check witherrors.Is.
Retries and idempotency
Each SDK ships a built-in retry policy you can configure at the client or per call. Defaults are conservative: one attempt (no retries) by default, base delay 300 ms, max delay 3 s, retry on408, 429, 500, 502, 503, 504. Bump maxAttempts to opt in.
idempotencyKey within a short window: create, destroy, pause, resume, createSnapshot, deleteSnapshot, and volume create / delete all accept the option. Pass a stable, unique-per-operation value (a UUID, a job ID, the SHA of the request body) and you can retry network failures without spinning up a duplicate sandbox.
Timeouts and cancellation
Best practice in Go: pass a
context.Context with a deadline matched to the operation. The SDK respects ctx.Done() everywhere, including the wait-until-ready polling loop.
Cleanup
None of the SDKs auto-destroy sandboxes when your process exits. Either setautoDestroy: true with a destroyTimeout, use oneShot: true so the sandbox terminates when its main process exits, or wrap your work in a cleanup block:
Versioning and stability
All three SDKs are at 0.x today; the public surface is shaped to stay stable, but minor bumps may break compatibility while we shake things out. Pin a specific version in production until we tag 1.0:Next steps
- Sandboxes overview, the lifecycle and billing model.
- Quickstart, the five-minute end-to-end walkthrough with all three SDKs side by side.
- Cookbook, recipes for the highest-traffic use cases.
- Snapshots, deep-dive on the snapshot lifecycle.
- Volumes, persistent volumes that survive sandbox destruction.
- Sandbox API tab, the REST contract the SDKs wrap.