Sandbox API

Sandboxes

Long-lived workspace / desktop sessions

Long-lived workspace / desktop sessions

Every request needs a credential — see Authentication.

MethodPathSummary
GET/sandbox/sandboxes/v1List Sandboxes
POST/sandbox/sandboxes/v1Create Sandbox
POST/sandbox/sandboxes/v1/_reapReap Sandboxes
DELETE/sandbox/sandboxes/v1/{sandbox_id}Destroy Sandbox
GET/sandbox/sandboxes/v1/{sandbox_id}Get Sandbox
GET/sandbox/sandboxes/v1/{sandbox_id}/artifactsList Sandbox Artifacts
GET/sandbox/sandboxes/v1/{sandbox_id}/artifacts/{capture_id}Get Sandbox Artifact
POST/sandbox/sandboxes/v1/{sandbox_id}/captureCapture Sandbox
GET/sandbox/sandboxes/v1/{sandbox_id}/eventsSSE sandbox lifecycle events
POST/sandbox/sandboxes/v1/{sandbox_id}/execExec Sandbox
DELETE/sandbox/sandboxes/v1/{sandbox_id}/filesDelete File
GET/sandbox/sandboxes/v1/{sandbox_id}/filesGet File
PUT/sandbox/sandboxes/v1/{sandbox_id}/filesPut File
POST/sandbox/sandboxes/v1/{sandbox_id}/startStart Sandbox
POST/sandbox/sandboxes/v1/{sandbox_id}/stopStop Sandbox
POST/sandbox/sandboxes/v1/{sandbox_id}/verifyVerify Sandbox
GET/sandbox/sandboxes/v1/{sandbox_id}/viewerMint viewer URL or proxy the desktop viewer UI
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/actionsUpload the viewer action log (clicks, scrolls, keys, searches)
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/annotationsSubmit a task-ambiguity label for the session
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/recordingsUpload a viewer session screen recording
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/screenshotsUpload a screenshot captured from the viewer canvas
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/startStart Viewer
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/stopStop Viewer
POST/sandbox/sandboxes/v1/{sandbox_id}/viewer/tokenRemint a fresh signed viewer URL (token refresh)
POST/sandbox/sandboxes/v1/{sandbox_id}/workspace/syncSync tenant GCS workspace ↔ guest SandboxWorkspace
POST/sandbox/sandboxes/v1/images/buildBuild-or-resolve a content-addressed desktop image

Common headers

Accepted on every endpoint on this page.

NameInTypeRequiredDescription
x-correlation-idheaderstringnoEnd-to-end correlation id for this eval run. When set, the gateway and downstream services honor it verbatim in logs, Cloud Trace span attributes, async job payloads, and Langfuse trace.id. When omitted, the gateway generates a ULID and echoes it on the response.
X-Sandbox-Project-Idheaderstring | nullnoProject to bill and rate-limit this request against. Validated against the credential: a project-scoped token may only name its own project, and gets 403 otherwise. Defaults to the token's project when omitted.
X-Sandbox-Tenant-Idheaderstring | nullnoTenant for this request. Must match the credential's tenant when the credential carries one.

Endpoints

List Sandboxes

GET /sandbox/sandboxes/v1

Parameters

NameInTypeRequiredDescription
task_idquerystring | nullno
reuse_fingerprintquerystring | nullnoFilter by create-intent fingerprint (sha256:…).
statusquerystring | nullno

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Create Sandbox

POST /sandbox/sandboxes/v1

Request body

application/json, required — CreateSandboxRequest

FieldTypeRequiredDescription
profilestringnoDefault: "sandboxes-default".
sourceobject | nullno
image_refstring | nullnoImmutable pin: OCI repo@sha256:<digest> / content-hash tag under SANDBOX_HARBOR_TASK_REPO (desktop-osworld / images/build). Floating tags rejected.
autostop_secnumber | nullno
ttl_secnumber | nullno
linked_job_idstring | nullno
task_idstring | nullnoOptional Gen Studio / CTP task id for labels and episode metadata; when omitted, sandbox_id is used.
reusebooleannoWhen true, return an existing non-destroyed sandbox whose create fingerprint matches (owner salt + tenant/project + profile + image_ref + workspace_key + purpose/template labels) instead of provisioning a new session. Default false so pass@k / episode reset stay create-always. Default: false.
reuse_saltstring | nullnoOptional client nonce mixed into the fingerprint salt. Rotate to force a fresh VM while keeping the same task_id. Required for fingerprinting when the caller has no Keycloak owner principal.
labelsobjectnoConsumer tags for analytics (trainer, pass, program, …).

Responses

StatusDescriptionBody
201Successful Responseany
422Validation ErrorHTTPValidationError

Reap Sandboxes

POST /sandbox/sandboxes/v1/_reap

Operator hook: stop/destroy idle sandboxes for this scope.

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Destroy Sandbox

DELETE /sandbox/sandboxes/v1/{sandbox_id}

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

Get Sandbox

GET /sandbox/sandboxes/v1/{sandbox_id}

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

List Sandbox Artifacts

GET /sandbox/sandboxes/v1/{sandbox_id}/artifacts

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
kindquerystring | nullno
limitqueryintegerno

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Get Sandbox Artifact

GET /sandbox/sandboxes/v1/{sandbox_id}/artifacts/{capture_id}

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
capture_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Capture Sandbox

POST /sandbox/sandboxes/v1/{sandbox_id}/capture

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

SSE sandbox lifecycle events

GET /sandbox/sandboxes/v1/{sandbox_id}/events

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
Last-Event-IDheaderstring | nullno

Responses

StatusDescriptionBody
200Successful Responseany
422Validation ErrorHTTPValidationError

Exec Sandbox

POST /sandbox/sandboxes/v1/{sandbox_id}/exec

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Request body

application/json, required — ExecRequest

FieldTypeRequiredDescription
commandstring[]yes
cwdstring | nullno
timeout_secnumber | nullno
envobject | nullno

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Delete File

DELETE /sandbox/sandboxes/v1/{sandbox_id}/files

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
pathquerystringyes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

Get File

GET /sandbox/sandboxes/v1/{sandbox_id}/files

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
pathquerystringyes

Responses

StatusDescriptionBody
200Successful Responseany
422Validation ErrorHTTPValidationError

Put File

PUT /sandbox/sandboxes/v1/{sandbox_id}/files

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
pathquerystringyes

Request body

application/json, required

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Start Sandbox

POST /sandbox/sandboxes/v1/{sandbox_id}/start

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Stop Sandbox

POST /sandbox/sandboxes/v1/{sandbox_id}/stop

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Verify Sandbox

POST /sandbox/sandboxes/v1/{sandbox_id}/verify

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Request body

application/json, required — VerifyRequest

FieldTypeRequiredDescription
require_verifier_evidencebooleannoDefault: false.

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Mint viewer URL or proxy the desktop viewer UI

GET /sandbox/sandboxes/v1/{sandbox_id}/viewer

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseany
422Validation ErrorHTTPValidationError

Upload the viewer action log (clicks, scrolls, keys, searches)

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/actions

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Request body

application/json, required — ActionLogRequest

FieldTypeRequiredDescription
eventsActionEvent[]yes
recording_idstring | nullnoRecording this log is time-aligned with.

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Submit a task-ambiguity label for the session

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/annotations

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Request body

application/json, required — AmbiguityAnnotation

FieldTypeRequiredDescription
clarity"clear" | "ambiguous"yes
ambiguity_types"intent" | "referential" | "parametric" | "cross_modal"[]noRequired when clarity is 'ambiguous'.
severity"low" | "medium" | "high" | "blocking" | nullno
risk"low" | "medium" | "high" | nullno
feasibility"feasible" | "partially_feasible" | "infeasible" | "unknown" | nullno
alternate_answersstring[]noOther defensible answers a solver could have given.
root_of_confusionstring | nullnoWhat in the task made it ambiguous.
task_promptstring | nullnoInstruction that was annotated.
notesstring | nullno
annotatorstring | nullno
recording_idstring | nullno
actions_idstring | nullno

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Upload a viewer session screen recording

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/recordings

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes
duration_secquerynumber | nullno

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Upload a screenshot captured from the viewer canvas

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/screenshots

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Start Viewer

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/start

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful ResponseViewerDescriptor
422Validation ErrorHTTPValidationError

Stop Viewer

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/stop

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Remint a fresh signed viewer URL (token refresh)

POST /sandbox/sandboxes/v1/{sandbox_id}/viewer/token

Issue a new viewer descriptor when the previous signed token is near expiry.

Requires API key / Keycloak / team token — not an expired viewer cookie. Gen Studio should call this and replace the iframe src with the new url.

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Responses

StatusDescriptionBody
200Successful ResponseViewerDescriptor
422Validation ErrorHTTPValidationError

Sync tenant GCS workspace ↔ guest SandboxWorkspace

POST /sandbox/sandboxes/v1/{sandbox_id}/workspace/sync

Parameters

NameInTypeRequiredDescription
sandbox_idpathstringyes

Request body

application/json, required — WorkspaceSyncRequest

FieldTypeRequiredDescription
direction"pull" | "push"yespull = GCS→guest SandboxWorkspace; push = guest→GCS snapshot.

Responses

StatusDescriptionBody
200Successful Responseobject
422Validation ErrorHTTPValidationError

Build-or-resolve a content-addressed desktop image

POST /sandbox/sandboxes/v1/images/build

Publish a curated desktop image via the platform BuildKit pool.

Pass catalog desktop_app ids in apps or a desktop_app_bundle id in bundle (see GET /sandbox/v1/catalog?kind=desktop_app and kind=desktop_app_bundle). The service generates the Dockerfile from platform-owned recipes — callers cannot supply arbitrary build instructions.

The returned image_uri is an immutable content-hash tag under SANDBOX_HARBOR_TASK_REPO. Pass it as image_ref on POST /v1 (expert session or trainer boot from a CTP task seed).

MVP note: this handler is synchronous; cold BuildKit builds can take minutes. The gateway routes /images/build onto SANDBOX_LONG_PROXY_TIMEOUT_SEC so the ordinary 120s proxy budget does not cut cold builds. Prefer content-addressed cache hits (cached=true); async 202 + poll is follow-up work.

Request body

application/json, required — BuildDesktopImageRequest

FieldTypeRequiredDescription
profilestringnoDefault: "desktop-ubuntu".
appsstring[] | nullnoCatalog desktop_app ids (e.g. chrome). Mutually exclusive with bundle.
bundlestring | nullnoCatalog desktop_app_bundle id (e.g. osworld). Mutually exclusive with apps.
datapoint_idstring | nullnoOptional builder tag prefix; defaults to sandboxes-{profile}.

Responses

StatusDescriptionBody
200Successful ResponseBuildDesktopImageResponse
422Validation ErrorHTTPValidationError

Schemas

The object shapes referenced above.

ActionEvent

One operator interaction inside the viewer.

FieldTypeRequiredDescription
atnumberyesMilliseconds since the recording started.
typestringyesclick, scroll, key, search, screenshot…
xinteger | nullno
yinteger | nullno
delta_xnumber | nullno
delta_ynumber | nullno
keystring | nullno
textstring | nullno
detailobject | nullno

ActionLogRequest

FieldTypeRequiredDescription
eventsActionEvent[]yes
recording_idstring | nullnoRecording this log is time-aligned with.

AmbiguityAnnotation

Task-ambiguity label for a session, with its evidence references.

FieldTypeRequiredDescription
clarity"clear" | "ambiguous"yes
ambiguity_types"intent" | "referential" | "parametric" | "cross_modal"[]noRequired when clarity is 'ambiguous'.
severity"low" | "medium" | "high" | "blocking" | nullno
risk"low" | "medium" | "high" | nullno
feasibility"feasible" | "partially_feasible" | "infeasible" | "unknown" | nullno
alternate_answersstring[]noOther defensible answers a solver could have given.
root_of_confusionstring | nullnoWhat in the task made it ambiguous.
task_promptstring | nullnoInstruction that was annotated.
notesstring | nullno
annotatorstring | nullno
recording_idstring | nullno
actions_idstring | nullno

BuildDesktopImageRequest

Build-or-resolve a content-addressed desktop image from curated catalog apps.

FieldTypeRequiredDescription
profilestringnoDefault: "desktop-ubuntu".
appsstring[] | nullnoCatalog desktop_app ids (e.g. chrome). Mutually exclusive with bundle.
bundlestring | nullnoCatalog desktop_app_bundle id (e.g. osworld). Mutually exclusive with apps.
datapoint_idstring | nullnoOptional builder tag prefix; defaults to sandboxes-{profile}.

BuildDesktopImageResponse

FieldTypeRequiredDescription
image_uristringyes
content_hashstringyes
cachedbooleanyes
profilestringyes
appsstring[]no

CreateSandboxRequest

FieldTypeRequiredDescription
profilestringnoDefault: "sandboxes-default".
sourceobject | nullno
image_refstring | nullnoImmutable pin: OCI repo@sha256:<digest> / content-hash tag under SANDBOX_HARBOR_TASK_REPO (desktop-osworld / images/build). Floating tags rejected.
autostop_secnumber | nullno
ttl_secnumber | nullno
linked_job_idstring | nullno
task_idstring | nullnoOptional Gen Studio / CTP task id for labels and episode metadata; when omitted, sandbox_id is used.
reusebooleannoWhen true, return an existing non-destroyed sandbox whose create fingerprint matches (owner salt + tenant/project + profile + image_ref + workspace_key + purpose/template labels) instead of provisioning a new session. Default false so pass@k / episode reset stay create-always. Default: false.
reuse_saltstring | nullnoOptional client nonce mixed into the fingerprint salt. Rotate to force a fresh VM while keeping the same task_id. Required for fingerprinting when the caller has no Keycloak owner principal.
labelsobjectnoConsumer tags for analytics (trainer, pass, program, …).

ExecRequest

FieldTypeRequiredDescription
commandstring[]yes
cwdstring | nullno
timeout_secnumber | nullno
envobject | nullno

HTTPValidationError

FieldTypeRequiredDescription
detailValidationError[]no

ValidationError

FieldTypeRequiredDescription
locstring | integer[]yes
msgstringyes
typestringyes
inputanyno
ctxobjectno

VerifyRequest

FieldTypeRequiredDescription
require_verifier_evidencebooleannoDefault: false.

ViewerDescriptor

Signed desktop viewer entry returned on running desktop/ide sandboxes.

FieldTypeRequiredDescription
urlstringyesGateway URL to open the viewer (includes signed token).
mode"novnc" | "guacamole" | "provider" | "selkies-ws" | "openvscode"yesIntegration mode for the external viewer_ui library.
embedstringnoDefault: "iframe".
input_enabledbooleanyesFalse while the sandbox is not in running status.
resolutioninteger[]no
expires_atintegeryesUnix epoch seconds when the signed viewer token in url expires. Remint via POST /v1/{sandbox_id}/viewer/token before this time.
guest_usernamestring | nullnoGuest OS login name when the provider exposes identity.
guest_hostnamestring | nullnoGuest OS hostname shown in the desktop shell.
guest_homestring | nullnoGuest home directory.
workspace_mountstring | nullnoGuest path for tenant workspace files (SandboxWorkspace).
stream_urlstring | nullnoOptional C1 gateway WebSocket path for pixels/input (/sandbox/stream/v1/{id}/websockify?ticket=…).

WorkspaceSyncRequest

FieldTypeRequiredDescription
direction"pull" | "push"yespull = GCS→guest SandboxWorkspace; push = guest→GCS snapshot.

Machine-readable spec: OpenAPI YAML.