Skip to main content
Brimble ships first-party SDKs for the sandbox API in three runtimes. They wrap the REST endpoints in idiomatic clients with built-in retries, resume/reconnect polling (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

Runtime requirements: Node 20+, Python 3.10+, Go 1.22+. Go API docs are hosted at pkg.go.dev/github.com/brimblehq/brimble-sdks/sandbox-go.

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.
Other constructor options (all optional):
  • baseUrl / base_url / BaseURL sets the API root. Defaults to https://sandbox.brimble.io.
  • timeoutMs / timeout_ms / Timeout sets the per-request HTTP timeout. Defaults to 30 seconds.
  • retry / retry / Retry lets you override the retry policy (see Retries and idempotency).
  • TypeScript-only: fetchImpl supplies a custom fetch for tests.
  • Python-only: session supplies a requests.Session for connection pooling.
  • Go-only: HTTPClient supplies a custom *http.Client.
The client errors out immediately if neither the constructor arg nor the env var is set.

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.
The presets bake in a sensible default for getting started: persistent storage so your files survive a restart, and a 20 GB disk so you have headroom. For finer control, use 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.
The matching 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.
The volume’s 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:
The handle exposes the sandbox’s identity, last-known state, and the full set of runtime methods:

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.
Without the flag, runtime methods return an error if the sandbox isn’t 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.
Specs ranges: 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

Both are async; the response acknowledges the request and the sandbox transitions a few seconds later. The handle auto-refreshes its cached state, so handle.status reflects the new value immediately.

Update egress

Change outbound network policy on a running sandbox. Returns the updated sandbox record, including network_updated when the underlying network profile changed.
See Network egress for mode semantics and create-time configuration.

Runtime operations

Once the sandbox is ready (or you’ve passed waitUntilReady):

Exec

By default 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

Both exec 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.
Values must be strings; numbers and booleans need to be stringified first. The map is scoped to the one call, sandbox-wide environment changes are not supported.

Stream exec output

Both exec 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 as ExecStreamFrame 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": "..." }
TypeScript exports ExecStream, parseSseFrames, and consumeExecStream. Python exports ExecStream, parse_sse_frames, and consume_exec_stream for lower-level use.

Upload and download files

Parent directories must exist; uploads to a non-existent directory fail with a 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.
Per-call cap is 100 files, and the payload counts toward the same file-size limit as single uploads, the SDKs reject more than 100 files client-side. For larger transfers (single big files, or more than 100 files), loop putFile instead.

Stats

Returns averages and a time-series of CPU%, memory%, and network bytes/sec.

Snapshots

Per-sandbox snapshot ops live on the handle. Account-wide ops live on client.snapshots.
Snapshot names match ^[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

Pass fromSnapshot 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.
Attach a volume to a sandbox by passing 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.
Legacy: 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.
The Brimble team adds and retires templates without warning; calling listTemplates() is the authoritative way to see what’s currently available.

Pagination

All list 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 single catch / 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.
A few SDK-specific sentinels:
  • Python: wait_until_ready() raises TimeoutError on deadline. Sandbox(api_key=...) raises ValueError if no key is found. volumes.create() raises ValueError for size or type violations before the HTTP call.
  • TypeScript: the constructor throws a plain Error if no key is found.
  • Go: context.DeadlineExceeded / context.Canceled for caller-driven cancellation; check with errors.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 on 408, 429, 500, 502, 503, 504. Bump maxAttempts to opt in.
Idempotency keys make lifecycle calls safe to retry. The server deduplicates retries that carry the same 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 set autoDestroy: 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.
Last modified on July 1, 2026