Skip to main content

Agent Sandboxes API

The Agent Sandboxes API lets you provision isolated gVisor sandboxes for agent workloads in Quave ONE. Sandboxes are created from versioned sandbox templates whose immutable revisions pin the image, resources, persistence mode, and lifetime limits, and Quave ONE owns the whole sandbox lifecycle: allocation, idle suspension, hard-lifetime expiry, and termination.

Availability. Agent Sandboxes are available for client regions on the Connect and Full Private plans. If your account is not on one of these plans, sandbox provisioning is not enabled for your regions — talk to Quave ONE to enable it.

Make sure to read the Get Started document to understand how the API works.

Note: These endpoints accept user tokens, and MCP or OAuth tokens carrying the scopes documented per operation. Environment tokens are not accepted, because a sandbox records the caller who requested it and an environment token identifies an environment rather than a person.

Sandboxes are account-scoped. Every request must include accountId, and the caller needs access to that account: read operations require account membership, and create or terminate operations require an admin or technical member.

Sandbox template object

A sandbox template is the named, reusable definition an agent asks for. Its current revision holds the runtime configuration.

FieldTypeDescription
templateIdStringTemplate ID.
accountIdStringOwning account ID.
appIdStringOptional app the template belongs to.
nameStringHuman-readable template name.
slugStringURL-safe identifier derived from the name.
descriptionStringOptional description.
enabledBooleanWhether new sandboxes may be requested from this template.
deprecatedAtStringISO 8601 timestamp set when the template was deprecated. Absent while active.
currentRevisionNumberIntegerRevision number of the latest revision.
createdAtStringISO 8601 creation timestamp.
updatedAtStringISO 8601 last update timestamp.

The current revision is returned by the get-template operation:

FieldTypeDescription
revisionIdStringRevision ID.
templateIdStringParent template ID.
revisionIntegerMonotonic revision number. Revisions are immutable.
imageDigestStringImmutable image digest the sandbox runs.
imageRefStringHuman-readable image reference.
commandArray of stringsOptional entrypoint command.
workingDirStringOptional working directory inside the sandbox.
portsArrayOptional declared ports.
resourcesObjectCPU and memory request and limit values.
persistenceModeStringEPHEMERAL or PERSISTENT_VOLUME.
persistentWorkspaceObjectPersistent workspace settings when the mode is PERSISTENT_VOLUME.
isolationProfileStringRuntime isolation profile. GVISOR in this version.
idleTimeoutSecondsIntegerIdle time after which Quave ONE suspends the sandbox.
hardLifetimeSecondsIntegerMaximum lifetime, after which Quave ONE terminates the sandbox.
suspendedRetentionSecondsIntegerHow long a suspended workspace is retained.
warmPoolObjectOptional warm-pool capacity with desired, min, and max. Mutually exclusive with PERSISTENT_VOLUME.
createdAtStringISO 8601 creation timestamp.

Sandbox object

FieldTypeDescription
sandboxIdStringSandbox ID.
accountIdStringOwning account ID.
appIdStringApp the sandbox belongs to.
templateIdStringTemplate the sandbox was created from.
templateRevisionIdStringExact template revision the sandbox runs.
templateRevisionNumberIntegerRevision number of that revision.
regionStringRegion the sandbox runs in.
workloadProfileStringWorkload profile the sandbox was provisioned for.
isolationProfileStringRuntime isolation profile, denormalized from the revision.
persistenceModeStringPersistence mode, denormalized from the revision.
ownerKindStringTyped owner reference kind required by the workload profile.
ownerIdStringID of the owner the sandbox is attached to.
externalSessionIdStringOptional external session identifier.
callerKeyStringCaller provenance, in the form user:USER_ID.
statusStringCurrent lifecycle status. See Sandbox statuses below.
statusReasonStringOptional short reason for the current status.
desiredStatusStringStatus Quave ONE is driving the sandbox toward, when different from status.
generationIntegerIncrements on every status change. Useful to detect concurrent transitions.
isWarmAllocationBooleanWhether the sandbox came from a warm pool.
resourcesObjectCPU and memory the sandbox was allocated.
claimedAtStringISO 8601 timestamp when the sandbox was claimed.
readyAtStringISO 8601 timestamp when the sandbox became ready.
lastActiveAtStringISO 8601 timestamp of the last activity. Drives idle suspension.
suspendedAtStringISO 8601 timestamp of the last suspension.
resumedAtStringISO 8601 timestamp of the last resume.
terminatedAtStringISO 8601 termination timestamp.
expiresAtStringISO 8601 hard-lifetime deadline, derived from the revision.
terminationReasonStringWhy the sandbox was terminated.
errorCodeStringStable product error code when the sandbox failed.
errorRetryableBooleanWhether errorCode describes a condition worth retrying.
correlationIdStringCorrelation ID for tracing the sandbox across systems.
createdAtStringISO 8601 creation timestamp.
updatedAtStringISO 8601 last update timestamp.

Note: idempotencyKey is write-only. You send it when creating a sandbox, and it is never returned in a sandbox object.

Create or connect a sandbox

Send a POST request to /api/public/v1/sandbox/create. Requires the quave:write:deploy scope.

FieldTypeRequiredDescription
accountIdStringYesAccount that owns the sandbox.
appIdStringYesApp the sandbox belongs to.
templateIdStringYesSandbox template to provision from. It must be enabled, not deprecated, and have at least one revision.
workloadProfileStringYesTESS_CODE_TURN, QUAVE_INTERACTIVE_AGENT, or QUAVE_AUTOMATION_RUN.
ownerKindStringYesOwner reference kind. Must be the kind required by the workload profile: EXTERNAL_SESSION, AGENT_CONVERSATION, or AGENT_RUN.
ownerIdStringYesID of the owner the sandbox is attached to.
externalSessionIdStringNoOptional external session identifier.
regionStringYesRegion to run the sandbox in.
idempotencyKeyStringYesCaller-chosen key that makes this call safe to retry.

This operation is idempotent. The same account, the same caller, and the same idempotencyKey always resolve to the same sandbox:

  • The first call creates the sandbox and answers 201 with wasCreated: true.
  • Any repeated call connects to the existing sandbox and answers 200 with wasCreated: false.

Retrying a create request after a network timeout is therefore safe and never provisions a second sandbox.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"accountId": "ACCOUNT_ID",
"appId": "APP_ID",
"templateId": "TEMPLATE_ID",
"workloadProfile": "QUAVE_INTERACTIVE_AGENT",
"ownerKind": "AGENT_CONVERSATION",
"ownerId": "CONVERSATION_ID",
"region": "us-5",
"idempotencyKey": "conversation-42-turn-7"
}' \
https://api.quave.cloud/api/public/v1/sandbox/create

Example response:

{
"success": true,
"wasCreated": true,
"sandbox": {
"sandboxId": "SANDBOX_ID",
"accountId": "ACCOUNT_ID",
"appId": "APP_ID",
"templateId": "TEMPLATE_ID",
"templateRevisionId": "TEMPLATE_REVISION_ID",
"templateRevisionNumber": 3,
"region": "us-5",
"workloadProfile": "QUAVE_INTERACTIVE_AGENT",
"isolationProfile": "GVISOR",
"persistenceMode": "EPHEMERAL",
"ownerKind": "AGENT_CONVERSATION",
"ownerId": "CONVERSATION_ID",
"callerKey": "user:USER_ID",
"status": "REQUESTED",
"generation": 0,
"expiresAt": "2026-08-07T13:00:00.000Z",
"createdAt": "2026-08-07T12:00:00.000Z"
}
}

List sandboxes

Send a GET request to /api/public/v1/sandboxes. Requires the quave:read scope.

Query parameterTypeDescription
accountIdStringAccount ID. Required.
appIdStringOptional app filter.
statusStringOptional status filter. Pass one status or comma-separated statuses, for example READY,BUSY. Unknown statuses are ignored.
limitIntegerSandboxes per page. Default: 50. Maximum: 100.
pageIntegerOne-based page number. Default: 1.

The response includes accountId, page, limit, totalCount, and sandboxes, sorted by creation time, newest first.

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/sandboxes?accountId=ACCOUNT_ID&status=READY,BUSY&page=1&limit=20'

Get a sandbox

Send a GET request to /api/public/v1/sandbox. Requires the quave:read scope.

Query parameterTypeDescription
accountIdStringAccount ID. Required.
sandboxIdStringSandbox ID. Required.
curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/sandbox?accountId=ACCOUNT_ID&sandboxId=SANDBOX_ID'

Terminate a sandbox

Send a POST request to /api/public/v1/sandbox/terminate. Requires the quave:write:deploy scope.

FieldTypeRequiredDescription
accountIdStringYesAccount ID.
sandboxIdStringYesSandbox ID.

Termination is idempotent. Terminating a sandbox that is already terminated succeeds and returns the sandbox unchanged, so retries are safe. A sandbox that has not reached a running state yet is first marked as failed and then terminated, so no sandbox is ever left holding compute.

If the sandbox status changes concurrently while the request is being processed, Quave ONE answers 409 with Sandbox is changing state. Retry the call.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"accountId": "ACCOUNT_ID",
"sandboxId": "SANDBOX_ID"
}' \
https://api.quave.cloud/api/public/v1/sandbox/terminate

Example response:

{
"success": true,
"sandbox": {
"sandboxId": "SANDBOX_ID",
"status": "TERMINATED",
"terminationReason": "USER_REQUESTED",
"terminatedAt": "2026-08-07T12:30:00.000Z"
}
}

List sandbox templates

Send a GET request to /api/public/v1/sandbox-templates. Requires the quave:read scope.

Query parameterTypeDescription
accountIdStringAccount ID. Required.
appIdStringOptional app filter.
includeDeprecatedBooleanInclude deprecated templates. Default: false.
curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/sandbox-templates?accountId=ACCOUNT_ID'

Get a sandbox template

Send a GET request to /api/public/v1/sandbox-template. Requires the quave:read scope.

Query parameterTypeDescription
accountIdStringAccount ID. Required.
templateIdStringTemplate ID. Required.

The response contains template and currentRevision. currentRevision is null when the template has no revision yet, and a template without a revision cannot serve sandboxes.

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/sandbox-template?accountId=ACCOUNT_ID&templateId=TEMPLATE_ID'

Sandbox statuses

StatusDescription
REQUESTEDThe sandbox was requested and is waiting to be allocated.
ALLOCATINGQuave ONE is allocating capacity for the sandbox.
STARTINGThe sandbox is starting.
READYThe sandbox is running and idle. It can accept work.
BUSYThe sandbox is running work.
SUSPENDINGQuave ONE is suspending the sandbox, usually after an idle timeout.
SUSPENDEDCompute was released. A persistent workspace is retained for the configured retention window.
RESUMINGThe sandbox is being resumed from a suspended workspace.
FAILEDThe sandbox failed. See errorCode and errorRetryable.
TERMINATINGTermination is in progress.
TERMINATEDTerminal status. The sandbox no longer exists and cannot be resumed.

Quave ONE drives these transitions itself. Sandboxes are suspended after their template's idleTimeoutSeconds of inactivity and terminated when they reach expiresAt, without any call from you.

Retryable errors

Sandbox failures surface as stable product error codes rather than raw runtime messages, so you can branch on them safely. The sandbox object carries errorCode together with errorRetryable, which tells you whether waiting and retrying can succeed.

Error codeRetryableDescription
SANDBOX_SUSPENDEDYesThe sandbox is suspended and must be resumed first.
SANDBOX_NOT_READYYesThe sandbox is not ready to accept work yet.
AGENT_UNREACHABLEYesThe sandbox runtime is temporarily unreachable.
SANDBOX_NOT_FOUNDNoThe sandbox does not exist in the runtime.
NAMESPACE_DENIEDNoThe sandbox runtime rejected the namespace.
UNAUTHENTICATEDNoThe runtime credential was not accepted.
TOKEN_EXPIREDNoThe runtime credential expired.
INSUFFICIENT_SCOPENoThe runtime credential lacked the required scope.
PATH_ESCAPENoA file path outside the sandbox workspace was rejected.
UNKNOWNNoUnclassified failure. Unmapped runtime errors fail closed to this code.

Retryable codes are safe to retry with backoff, ideally reusing the same idempotencyKey so a retry reconnects instead of provisioning a second sandbox.

Note: Running commands, streaming output, and reading or writing files inside a sandbox are not part of this API version. This version covers the sandbox and sandbox template control plane only.