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 | Yes | VM shape. See GET /v1/shapes for the catalog. Example: s-1vcpu-256mb |
rootfs | string | No | Rootfs catalog name. Omit for host default. 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. |
Note: bandwidth_quota_bytes is not settable at create time. Each sandbox starts with a deployment-configured allowance; the software default is 5 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.
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": 5368709120
}
}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) |
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.
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.