Sandboxes
Core CRUD and lookup for sandbox microVMs.
At a glance
- Base URL:
https://api.sb.createos.sh - Auth:
X-Api-Key: <token>header. Get a token - Response envelope: JSend,
{"status": "...", "data": ...}
Sandbox status lifecycle
A sandbox moves through these statuses:
| Status | Description |
|---|---|
creating | Spawn in progress |
running | Active; can exec and transfer files |
pausing | Snapshot in progress |
paused | Snapshotted to storage; host freed |
resuming | Restoring from snapshot |
forking | Bundle copy in progress (source stays paused) |
error | Resume/fork exhausted retries; POST /resume to retry |
destroying | Delete in progress |
destroyed | Permanently gone |
failed | Terminal failure |
POST /v1/sandboxes
Create a new sandbox. Time to the first command depends on the image, available capacity, and host load.
Auth required: Yes
Request body
| Field | Type | Required | Description |
|---|---|---|---|
shape | string | Conditional | Required for ordinary creates; defaults to captured shape with snapshot_id. VM shape. See GET /v1/shapes for the catalog. Example: s-1vcpu-256mb |
rootfs | string | Conditional | Required for ordinary creates. Omit when restoring with snapshot_id. Rootfs catalog name. See GET /v1/rootfs. Disabled names are rejected with 400. |
name | string | No | User-facing VM name, unique per user among non-terminal sandboxes. Auto-generated (<adjective>-<animal>) if omitted. Used as hostname and in-network DNS name. |
disk_mib | integer | No | Disk size in MiB. 0 = shape default (10 GiB). |
ssh_pubkeys | string[] | No | OpenSSH public keys for SSH access. Default CLI shell and tunnel commands use API-key authentication and do not require SSH keys. |
envs | object | No | Environment variables exported into every exec. Keys must match ^[A-Za-z_][A-Za-z0-9_]*$. Up to 64 entries, 4 KiB per value, 64 KiB total. Values are never returned by GET, only key names. |
ingress_enabled | boolean | No | When true, sandbox is reachable at <ulid>-<port>.<domain>. Off by default. |
networks | object[] | No | Private networks to join at create time. Each entry: {"id": "<name|net-ulid>"}. |
disks | object[] | No | S3 disks to mount. Each entry: {"disk_id": "<name|id>", "mount_path": "/mnt/data", "sub_path": "optional/prefix"}. Disk must be pre-registered via POST /v1/disks. |
egress | string[] | No | Outbound allowlist. Each entry is host[:port], ip[:port], cidr[:port], or *. Omit / empty / ["*"] = allow all. |
auto_pause_after_seconds | integer | No | Pause after this many seconds of inactivity (no exec, file transfer, or tunnel). Range: 60-86400. Null/omitted = never auto-pause. |
host_id | string | No | Pin to a specific host. 503 if the host can't fit the VM. |
region | string | No | Placement region. Omit to use the receiving API's default region. Requests for another configured region are forwarded there. An unavailable region returns 503; an unreachable regional service returns 502. |
When enabled for the account, an unpinned ordinary create can fall back from eu to us (or vice versa) after a capacity rejection. Explicit region/host placement, disks, networks and snapshot restores prevent this fallback. Check the returned sandbox region; set region explicitly when location matters.
Note: bandwidth_quota_bytes is not settable at create time. Each sandbox starts with a deployment-configured allowance; the software default is 50 GiB. Read /bandwidth for your sandbox's actual quota and use POST /v1/sandboxes/{id}/bandwidth/recharge to increase it once the sandbox is running.
Snapshots and labels
Use snapshot_id to restore an owned ready snapshot; it cannot be combined with rootfs. Disk size defaults to the captured size. Disk snapshots may grow to an allowed size, but cannot shrink; full snapshots require the captured CPU, memory and disk size.
The optional labels object stores string key/value metadata, for example {"team":"backend","environment":"test"}. Labels are returned in sandbox views. Limits: 100 entries, non-empty keys up to 128 bytes, values up to 1,024 bytes, and 65,536 bytes total including overhead. Keys and values cannot contain NUL bytes. Labels are metadata, not secrets or placement selectors.
Example request
curl -X POST https://api.sb.createos.sh/v1/sandboxes \
-H "X-Api-Key: $CREATEOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"shape": "s-1vcpu-1gb",
"rootfs": "devbox:1",
"name": "my-sandbox",
"ingress_enabled": true,
"auto_pause_after_seconds": 600,
"ssh_pubkeys": ["ssh-ed25519 AAAA..."],
"envs": { "ANTHROPIC_API_KEY": "sk-ant-..." },
"egress": ["pypi.org", "1.1.1.1:53"]
}'Example response
{
"status": "success",
"data": {
"id": "sb-01K...",
"name": "my-sandbox",
"ip": "192.168.0.12",
"shape": "s-1vcpu-1gb",
"rootfs": "devbox:1",
"vcpu": 1,
"mem_mib": 1024,
"disk_mib": 10240,
"spawn_ms": 87.4,
"egress": ["pypi.org", "1.1.1.1:53"],
"bandwidth_quota_bytes": 53687091200
}
}Notable errors: 400 invalid body or disabled rootfs, 503 no host capacity.
GET /v1/sandboxes
List sandboxes owned by the caller (paginated).
Auth required: Yes
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results per page (maximum 500) |
labels | object encoded as JSON | Match every supplied key/value pair (AND). URL-encode the JSON query value. | |
offset | integer | 0 | Pagination offset |
status | string | Filter by a sandbox lifecycle status, including paused, pausing, resuming, forking, and error. Omit to list all statuses. |
Example request
curl "https://api.sb.createos.sh/v1/sandboxes?status=running&limit=10" \
-H "X-Api-Key: $CREATEOS_API_KEY"Example response
{
"status": "success",
"data": {
"data": [
{
"id": "sb-01K...",
"name": "my-sandbox",
"status": "running",
"ip": "192.168.0.12",
"vcpu": 1,
"mem_mib": 1024,
"disk_mib": 10240,
"shape": "s-1vcpu-1gb",
"rootfs": "devbox:1",
"region": "eu",
"ingress_enabled": true,
"auto_pause_after_seconds": 600,
"created_at": "2026-06-17T08:00:00Z"
}
],
"pagination": {
"total": 1,
"limit": 10,
"offset": 0,
"count": 1
}
}
}GET /v1/sandboxes/{id}
Get details for a single sandbox.
Auth required: Yes
Path parameters
| Parameter | Description |
|---|---|
id | Sandbox id (sb-<ulid>) |
Example request
curl https://api.sb.createos.sh/v1/sandboxes/sb-01K... \
-H "X-Api-Key: $CREATEOS_API_KEY"Example response
{
"status": "success",
"data": {
"id": "sb-01K...",
"name": "my-sandbox",
"status": "running",
"ip": "192.168.0.12",
"vcpu": 1,
"mem_mib": 1024,
"disk_mib": 10240,
"shape": "s-1vcpu-1gb",
"rootfs": "devbox:1",
"region": "eu",
"ingress_enabled": true,
"bandwidth_ingress_bytes": 1048576,
"auto_pause_after_seconds": 600,
"envs": ["ANTHROPIC_API_KEY"],
"ssh_pubkeys": ["ssh-ed25519 AAAA..."],
"egress": ["pypi.org"],
"created_at": "2026-06-17T08:00:00Z",
"running_at": "2026-06-17T08:00:01Z"
}
}Notable errors: 404 sandbox not found.
envs returns only the key names; values are never exposed via the API.
Lifecycle fields such as paused_at, last_resumed_at, and forked_from are omitted when unset.
PATCH /v1/sandboxes/{id}
Partially update a sandbox. Only fields you include are changed. labels merges supplied key/value pairs into the existing map; matching keys are overwritten and other labels remain. An empty map does not clear labels. The merged map must meet the limits above. Capture conflicts return 409; wait for the capture to release the source before retrying.
curl -G https://api.sb.createos.sh/v1/sandboxes \
-H "X-Api-Key: $CREATEOS_API_KEY" \
--data-urlencode 'labels={"team":"backend"}'
curl -X PATCH "https://api.sb.createos.sh/v1/sandboxes/$SANDBOX_ID" \
-H "X-Api-Key: $CREATEOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"labels":{"environment":"production"}}'Auth required: Yes
Path parameters
| Parameter | Description |
|---|---|
id | Sandbox id |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
ingress_enabled | boolean | No | Toggle public ingress on or off |
auto_pause_after_seconds | integer | No | New idle-pause timeout. Range: 60-86400. Omit to leave unchanged. |
disable_auto_pause | boolean | No | Set true to turn off auto-pause entirely. Takes precedence over auto_pause_after_seconds when both are sent. |
Example request
curl -X PATCH https://api.sb.createos.sh/v1/sandboxes/sb-01K... \
-H "X-Api-Key: $CREATEOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ingress_enabled": false, "auto_pause_after_seconds": 1800}'Example response
Returns the full updated SandboxView object (same shape as GET).
Notable errors: 400 invalid values, 404 not found.
DELETE /v1/sandboxes/{id}
Destroy a sandbox permanently.
Auth required: Yes
Path parameters
| Parameter | Description |
|---|---|
id | Sandbox id |
Example request
curl -X DELETE https://api.sb.createos.sh/v1/sandboxes/sb-01K... \
-H "X-Api-Key: $CREATEOS_API_KEY"Example response
{
"status": "success",
"data": {
"id": "sb-01K...",
"status": "destroying"
}
}Idempotent: deleting an already-destroyed sandbox returns the same shape with status: "destroyed". A fresh delete returns "destroying" and completes within a few seconds.
Notable errors: 404 not found.
GET /v1/sandboxes/by-ip/{ip}
Look up a sandbox by its VM IP address.
Auth required: Yes
Path parameters
| Parameter | Description |
|---|---|
ip | VM IP address (e.g. 192.168.0.12) |
Example request
curl https://api.sb.createos.sh/v1/sandboxes/by-ip/192.168.0.12 \
-H "X-Api-Key: $CREATEOS_API_KEY"Example response
Returns a SandboxView object (same shape as GET /v1/sandboxes/{id}).
Notable errors: 404 no sandbox found with that IP.