A BoxCompute workspace is the parent for Sandbox instances. A workspace can own multiple Sandboxes, each with a stable customer-facing id. All list and lookup operations are limited to the account that owns the API key.
New Sandboxes are VM-only. See VM Sandboxes (Beta)
for the fixed profile, network choice, integration walkthrough, and current
limitations. vmSandbox: false returns 400 INVALID_REQUEST without allocating
compute. Legacy gVisor slots cannot be started, resumed, or recreated through
the customer API. Contact support@boxcompute.ai about legacy data rather than
retrying start or execution; do not assume a historical slot still has a recoverable disk.
Use an official SDK
import { BoxCompute } from "@boxcompute/sdk";
const boxcompute = new BoxCompute({ apiKey: process.env.BOXCOMPUTE_API_KEY! });
const workspaces = await boxcompute.workspaces.list();
const workspace = workspaces[0] ?? await boxcompute.workspaces.create({
name: "Agent workspace",
idempotencyKey: "workspace-bootstrap-v1",
});
const sandbox = await boxcompute.sandboxes.create({
workspaceId: workspace.id,
name: "Worker 1",
idempotencyKey: crypto.randomUUID(),
});
const current = await boxcompute.sandboxes.inspect(sandbox.id);
await boxcompute.sandboxes.delete(current.id);import os
import uuid
from boxcompute import BoxCompute
with BoxCompute(api_key=os.environ["BOXCOMPUTE_API_KEY"]) as boxcompute:
workspaces = boxcompute.workspaces.list()
workspace = workspaces[0] if workspaces else boxcompute.workspaces.create(
name="Agent workspace",
idempotency_key="workspace-bootstrap-v1",
)
sandbox = boxcompute.sandboxes.create(
workspace_id=workspace.id,
name="Worker 1",
idempotency_key=str(uuid.uuid4()),
)
current = boxcompute.sandboxes.inspect(sandbox.id)
boxcompute.sandboxes.delete(current.id)Generate one idempotency key per intended Sandbox creation and reuse it only when retrying that same creation.
Direct HTTP
List owned workspaces
curl 'https://api.boxcompute.ai/api/v2/workspaces' \
-H 'Authorization: Bearer bc_live_REDACTED'This includes owned workspaces that do not yet have a Sandbox. Use a returned workspace id when creating an instance.
Bootstrap a workspace
If the account has no workspace, create one with a key that has workspace:create scope:
curl -X POST 'https://api.boxcompute.ai/api/v2/workspaces' \
-H 'Authorization: Bearer bc_live_REDACTED' \
-H 'Idempotency-Key: workspace-bootstrap-v1' \
-H 'Content-Type: application/json' \
-d '{"name":"Agent workspace"}'name is required, must contain a non-whitespace character, and can contain at most 80 characters. The required idempotency key is account-scoped: retrying the same canonical request within 24 hours returns the original 201 response and workspace ID. Reusing the key for a different public mutation returns 409 IDEMPOTENCY_CONFLICT.
The response is { "workspace": ... }. The new workspace appears in subsequent workspace lists and can own multiple Sandboxes. Accounts can have at most 10 workspaces; exceeding that limit returns 409 WORKSPACE_QUOTA_EXCEEDED.
Workspaces persist independently of their Sandboxes. The public API does not provide workspace deletion, so create one only when the account needs another durable Sandbox parent.
Create and start an instance
curl -X POST 'https://api.boxcompute.ai/api/v2/sandboxes' \
-H 'Authorization: Bearer bc_live_REDACTED' \
-H 'Idempotency-Key: 019f1234-5678-7000-8000-000000000000' \
-H 'Content-Type: application/json' \
-d '{"workspaceId":"ws_REDACTED","name":"Worker 1"}'workspaceId is required and must identify a workspace owned by the key’s account. name is optional; when supplied, it must be a non-empty string of at most 80 characters. Each accepted request creates another independent VM. Creation returns 202 while pending, and replaying the same idempotency key and body returns 201 once it is running. Both success responses contain { "sandbox": ... }.
The whole root disk, including the OS and /workspace, defaults to 30 GiB. Raw HTTP callers may
set diskGiB to a whole number from 10 to 30 at creation; below 30 requires early access and is
otherwise rejected with 400 INVALID_REQUEST. It cannot be resized later. The full chosen disk
counts toward account storage even when stopped. Changing it under the same idempotency key
returns 409 IDEMPOTENCY_CONFLICT; omitted and explicit 30 are equivalent. See the
disk contract before choosing a non-default size.
Raw HTTP callers can also set immutable metadata tags and autoResume: false to require an
explicit start before execution or file access on a stopped VM. List with repeated
metadata=key=value filters to find tagged Sandboxes. See metadata and wake-up control
for limits, a runnable example and retry equivalence. These options are not exposed by the
current SDKs or CLI.
Creation can return 429 WORKSPACE_QUOTA_EXCEEDED when the account reaches its running-VM,
Sandbox-count or storage limit. A durably accepted VM becomes expired when this limit refuses
provisioning, and replay returns the 429 reason. Stop an unneeded running VM for the running
limit; freeing total-VM or storage capacity requires deletion. See capacity recovery.
Retry creation safely
Idempotency-Key is required with explicit vmSandbox: true and optional when the selector is omitted. Use one whenever a create might be retried after a timeout or lost response. Generate a unique value for each intended creation and persist it until the response is safely recorded.
Repeating the same canonical request with the same key on the same account within 24 hours recovers the same Sandbox ID: 202 while pending, then 201 once running. Concurrent requests with that key share the same creation. Reusing it for a different create request or another public mutation returns 409 IDEMPOTENCY_CONFLICT. Keys are account-scoped even when the requests use different API keys. Completed historical responses retain their original runtime; pending historical gVisor creates now fail with 400 INVALID_REQUEST without allocating compute.
Omitting the header preserves non-idempotent behavior: every request can create another Sandbox. After 24 hours, a reused key can also create a new mutation, so do not use a longer retry window.
List and inspect
curl 'https://api.boxcompute.ai/api/v2/sandboxes' \
-H 'Authorization: Bearer bc_live_REDACTED'
curl 'https://api.boxcompute.ai/api/v2/sandboxes/sbx_REDACTED' \
-H 'Authorization: Bearer bc_live_REDACTED'The list response is { "sandboxes": [...] }; inspection returns { "sandbox": ... }. State is
cold, pending, running, or expired. Ordinary Sandboxes use cold and running; VM beta
provisioning adds pending, and expired means the VM runtime is no longer available. See
HTTP API reference for the complete shape.
A running VM stops on its own 30 minutes after the last API call that used it, and the Sandbox becomes cold. See what keeps a VM awake, or stop it sooner. Its retained workload and command output can remain available without restarting it; see Read Sandbox logs.
VMs also request maintenance sleep after about 42 hours of continuous uptime, even with activity. This is not Sandbox expiry or deletion: the disk remains, but processes, shells and open connections stop. Save progress to disk and resume the same Sandbox before restarting applications. See VM sleep and resume.
Start an existing slot
Direct HTTP clients can start an existing owned Sandbox without allocating another instance:
curl -X POST 'https://api.boxcompute.ai/api/v2/sandboxes/sbx_REDACTED/start' \
-H 'Authorization: Bearer bc_live_REDACTED' \
-H 'Idempotency-Key: start-existing-v1' \
-H 'Content-Type: application/json' \
-d '{}'This route requires sandbox:create, accepts only an empty JSON object, and returns 201 with
{ "sandbox": ... }. The idempotency header is optional. It reuses the selected slot and its
recorded runtime choice; it does not accept a workspace, image, library, profile, or runtime
selector. The CLI exposes this as bxc sandbox resume SANDBOX_ID; the SDKs do not expose a resume method.
Starting can also return 429 WORKSPACE_QUOTA_EXCEEDED when account Sandbox or retained-storage
capacity is full. It cannot start, resume, or recreate a legacy gVisor runtime.
Stop a VM
Stop a running VM when you are done with it instead of paying until the idle stop:
curl -X POST 'https://api.boxcompute.ai/api/v2/sandboxes/sbx_REDACTED/stop' \
-H 'Authorization: Bearer bc_live_REDACTED' \
-H 'Content-Type: application/json' \
-d '{}'This route requires sandbox:create, accepts only an empty JSON object, and returns 200 with
{ "sandbox": ..., "stopped": true }. Compute billing ends and the disk, including /workspace, is
kept; running processes, shells and open connections end. stopped is false when the VM is
already stopped (cold). Stopping never starts a VM and needs no credit, so it also works at zero balance.
409 SANDBOX_UNAVAILABLE means the VM is still starting, stopping or in use; retry shortly.
By default, start, execute or any file call starts it again with the same files after a cold start
of about 30 seconds. With autoResume: false, execution, operation start and file calls instead
return 409 SANDBOX_STOPPED; use explicit start first. Each run is billed for at least 60 seconds;
see Track Sandbox costs. CLI 0.11.0 and later expose stop as
bxc sandbox stop SANDBOX_ID; resume with bxc sandbox resume SANDBOX_ID.
The SDKs do not expose stop yet.
What keeps a VM awake
A running VM stops 30 minutes after the last API call that used it; the check runs once a minute. These calls count:
- start and execute
- every file call, including stat, read and list
- starting an operation
- creating a preview, and requests to a preview URL (counted at most once a minute)
- desktop start/stop, screenshots, actions and active viewer use on an already running VM
Reading the Sandbox, desktop status, logs, analytics or costs, polling or waiting on an operation, and service
access do not count. Neither does activity inside the VM: a server or other background process does
not keep it running. Execute and file calls, including reads, start a stopped VM first unless
it was created with autoResume: false. Desktop calls never start a stopped VM.
GET /api/v2/sandboxes/:id never starts a VM. While it runs, its lastUsedAt is the last call that
counted, so the idle stop is due 30 minutes later.
Delete
curl -X DELETE 'https://api.boxcompute.ai/api/v2/sandboxes/sbx_REDACTED' \
-H 'Authorization: Bearer bc_live_REDACTED' \
-H 'Content-Type: application/json'Deletion returns 204 No Content and removes that Sandbox instance, its compute and its storage. It
does not delete its parent workspace or sibling Sandboxes. BoxCompute keeps a read-only history row
for about 15 months; this record does not retain files, consume Sandbox quota or provide restore,
start or delete controls.
Review deleted Sandboxes
List up to the 100 most recently deleted Sandboxes for the account:
curl 'https://api.boxcompute.ai/api/v2/deleted-sandboxes' \
-H 'Authorization: Bearer bc_live_REDACTED'{
"deletedSandboxes": [
{
"id": "dsb_REDACTED",
"sandboxId": "sbx_REDACTED",
"name": "Worker 1",
"workspaceId": "ws_REDACTED",
"workspaceName": "Agent workspace",
"vmSandbox": true,
"size": "small",
"runtimes": ["REDACTED_OPAQUE_RUNTIME"],
"deletedVia": "api",
"deletedByApiKeyId": "key_REDACTED",
"createdAt": 1791244800000,
"lastUsedAt": 1791244860000,
"deletedAt": 1791244920000
}
]
}workspaceName, deletedByApiKeyId, and lastUsedAt can be null. deletedVia is console,
api, agent, or workspace. Treat values in runtimes as opaque log selectors and pass them back
only as described in Read Sandbox logs. The current SDKs and CLI do not expose
deleted-Sandbox history directly.
The deletion record also preserves the identifier used to inspect that Sandbox’s retained compute-cost detail. Cost history does not restore its compute or files.