Skip to main content

Quickstart

This walkthrough takes you from an empty account to a sandbox that ran a command, kept its files, and was cleaned up. Every step shows the Public API call and the equivalent MCP tool, so you can follow it from a script or let an AI client do it.

The API base URL is https://api.quave.cloud/api/public/v1, and every request sends your token in the Authorization header (no Bearer prefix). See Get Started for tokens.

export API=https://api.quave.cloud/api/public/v1
export TOKEN=YOUR_TOKEN
export ACCOUNT_ID=YOUR_ACCOUNT_ID

1. Pick an app and a region​

Sandboxes belong to an app. Any app of the account works, including an empty one created only to group sandboxes (create-app over MCP, or the dashboard). Note its id as APP_ID.

Pick a region enabled for your account that has the Agent Sandbox add-on. If you are unsure, try the create call in step 4: a region without the add-on answers 409 and nothing is created.

2. Create a template​

A template is the name agents ask for. Its runtime configuration lives in revisions (step 3).

curl -X POST "$API/sandbox-template/save" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"accountId": "'"$ACCOUNT_ID"'",
"appId": "APP_ID",
"name": "python-workspace",
"description": "Python 3.12 with a persistent workspace"
}'

The response (201) carries template.templateId. MCP: save-sandbox-template.

3. Add a revision​

A revision pins the image by digest and sets size, persistence, and timeouts. Get the digest of the image you want to run, for example with docker buildx imagetools inspect python:3.12-slim, and use the repo/image@sha256:... form.

curl -X POST "$API/sandbox-template/revision" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"accountId": "'"$ACCOUNT_ID"'",
"templateId": "TEMPLATE_ID",
"imageDigest": "docker.io/library/python:3.12-slim@sha256:<64 hex characters>",
"imageRef": "python:3.12-slim",
"command": ["sleep", "infinity"],
"zClouds": 2,
"persistenceMode": "BOTH",
"snapshotExclude": ["node_modules", "*.log"],
"idleTimeoutSeconds": 1800,
"hardLifetimeSeconds": 14400
}'
  • command keeps the container alive so you can run commands in it. Without a command, the image's own entrypoint must be long-running, or the sandbox exits and never becomes READY.
  • zClouds: 2 is 1 vCPU and 1 GB of memory (1 zCloud = 0.5 vCPU + 512 MB).
  • BOTH keeps a live directory at /workspace/persist and snapshots the whole workspace when the sandbox is paused. See Persistence and volumes.
  • The sandbox is terminated after 30 minutes without activity or 4 hours in total, whichever comes first.

MCP: create-sandbox-template-revision. More options in Templates and images.

4. Create a sandbox​

curl -X POST "$API/sandbox/create" \
-H "Authorization: $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-42",
"region": "us-5",
"idempotencyKey": "conversation-42"
}'

The first call answers 201 with wasCreated: true and the sandbox, usually in ALLOCATING. Repeating the same call (same account, app, caller, and idempotencyKey) answers 200 with wasCreated: false and the same sandbox, so you can retry after any network error. MCP: create-sandbox.

Because the mode is BOTH and no volumeName was sent, Quave ONE created a shared volume named sbvol-<sandboxId> for it. The response shows it in volumeName.

Workload profiles​

workloadProfile labels what the sandbox is for, and decides which ownerKind it must carry. Use the one that matches your system; ownerId is your own identifier for that owner.

workloadProfileownerKindUse it for
CODE_INTERPRETER_TURNEXTERNAL_SESSIONCode execution on behalf of a session in your own product, such as a chat assistant's code interpreter. Put your session id in ownerId (and optionally externalSessionId).
QUAVE_INTERACTIVE_AGENTAGENT_CONVERSATIONAn interactive agent conversation; one sandbox per conversation.
QUAVE_AUTOMATION_RUNAGENT_RUNAn automated, unattended agent run.

A mismatched pair is refused. The profile does not change size, limits, or price.

5. Wait until it is ready​

curl "$API/sandbox?accountId=$ACCOUNT_ID&sandboxId=SANDBOX_ID" -H "Authorization: $TOKEN"

Poll every few seconds until sandbox.status is READY. With room on the sandbox pool this takes a few seconds; when the pool has to add a node first, allow a few minutes. If the status becomes FAILED, read errorCode and errorRetryable (error codes). MCP: get-sandbox.

6. Run commands​

curl -X POST "$API/sandbox/execute" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{
"accountId": "'"$ACCOUNT_ID"'",
"sandboxId": "SANDBOX_ID",
"command": ["sh", "-c", "python3 -V && echo hello > /workspace/persist/hello.txt && ls -l /workspace/persist"],
"timeoutSeconds": 120
}'

The response is {"success": true, "result": {"exitCode": 0, "stdout": "..."}}. stdout and stderr are base64-encoded; decode them before reading. Empty streams are omitted. The command is an argv array; use ["sh", "-c", "..."] for shell features. Pass secrets in envVars; they exist only for that command. MCP: execute-sandbox-command. Details in Running commands.

7. Pause and resume​

curl -X POST "$API/sandbox/pause" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"accountId": "'"$ACCOUNT_ID"'", "sandboxId": "SANDBOX_ID"}'

Pausing snapshots the workspace (in SNAPSHOT and BOTH), releases the compute, and moves the sandbox to SUSPENDED. A paused sandbox is not billed for compute. MCP: pause-sandbox.

To continue, just run a command again: execute on a SUSPENDED sandbox resumes it (same sandboxId) and restores the snapshot. If the resume takes longer than the request, you get 409 with data.retryable: true and data.retryAfterSeconds; wait and repeat the same call. You can also call POST /sandbox/resume (MCP: resume-sandbox) and poll until READY.

8. Clean up​

curl -X POST "$API/sandbox/terminate" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"accountId": "'"$ACCOUNT_ID"'", "sandboxId": "SANDBOX_ID"}'

Termination is final and idempotent. It never deletes the shared volume: /workspace/persist is still there for the next sandbox (pass volumeName on create), and for inspection on the Shared Storage page. When you no longer need the data, remove the volume:

curl -X POST "$API/shared-volume/remove" \
-H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
-d '{"accountId": "'"$ACCOUNT_ID"'", "name": "sbvol-SANDBOX_ID", "purgeAfterDays": 7}'

The data stays restorable for purgeAfterDays days. MCP: terminate-sandbox, remove-shared-volume, restore-shared-volume.

Next steps​