Skip to content

API reference

Sandboxes

10 routes of the published API contract, generated from it.

GET/v1/sandboxes

Auth: API key

List your org's sandboxes, newest first. ?labels=k=v,k2=v2 returns only those whose run carries every label given, and ?state=running,ready narrows by state: queued is every sandbox still waiting to start, awaiting_capacity only those a run answer reports as waiting for capacity. Both filters run on the server, so they find a match older than one page — which is how two copies of your service agree on one sandbox instead of each starting its own. Pages are ?limit=N (1..=500, default 50) and ?cursor=; echo back next_cursor to get the next page, and stop when it is null. A short page is not the end: a filter can empty a page that still has more behind it.

Parameters

cursorquery · string
labelsquery · string
limitquery · integer
statequery · string

Answer 200 · SandboxList · application/json

labelsrequiredmap of string
The label filter this list was produced with; empty when none was given.
limitrequiredinteger
The page size that was applied.
next_cursorstring | null
Pass back as ?cursor= for the next page; null on the last page. A short page is not the end: a filter can empty a page that still has more behind it.
sandboxesrequiredSandboxView[]
staterequiredstring[]
The state filter this list was produced with; empty when none was given. queued keeps every waiting sandbox; awaiting_capacity only those waiting for capacity being added.

GET/v1/sandboxes/{id}

Auth: API key

One sandbox, with its execs. auto_pause_s and auto_pause_mode report the idle rule it is under — the values PATCH sets; auto_pause_s is 0 when it never pauses itself on idle. optimize_for is what the sandbox asked for; optimize_for_applied is what its latest start or resume actually got (null before the first). They can differ — the next resume asks again.

Parameters

idrequiredpath · string

Answer 200 · SandboxView · application/json

attemptrequiredinteger
1 for the first try, 2 for the first retry, and so on.
auto_pause_moderequiredstring
What idle means for the rule: requests | activity.
auto_pause_srequiredinteger
The idle rule it is under: park after this many idle seconds, 0 for never.
billable_secondsinteger | null
Once it has ended, the seconds it is billed for (the run report's billable_seconds). Present on GET /v1/sandboxes/{id} for an ended sandbox; absent from list rows.
cost_usdnumber | null
What it has cost in USD at its run's rates: once it has ended, the run report's cost; while it is live, the seconds billed so far (running time only). Present on GET /v1/sandboxes/{id} once it has started; absent from list rows.
created_atrequiredstring
When the sandbox was created, RFC 3339: when its run was started, or when the retry or replay that made it was asked for.
e2e_msnumber | null
Milliseconds from the start request, after any time queued, to the sandbox being handed over: it does not include queued_ms. Add queued_ms for the whole wait.
egressrequiredEgressCounters
Data out, and whether it was cut for the organisation's monthly cap.
elapsed_msinteger | null
Milliseconds of running time since hand-over, time spent paused excluded: what timeout_ms is measured against, so timeout_ms - elapsed_ms is the time it has left. null before hand-over and once it has ended.
env_verifiedrequiredboolean
Whether this sandbox's environment passed its checks.
eta_sinteger | null
Seconds until this sandbox is expected to start, with queued_reason: capacity_arriving. null with the other reasons, and once the sandbox has waited longer than the estimate it was first given; queued_message then says it is taking longer than expected rather than repeating a number.
execsExecView[] | null
The commands run in this sandbox. Present on GET /v1/sandboxes/{id}; absent from the rows of a list, which would otherwise carry every byte of every command's output.
failureobject
What went wrong, rendered against this sandbox's own evidence. null on success.
labelsrequiredmap of string
Inherited from the run. A caller that looked a sandbox up by label needs to see the labels it got back, and one that did not needs a way to learn what a sandbox is tagged with.
one_timeboolean | null
True when this is a one-time wait: once the waiting sandboxes can start, the rest of the run starts at once too. False otherwise.
optimize_forrequiredOptimizeFor
What the sandbox asked for.
optimize_for_appliedOptimizeFor | null
What its latest start or resume actually got; null before the first.
outcomestring | null
Why it ended; null while it is still alive.
pause_expires_atstring | null
While it is paused: when it will be deleted, with its files and state, unless it is resumed before then (RFC 3339). A paused sandbox is kept for 30 days, and resuming it starts the 30 days again. Absent while it is not paused, and for a paused sandbox that is not due to be deleted.
pause_reasonstring | null
Why it is paused, when a limit paused it rather than a request: spend_cap (its run reached its spend cap) or credits_exhausted (the organization's credits ran out). Its files and state are kept and paused time is not billed; it resumes once the cap is raised or credits are added. Absent otherwise.
queued_messagestring | null
The same, in words, ready to show a person.
queued_msnumber | null
Milliseconds spent waiting for capacity.
queued_reasonQueuedReason | null
ready_msnumber | null
Milliseconds until the sandbox was ready to take work.
replay_ofstring | null
The sandbox this one is a replay of.
retried_bystring | null
The retry that replaced this sandbox, if it was retried.
retry_ofstring | null
The sandbox this one is a retry of.
run_idrequiredstring
The run this sandbox belongs to.
sandbox_idrequiredstring
snapshot_idrequiredstring
The snapshot it was started from, as its run names it (GET /v1/runs/{id}): the snapshot's id, or promigence/base for the built-in base.
staterequiredstring
queued | ready | running | paused | ended.
t_endstring | null
When it ended, RFC 3339.
t_handoffstring | null
When the sandbox was handed over, RFC 3339.
timeout_msinteger | null
The lifetime this sandbox is bound to, in milliseconds of running time: time spent paused does not count. A PATCH with a larger timeout_ms extends it.
verify_each_msnumber | null
Milliseconds the per-sandbox verification took, when verify_each was asked for.
warningsrequiredstring[]

PATCH/v1/sandboxes/{id}

Auth: API key

Extend a live sandbox's total lifetime, and set when it parks itself. timeout_ms sets the total, not an offset from now; 409 cap_exceeded if the extra seconds do not fit the run's cap. auto_pause_s parks the sandbox once it has been idle that long — 0 turns it off, the minimum is 2, and a sandbox with a command, a background command, a terminal or an SSH session still open is never idle, so a long build or a dev server is safe. auto_pause_mode says what idle means: requests (the default) counts calls to the sandbox; activity also waits until, for the whole window, nothing inside has used CPU or sent or received a byte, and no network connection to the outside is open — for an agent that runs inside the sandbox on its own. Start such an agent in the background of a command that returns: it keeps running when the sandbox wakes. An agent still running as an open command keeps the sandbox awake in either mode, because a pause would end it. A sandbox parked by this rule wakes by itself on its next command or file call, and parked time is not billed. Any of the fields may be sent on its own.

Parameters

idrequiredpath · string

Request body SandboxPatch · application/json · optional

auto_pause_modeAutoPauseMode | null
auto_pause_sinteger | null
Pause after this many idle seconds; 0 turns the rule off.
timeout_msinteger | null
The new lifetime in milliseconds, counted from now.

Answer 200 · SandboxPatched · application/json

DELETE/v1/sandboxes/{id}

Auth: API key

Kill it now. The answer's outcome says how it ended: aborted_by_client for a sandbox that had started, billed for the seconds it used; not_started for one still queued, which is not billed; or, when its command had already returned, that command's own outcome. A sandbox that had already ended answers with the outcome it ended with and already_ended: true.

Parameters

idrequiredpath · string

Answer 200 · SandboxKilled · application/json

already_endedrequiredboolean
True when the sandbox had already ended and this call changed nothing.
okrequiredboolean
outcomestring | null
sandbox_idrequiredstring
staterequiredstring
"ended".

GET/v1/sandboxes/{id}/network

Auth: API key

Read the network policy a live sandbox is running under.

Parameters

idrequiredpath · string

Answer 200 · SandboxNetwork · application/json

networkrequiredobject
"egress", "none", or {"allow": [...]} — the policy in force: as it was written, or "none" once the sandbox's egress has been cut.
refused_connectionsobject
Connections refused, counted, including those to a bare address (which have no name to list and whose addresses are not recorded). Only for a sandbox that is still running.
refused_hostsrequiredobject
The hosts this sandbox was refused, by name. Current while it is running, and as recorded once it is not. A connection to a bare address has no name: it is counted in refused_connections, not listed here.
refused_lookupsobject
Name lookups refused. Only for a sandbox that is still running.
sandbox_idrequiredstring
staterequiredstring
via_secretViaSecret[]
The hosts this sandbox may reach for its host-bound secrets (secrets set --host), each secret by name with its hosts, whatever network says. Absent when it has none.

POST/v1/sandboxes/{id}/network

Auth: API key

Cut a live sandbox's egress. Use it to fetch dependencies first and close the network before the agent runs. One direction only: there is no call that opens egress again, so a sandbox can never widen its own access.

Parameters

idrequiredpath · string

Request body SandboxNetworkSet · application/json

networkrequiredstring
none, the only value.

Answer 200 · SandboxNetworkCut · application/json

appliedrequiredboolean
False if the sandbox was already running with no egress.
networkrequiredNetworkMode
A network policy named by its mode alone.
sandbox_idrequiredstring
staterequiredstring

POST/v1/sandboxes/{id}/pause

Auth: API key

Park the sandbox: it does not count against your concurrency limit, and paused time is not billed. Background commands started with background: true are ended by the pause, and the answer says how many (background_execs_killed, only when there were any); start them again after resume. A sandbox that was given the value of a secret pauses as usual, and the answer carries a secrets_in_saved_state warning.

Parameters

idrequiredpath · string

Answer 200 · SandboxPaused · application/json

already_pausedboolean | null
Present, and true, only when the sandbox was already paused and this call changed nothing. The fields below it are then absent.
background_execs_killedinteger | null
Background commands the pause ended, when there were any. A pause does not keep a running background command: start it again after resume.
bytesParkedBytes | null
What the parked sandbox occupies on disk while it is not running.
durableboolean | null
True when the parked state is kept safe from a failure on our side. false is not an error.
okrequiredboolean
paused_idstring | null
An opaque name for this pause. Resume by the sandbox id; this one is for your records.
recordedboolean | null
False if the sandbox was parked but the change could not be written down, which is worth retrying.
sandbox_idrequiredstring
staterequiredstring
"paused".
took_msinteger | null
Milliseconds the pause took, from the request to the answer.
warningsstring[] | null
Things to know about what was parked, one code: sentence per line; only when there is one. Today the only one is secrets_in_saved_state.

POST/v1/sandboxes/{id}/probe

Auth: API key

Re-check the sandbox's files against what the verified snapshot recorded. first_bad_path is the first path that differs.

Parameters

idrequiredpath · string

Request body SandboxProbeRequest · application/json · optional

pathsstring[]
Paths to check; omitted, the whole workspace.

Answer 200 · SandboxProbe · application/json

checkedrequiredobject
What was checked: the list you asked for, or the string "verified_paths" for the snapshot's own recorded set.
first_bad_pathstring | null
The first path that differs, and the only part of a failed check anyone can act on.
msrequirednumber
How long the check took, in milliseconds.
okrequiredboolean
True when every path checked still matches what was verified.
sandbox_idrequiredstring

POST/v1/sandboxes/{id}/resume

Auth: API key

Bring a paused sandbox back where it was, and restart the billing clock.

Parameters

idrequiredpath · string

Request body SandboxResume · application/json · optional

optimize_forOptimizeFor | null
Omitted: the sandbox's own setting.
spend_capnumber | null
For a sandbox paused at its run's spend cap (pause_reason: spend_cap): the run's new cap in US dollars, above the one it reached. The sandbox goes on with what the new cap leaves, up to its own timeout.

Answer 200 · SandboxResumed · application/json

notestring | null
What restarted means for this sandbox, in one sentence. Absent for a normal resume.
okrequiredboolean
paused_idrequiredstring
The pause that was restored: the paused_id its pause answered with.
recordedrequiredboolean
False if the sandbox came back but the change could not be written down.
restartedboolean
True when the sandbox came back from its saved files only: its files are as they were when it paused, but the programs that were running then were not kept and start fresh. Absent (false) for a normal resume, where everything comes back as it was.
sandbox_idrequiredstring
staterequiredstring
"ready".
took_msrequiredinteger
Milliseconds the resume took, from the request to the answer.

POST/v1/sandboxes/{id}/snapshot

Auth: API key

Snapshot a running sandbox without stopping it, and get back an ordinary snapshot id you can fork. Background processes are ended first and the count is in the reply. The new snapshot inherits the tier and source of the snapshot this sandbox came from. It is not marked verified: the sandbox has run your code since, so verification has to be earned again rather than inherited. A sandbox that was given the value of a secret is refused with secrets_in_snapshot (409), because the snapshot, and every sandbox started from it, could contain that value; force: true takes it anyway and the answer carries a secrets_in_saved_state warning. An alias another snapshot already has is refused unless move_alias: true moves it here (the other snapshot stays, unnamed).

Parameters

idrequiredpath · string

Request body SandboxSnapshotCreate · application/json · optional

aliasstring | null
A name for the new snapshot, unique in the project.
forceboolean
Take it even though the sandbox was given a secret's value.
move_aliasboolean
If another snapshot has alias, move the name to the new one (the other stays, unnamed). Without it such an alias is refused.

Answer 200 · SandboxSnapshot · application/json

aliasstring | null
background_execs_killedrequiredinteger
Background processes ended to take the snapshot. Reported rather than silent: a live snapshot must not quietly cost a customer their dev server.
bytesrequiredSnapshotBytes
What the image occupies on disk.
okrequiredboolean
sandbox_idrequiredstring
The sandbox that was photographed. Still yours, still running.
snapshot_idrequiredstring
An ordinary snapshot id, forkable by every path that forks anything else. There is deliberately no second kind of snapshot: a parallel identifier with its own rules is how two code paths drift apart.
still_runningrequiredboolean
Always true on a 200. See the type's note.
tierrequiredstring
took_msrequiredinteger
Milliseconds the snapshot took, from the request to the answer.
warningsstring[]
Things the caller should know about what was taken, one code: sentence per line. Absent when there are none. Today the only one is secrets_in_saved_state: the sandbox had been given the value of a secret and the snapshot was taken with force, so the snapshot, and every sandbox started from it, can contain that value.

Get started

Leave your work email and we will get you started.

See our Privacy Policy for how we use your email.