Sandboxes
Create, list, inspect, set TTLs for, and kill sandboxes — isolated live execution environments.
A sandbox is an isolated live execution environment. You create one from a template, run commands and work with files inside it, and it's destroyed when its TTL expires or when you kill it.
The Sandbox object
| Field | Type | Description |
|---|---|---|
id | string | Sandbox ID, prefixed sbx_ |
orgId | string | Owning organization, prefixed org_ |
projectId | string | Owning project, prefixed prj_ |
status | string | creating, starting, ready, running, idle, expiring, destroyed, or failed |
templateId | string | Template ID, prefixed tpl_ |
templateSlug | string | Template slug, such as python-node |
templateVersion | string | Resolved template version |
templateVersionId | string | TemplateVersion ID, prefixed tplv_ |
ttlMs | integer | Requested lifetime in milliseconds |
expiresAt | string | ISO 8601 timestamp when the sandbox expires |
metadata | object | String labels set at creation |
networkPolicy | object | Normalized create-time outbound access policy |
createdAt | string | ISO 8601 creation timestamp, when available |
destroyedAt | string | ISO 8601 destruction timestamp, once destroyed |
destroyedReason | string | idle_expired, platform_cleanup, ttl_expired, or user_killed |
Create a sandbox
POST /v1/sandboxes — requires scope sandbox:create. Accepts an
Idempotency-Key header.
Creates a sandbox from a template and returns it once provisioning starts. All body fields are optional:
| Field | Type | Required | Default | Constraints |
|---|---|---|---|---|
projectId | string | No | Default project | A prj_ ID your key can access |
template | string | No | python-node | python-node or node; discover the current catalogue through Templates |
templateVersionId | string | No | — | A tplv_ ID; pins an exact immutable environment |
ttlMs | integer | No | 3600000 | Positive integer, max 3600000 (1 hour). Use the TTL endpoint later. |
metadata | object | No | — | String map; max 16 keys, keys ≤ 64 chars, values ≤ 512 chars, ≤ 4 KB total |
networkPolicy | object | No | unrestricted | Create-time outbound mode and, for list modes, up to 64 reviewed hosts |
curl -X POST https://api.crownest.dev/v1/sandboxes \
-H "Authorization: Bearer $CROWNEST_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3d9c2a-create-01" \
-d '{
"template": "python-node",
"ttlMs": 1800000,
"metadata": { "run": "nightly-eval" }
}'Returns 201 with the created sandbox:
{
"sandbox": {
"id": "sbx_abc123",
"orgId": "org_abc123",
"projectId": "prj_abc123",
"status": "creating",
"templateId": "tpl_abc123",
"templateSlug": "python-node",
"templateVersion": "2026-06-17",
"templateVersionId": "tplv_python_node_2026_06_17",
"ttlMs": 1800000,
"expiresAt": "2026-06-11T13:30:00.000Z",
"metadata": { "run": "nightly-eval" },
"networkPolicy": { "mode": "deny-all" },
"createdAt": "2026-06-11T13:00:00.000Z"
}
}Control outbound access
networkPolicy is immutable for the lifetime of a Sandbox. Use one of these
provider-neutral forms:
{ "mode": "unrestricted" }
{ "mode": "deny-all" }
{ "mode": "allowlist", "hosts": ["api.example.com", "*.example.org"] }
{ "mode": "denylist", "hosts": ["telemetry.example.com"] }deny-all blocks internet traffic except Cloudflare's documented container DNS
path. An allowlist denies internet by default and permits matching HTTP or HTTPS
request hosts. A denylist keeps default internet access but blocks matching HTTP
or HTTPS request hosts. Redirect destinations are checked again.
Hosts are lowercase ASCII names, exact IPv4 literals, or a leftmost *. suffix
pattern. The suffix matches nested subdomains but not the apex. URLs, ports,
CIDR ranges, IPv6 literals, Unicode names, and other glob forms are rejected.
This surface doesn't claim post-resolution CIDR filtering or universal
DNS-rebinding protection.
Restrictive configuration completes before the Sandbox becomes ready. If the runtime can't install it, creation fails instead of returning an unrestricted Sandbox. HTTPS interception uses the curated image's trust configuration; certificate-pinned or private-trust-store clients can fail.
crownest sandboxes create \
--network-policy allowlist \
--network-hosts api.example.com,github.comErrors: invalid_request, forbidden, not_found, quota_exceeded,
sandbox_ttl_exceeded.
List sandboxes
GET /v1/sandboxes — requires scope sandbox:read.
Returns a list of live sandboxes, newest first, in the standard
{ data, hasMore } envelope.
Destroyed and failed sandboxes are excluded unless you ask for them:
| Parameter | Type | Description |
|---|---|---|
projectId | string | Filter to one project |
status | string | Filter by sandbox status |
include | string | historical to include destroyed and failed sandboxes |
metadata.<key> | string | Filter by an exact metadata label, for example metadata.run=nightly-eval |
curl "https://api.crownest.dev/v1/sandboxes?include=historical" \
-H "Authorization: Bearer $CROWNEST_API_KEY"{
"data": [
{
"id": "sbx_abc123",
"status": "ready",
"templateSlug": "python-node",
"expiresAt": "2026-06-11T13:30:00.000Z"
}
],
"hasMore": false
}Get a sandbox
GET /v1/sandboxes/{sandboxId} — requires scope sandbox:read.
Returns one sandbox by ID. Poll this endpoint to watch status move from
creating to ready.
curl https://api.crownest.dev/v1/sandboxes/sbx_abc123 \
-H "Authorization: Bearer $CROWNEST_API_KEY"Returns 200 with { "sandbox": { ... } }. Errors: forbidden,
not_found.
Set a sandbox TTL
POST /v1/sandboxes/{sandboxId}/ttl — requires scope
sandbox:extend. Requires an Idempotency-Key
header.
This endpoint resets a live sandbox's TTL countdown from the time of the
request. The requested ttlMs must
move expiresAt later than its current value. A sandbox's total session is
capped at 24 hours from creation.
| Field | Type | Required | Description |
|---|---|---|---|
ttlMs | integer | Yes | New lifetime in milliseconds. Max 86400000 (24 hours), counted from creation. |
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/ttl \
-H "Authorization: Bearer $CROWNEST_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3d9c2a-extend-01" \
-d '{ "ttlMs": 3600000 }'Returns 200 with { "sandbox": { ... } }, including the refreshed
expiresAt.
Errors: invalid_request, forbidden, not_found,
sandbox_destroyed, sandbox_ttl_exceeded.
Deprecated alias
POST /v1/sandboxes/{sandboxId}/extend remains available until July 10, 2027. Its responses include Deprecation, Sunset, and Link headers that
identify POST /v1/sandboxes/{sandboxId}/ttl as the canonical endpoint.
Kill a sandbox
DELETE /v1/sandboxes/{sandboxId} — requires scope sandbox:kill.
Destroys the sandbox immediately. The workspace filesystem is lost; only artifacts you exported survive. The operation is idempotent by state — deleting an already-destroyed sandbox returns the destroyed record rather than an error.
curl -X DELETE https://api.crownest.dev/v1/sandboxes/sbx_abc123 \
-H "Authorization: Bearer $CROWNEST_API_KEY"{
"sandbox": {
"id": "sbx_abc123",
"status": "destroyed",
"destroyedAt": "2026-06-11T13:05:00.000Z",
"destroyedReason": "user_killed"
}
}Errors: forbidden, not_found.