Skip to main content

Processes, files and terminals

execute runs one command per request, up to 15 minutes. For everything else, open a gateway session and talk to the sandbox directly:

  • start processes that run for hours and stream their output, reattaching after a disconnect;
  • send input, resize terminals, and send signals;
  • list, read, write, and delete files in the workspace;
  • run interactive terminals (PTY).

Every client uses the same session: your own scripts, AI agents, and Quave ONE Solutions alike.

Open a session​

curl -X POST "$API/sandbox/session" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"accountId": "ACCOUNT_ID", "sandboxId": "SANDBOX_ID", "scopes": ["exec", "file"]}'
{
"success": true,
"endpoint": "https://sandbox-gateway.REGION_DOMAIN",
"token": "qsb1....",
"expiresAt": "2026-10-11T12:05:00.000Z"
}
ScopeGrants
execStart, inspect, and stream processes; signal and status imply it.
fileList, read, write, and delete files under /workspace.
ptyStart processes with a terminal and resize them.
  • Requires the quave:write:dangerous scope and permission to manage the sandbox, like execute. An unknown scope answers 400.
  • A paused sandbox is resumed first. While the resume is still running, the answer is 409 with data.retryable: true and data.retryAfterSeconds.
  • The token is short-lived (see expiresAt). Open a new session before it expires; sessions are cheap. A process keeps running when its session expires.
  • Send the token to the gateway as Authorization: Bearer <token>. Never send your Quave ONE token to the gateway.
  • Opening a session is audited (sandbox and scopes only). Output and file contents never pass through or get stored by Quave ONE.

In the examples below, GW is the session endpoint and SESSION its token.

Processes​

Start a process​

curl -X POST "$GW/v1/process/start" \
-H "Authorization: Bearer $SESSION" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: build-42' \
-d '{
"config": {
"command": ["sh", "-c", "cd /workspace/persist/repo && npm ci && npm test"],
"cwd": "/workspace",
"envVars": {"CI": "1"}
},
"timeoutSeconds": 7200
}'
{ "executionId": "EXECUTION_ID", "processId": 42, "startedAt": "2026-10-11T12:00:00Z" }
  • command is argv, never a shell string.
  • timeoutSeconds is the process deadline. It defaults to 30 minutes; set it to the time the job needs, or -1 for no deadline (the process then runs until it exits or the sandbox ends).
  • Idempotency-Key is required (up to 200 bytes). Use a key unique to the work you start: a retried start with the same key returns the same execution instead of starting a second process. Without it the gateway answers 422 idempotency_key_required.
  • Add "pty": {"cols": 120, "rows": 40} (needs the pty scope) to run the process in a terminal.

Check a process​

GET $GW/v1/process/EXECUTION_ID returns state (running or exited), exitCode and reason once exited, startedAt, exitedAt, stdoutBytes, stderrBytes, and outputTruncated.

Stream output​

curl -N "$GW/v1/process/EXECUTION_ID/stream?from=0" -H "Authorization: Bearer $SESSION"

The response is newline-delimited JSON (application/x-ndjson), one event per line, until the process exits:

{"seq":0,"init":{"processId":42}}
{"seq":1,"stdout":"aW5zdGFsbGluZy4uLgo="}
{"seq":2,"stderr":"d2FybmluZwo="}
{"seq":3,"exit":{"exitCode":0}}
  • stdout and stderr are base64-encoded bytes.
  • seq increases by one per event. To reattach after a disconnect, stream again with from= the last seq you processed plus one. Events are replayed from a per-process buffer.
  • A truncated event ({"reason":"ring_evicted"|"output_cap", "droppedBytes":...}) tells you that output was dropped: either you reattached after the buffer rolled over, or the process passed its total output cap (16 MB). The process keeps running.
  • exit.reason is set when the process did not exit on its own: deadline_exceeded, canceled, killed (out of memory or an unrequested kill), or agent_shutdown (the sandbox was paused or terminated).

Input, signals and terminals​

RequestBodyEffect
POST /v1/process/EXECUTION_ID/stdin (?eof=true to close)Raw bytesWrites to the process's standard input.
POST /v1/process/EXECUTION_ID/signal{"signal": "SIGTERM"} (SIGINT, SIGTERM, SIGKILL)Signals the process.
POST /v1/process/EXECUTION_ID/resize{"cols": 120, "rows": 40}Resizes a PTY process.

Keeping the sandbox alive​

A sandbox is never terminated for idleness while a process started through the gateway is running, and API calls count as activity. The maximum lifetime (hardLifetimeSeconds) always applies. If you do not need a live stream, stdin or a terminal, the command API does the same with plain Public API calls.

Files​

All paths resolve inside /workspace; anything outside answers an error (PATH_ESCAPE). Files you need after the sandbox ends belong under the persistent mount path (default /workspace/persist, see Persistence and volumes).

RequestEffect
GET /v1/files/list?path=/workspace/persistLists a directory: name, path, type, size, mode, modTime.
GET /v1/files/stat?path=...Metadata of one path.
GET /v1/files/read?path=...Raw file bytes (application/octet-stream).
PUT /v1/files/write?path=...&mode=0o644Writes the raw request body to the file (creates or replaces). mode is optional.
POST /v1/files/mkdir?path=...&parents=trueCreates a directory, with parents.
DELETE /v1/files?path=...&recursive=trueDeletes a file or directory.
curl -X PUT "$GW/v1/files/write?path=/workspace/persist/input.csv" \
-H "Authorization: Bearer $SESSION" --data-binary @input.csv
curl "$GW/v1/files/read?path=/workspace/persist/report.pdf" \
-H "Authorization: Bearer $SESSION" -o report.pdf

Errors​

Gateway errors are JSON with a stable error code and a retryable flag. Retry retryable: true errors (for example sandbox_not_ready while the sandbox starts or resumes) after a short delay. 401 means the session token expired or is invalid: open a new session. 410 sandbox_expired means the sandbox reached its maximum lifetime: create a new sandbox and attach the same volume.