Skip to content

Environments

List Environments

List the org’s environments (built-in first — it is created first).

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentResponse[]
422Validation ErrorHTTPValidationError

Create Environment

Create an environment.

A built-in sandbox’s name is not the caller’s to choose: there is at most one per org and it is always called “Built-in sandbox”, so name is ignored entirely for BUILTIN_SANDBOX (older clients may still send one). A remote environment, by contrast, requires a name — there can be many.

For a remote environment this also mints the daemon token and returns the install one-liner. That response is the only place the plaintext token ever appears — losing it means creating a new environment.

A custom-image environment needs an image_ref, is capped by the org’s max_custom_environments entitlement, and comes back validating: the pull and the conformance probe run on the agent-runner and the row settles on online or invalid when they finish. Poll GET for it, exactly as the create-modal already polls a remote machine waiting to call home.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
image_refstringno
kindEnvironmentKindyes
namestringno

Responses

StatusDescriptionBody
201Successful ResponseCreateEnvironmentResponse
422Validation ErrorHTTPValidationError

GET /api/orgs/{org_id}/environments/{environment_id}

Section titled “GET /api/orgs/{org_id}/environments/{environment_id}”

Get Environment

Fetch one environment. The create-modal polls this until status is online — i.e. until the machine has called home.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentResponse
422Validation ErrorHTTPValidationError

PATCH /api/orgs/{org_id}/environments/{environment_id}

Section titled “PATCH /api/orgs/{org_id}/environments/{environment_id}”

Update Environment

Edit an environment’s operator-owned settings.

The one that matters here is exec_timeout_s — how long a single command may run. It is a ceiling, not a default: an agent’s bash call asks for the time it needs and is clamped to this, so raising it does not make every trivial command hold a long lease.

The value is clamped into the platform’s bounds rather than rejected — the bounds are ours, not the caller’s mistake — and the response reports the ceiling that will actually be enforced, which for an unset environment is its per-kind default rather than the null that is stored.

image_ref is the other one, and it is the Update button for a custom image: it re-points the environment, drops the pinned digest and sends it back through validation. That is the only way a new push to the same tag ever takes effect — a validated environment follows a digest, never a tag, so nothing changes under a running agent until somebody asks for it. Note that dropping the pin is not merely bookkeeping: it is the signal the validator reads to decide it should resolve the reference again rather than re-probe the bytes POST /validate would have re-probed.

name is the operator’s label for a machine or an image they set up. The built-in sandbox is not one of those: it is the same container pool for every org, named by us and described everywhere in the product by that name, so renaming it is refused rather than quietly ignored.

Admin-only: it decides how long an agent may hold a machine the org owns, and which image runs on our infrastructure.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
exec_timeout_resetbooleanno
exec_timeout_sintegerno
image_refstringno
namestringno

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentResponse
422Validation ErrorHTTPValidationError

DELETE /api/orgs/{org_id}/environments/{environment_id}

Section titled “DELETE /api/orgs/{org_id}/environments/{environment_id}”

Delete Environment

Delete an environment.

Agents and chats pointing at it keep working: the FK is SET NULL, so they fall back to the resolution order rather than breaking.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

GET /api/orgs/{org_id}/environments/{environment_id}/activity

Section titled “GET /api/orgs/{org_id}/environments/{environment_id}/activity”

Get Environment Activity

What has actually run here — the audit sandbox_runs has always kept.

Every one of these rows was already being written on every acquire and teardown; nothing in the product read them back. That is why an org whose agents mysteriously stopped getting a workspace had no way to see that the last four runs failed, and why “is anybody using this environment?” was a question only answerable by deleting it.

A separate request from the environment itself, and deliberately not folded into the list: the settings page polls the list, and a reporting join over the run history has no business running every few seconds against every environment an org owns. This is fetched once, when somebody opens the panel.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
limitqueryintegerno
offsetqueryintegerno
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentActivityResponse
422Validation ErrorHTTPValidationError

POST /api/orgs/{org_id}/environments/{environment_id}/ping

Section titled “POST /api/orgs/{org_id}/environments/{environment_id}/ping”

Ping Environment

Actually talk to the machine, and report what happened.

ping is the one verb every daemon has ever served, and the daemon deliberately does not count it as activity — it never touches the workspace and never renews a pinned workspace’s claim — so this is safe to press against a machine somebody is working on.

It answers a question no stored field can. status and last_seen_at are both history; connected says a socket exists. Only a round trip says the machine on the other end is still answering, which is what an operator who has just changed a firewall rule or woken a laptop is asking.

Failures come back as ok: false with a sentence, never as an error status: the request succeeded, the machine is what did not.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentPingResponse
422Validation ErrorHTTPValidationError

POST /api/orgs/{org_id}/environments/{environment_id}/reveal-token

Section titled “POST /api/orgs/{org_id}/environments/{environment_id}/reveal-token”

Reveal Environment Token

Read a remote machine’s current daemon token back, in the clear.

The View Install Command dialog’s other button: unlike rotate-token, this mints nothing and changes nothing on the row — it only decrypts the copy already stored and hands it back, so an admin who still has a working token installed somewhere can see it again without re-keying (and breaking) every other machine on this environment.

Why this is safe to expose at all: whoever may call it — an org admin, the same gate as rotate-token — could already mint a token that works with no confirmation beyond the click, so reading the existing one back grants no new capability; it is strictly the less disruptive of the two ways to end up holding a working token. See EnvironmentService.reveal_token for the full rationale.

POST rather than GET on purpose — nothing should prefetch or cache a secret. The response also carries Cache-Control: no-store, belt and braces against any intermediary that caches POST responses.

404 for no such environment, 422 for a kind with nothing to reveal (not a remote machine), 409 for a remote machine whose token predates this feature — the row has never had an encrypted copy stored, so there is nothing here to decrypt and rotating once is the only way to get one.

Admin-only, like every other endpoint that hands back a working credential.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseRevealTokenResponse
422Validation ErrorHTTPValidationError

POST /api/orgs/{org_id}/environments/{environment_id}/rotate-token

Section titled “POST /api/orgs/{org_id}/environments/{environment_id}/rotate-token”

Rotate Environment Token

Re-key a remote machine and hand back a fresh install command.

The fresh plaintext token is returned directly here, same response shape as a create — and, like a freshly created one, it can also be read back again later through POST .../reveal-token without another rotation.

Two things it is for: an install command that was lost, and a machine being replaced. Before it, both meant deleting the environment and creating another, which SET NULLs every agent and chat pointing at it and throws away the run history — a destructive operation standing in for a credential rotation.

The currently connected daemon keeps serving. Authentication happens at connect, so an established socket is unaffected and only the next connect needs the new token. Say so in the UI: the natural assumption is the opposite, and an operator who believes this cuts a machine off will never press it.

Admin-only — it mints a credential.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseCreateEnvironmentResponse
422Validation ErrorHTTPValidationError

GET /api/orgs/{org_id}/environments/{environment_id}/usage

Section titled “GET /api/orgs/{org_id}/environments/{environment_id}/usage”

Get Environment Usage

Which agents and chats point at this environment.

The delete dialog’s honest version. It has always promised that agents “fall back to their next configured environment” — true, and useless on its own, because the operator still cannot see which agents are about to start running somewhere else. Naming them is the difference between a warning and a decision.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentUsageResponse
422Validation ErrorHTTPValidationError

POST /api/orgs/{org_id}/environments/{environment_id}/validate

Section titled “POST /api/orgs/{org_id}/environments/{environment_id}/validate”

Revalidate Environment

Re-run validation against the pinned digest — the Re-validate button.

Distinct from PATCH with an image_ref, and the difference is which bytes get probed. This re-checks the exact image the org’s agents are running and never re-resolves the tag, so pressing it cannot move an environment onto a push nobody has approved; Update is the one that re-resolves and re-pins, because it is deliberately pointing at something we have not looked at.

That split is what makes the product’s headline promise true — we pin the digest, so a new push to the same tag changes nothing until somebody asks for it — and neither this endpoint nor the event it pushes carries a mode to say so: a pinned digest on the row is the instruction, and the worker reads it there.

The other reason it exists, and the one it is now actually shaped for: our own sandbox contract moves. An image validated against contract 1 keeps working when contract 2 ships (our release never takes a customer’s agents offline), and re-probing the pinned bytes is exactly how its owner finds out what a rebuild would gain them — a question about this image, which a tag re-resolution would have quietly answered about a different one.

An environment that never pinned anything — it failed before the digest was resolved — has nothing to re-check, so this falls back to pulling the stored reference. That is the only thing it could mean, and it is still useful: it is how a rejected environment gets a fresh verdict.

Parameters

NameInTypeRequiredDescription
environment_idpathstring (uuid)yes
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseEnvironmentResponse
422Validation ErrorHTTPValidationError

GET /api/orgs/{org_id}/environments/custom-images/registries

Section titled “GET /api/orgs/{org_id}/environments/custom-images/registries”

List Allowed Registries

The registry hosts we will pull a custom image from.

Served rather than hard-coded in the client for the usual reason: the allowlist is a security decision that changes on our side, and a create form listing hosts we no longer accept is a form that produces confident 422s.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful Responsestring[]
422Validation ErrorHTTPValidationError