REST API reference
Every Hotcell daemon endpoint: auth, sandboxes, exec over SSE, files, processes, ports, sessions, code contexts, backups, metrics and egress tokens.
The daemon serves JSON over HTTP on http://127.0.0.1:4750 by default. Request and response bodies are JSON unless noted. Streaming endpoints use Server-Sent Events.
Authentication
Every request needs the API key, except GET /healthz, the dashboard HTML (/ and /dashboard) and GET /info. Send it as either header:
Authorization: Bearer <key>
X-API-Key: <key>
The key is HOTCELL_API_KEY, or the one the daemon generates in ${HOTCELL_HOME:-~/.hotcell}/api-key (mode 0600) when that variable is unset.
curl -s http://127.0.0.1:4750/sandboxes -H "Authorization: Bearer $(cat ~/.hotcell/api-key)"
Other request checks:
- Host guard. When the daemon binds to loopback, requests whose
Hostheader isn't a loopback name,HOTCELL_HOSTor an entry inHOTCELL_ALLOWED_HOSTSget403. - Cross-site guard. Browser requests marked cross-site (
Sec-Fetch-Siteor a foreignOrigin) get403. - Body size. Bodies over
HOTCELL_MAX_BODY_BYTES(default 32 MiB) get413.
Status codes
| Code | Meaning |
|---|---|
200, 201 |
Success. |
400 |
Invalid input (missing field, unknown driver, bad policy). |
401 |
Missing or invalid API key. |
403 |
Host or cross-site guard. |
404 |
Unknown sandbox, process, session, context, backup, token or route. |
409 |
Sandbox is creating, error or stopped for an operation that needs it live. |
413 |
Body too large. |
422 |
Provisioning failed during a blocking create. |
503 |
Admission refused (host memory budget exhausted), with Retry-After: 5. Also /healthz when the driver is unreachable. |
Errors are { "error": "<message>" }.
Daemon
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
{ ok, driver, version }; 503 if the driver ping fails. |
GET |
/info |
Version, driver, drivers, defaultImage, proxyPort, egressPort, egressProviders, cost rates, defaultSleepAfterMs, auth, otlp, default limits. |
GET |
/capacity |
Admission snapshot: memory, cpu, running, fits, pool, and boot (microVM boot queue). |
GET |
/traces?limit=N |
Recent finished spans, newest first (default 100). |
POST |
/reload-keys |
Reload provider keys without a restart. The CLI calls this after hotcell keys changes. |
GET |
/, /dashboard |
The web dashboard. |
Sandboxes
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes |
Create a sandbox. Returns 201 with the record. |
GET |
/sandboxes |
{ sandboxes: [...] }. |
GET |
/sandboxes/:id |
The sandbox record. |
DELETE |
/sandboxes/:id |
Destroy, including its volume, sessions and egress tokens. Allowed in any state. |
POST |
/sandboxes/:id/stop |
Stop. |
POST |
/sandboxes/:id/start |
Start or resume. Admission-gated. |
POST |
/sandboxes/:id/pause |
Pause (memory snapshot on microVM drivers). |
Create body
| Field | Type | Description |
|---|---|---|
image |
string | Default HOTCELL_IMAGE. |
driver |
string | container, firecracker or applevz. Default HOTCELL_DRIVER. |
env |
object | Sandbox env. |
labels |
object | Labels. |
persist |
boolean | Default true. |
sleepAfter |
number | Idle auto-pause in ms. Default HOTCELL_SLEEP_AFTER_MS. |
setup |
string[] | Commands run once after start. |
repo |
string | Git URL cloned into /workspace. |
repoRef |
string | Branch or tag to check out. |
branch |
string | New branch after clone; auto generates one. |
memoryMb, cpus, pidsLimit |
number | Hard caps. Default to the daemon defaults. memoryMb must be at least 6. |
egress |
boolean or object | true, or an egress policy object. |
egressSpendCapUsd |
number | Per-sandbox LLM spend ceiling. |
detach |
boolean | Respond immediately with status creating; poll GET /sandboxes/:id. |
networked |
boolean | Firecracker: NAT network interface. |
writableRootfs |
boolean | Firecracker: private writable rootfs without a network device. |
cpuset |
string | Firecracker: pin the VMM to a CPU list such as "0-7". |
The record includes id, image, status, statusReason, createdAt, driver, labels, env, persist, lastActivityAt, sleepAfterMs, limits, egressSpendCapUsd and usage.
Exec and terminal
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/exec |
Body { command, cwd?, env?, sessionId? }. SSE stream of stdout, stderr and exit events. |
GET (WebSocket) |
/sandboxes/:id/terminal?cols=&rows=&key= |
Interactive PTY. Binary frames are stdin and output; text frames are JSON control messages ({"type":"resize","cols":N,"rows":N}). |
Exec frames:
data: {"type":"stdout","data":"..."}
data: {"type":"stderr","data":"..."}
data: {"type":"exit","exitCode":0}
The stream carries an SSE comment (: hb) every 15 seconds while idle.
Files
| Method | Path | Body | Response |
|---|---|---|---|
POST |
/sandboxes/:id/files/write |
{ path, content, mode? } |
{ ok: true } |
POST |
/sandboxes/:id/files/read |
{ path } |
{ content } |
POST |
/sandboxes/:id/files/mkdir |
{ path, parents? } |
{ ok: true } |
POST |
/sandboxes/:id/files/list |
{ path } |
{ entries: [{ path, name, isDirectory, size, modifiedAt }] } |
GET |
/sandboxes/:id/watch?path=&interval= |
SSE of { type, path } where type is created, modified or deleted. Default path /workspace. |
mode is a 3- or 4-digit octal string.
Processes and ports
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/processes |
Body { command, cwd?, env? }. Returns 201 with a process handle. |
GET |
/sandboxes/:id/processes |
{ processes: [...] }. |
DELETE |
/sandboxes/:id/processes/:procId |
Body { signal? }. Default SIGTERM. |
GET |
/sandboxes/:id/processes/:procId/logs?follow=1 |
SSE of { type: "log", data }, ending with { type: "end" }. |
POST |
/sandboxes/:id/wait-port |
Body { port, timeoutMs?, intervalMs?, host? }. Returns { ready }. |
POST |
/sandboxes/:id/expose |
Body { port, token? }. Returns 201 with { port, exposeId, token, createdAt, url }. |
GET |
/sandboxes/:id/expose |
{ exposed: [...] }. |
DELETE |
/sandboxes/:id/expose/:port |
Remove an exposed port. |
Sessions and environment
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/sessions |
Body { id?, cwd?, env? }. Default cwd /workspace. 409 if the id exists. |
GET |
/sandboxes/:id/sessions |
{ sessions: [...] }. |
POST |
/sandboxes/:id/sessions/:sessionId/env |
Body { env }; merged. |
DELETE |
/sandboxes/:id/sessions/:sessionId |
Delete a session. |
GET |
/sandboxes/:id/env |
{ env }. |
POST |
/sandboxes/:id/env |
Body { env }; merged into the sandbox env. |
Code interpreter
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/code-contexts |
Body { language? } (python default, or javascript). Returns 201 with { contextId, language, createdAt }. |
GET |
/sandboxes/:id/code-contexts |
{ contexts: [...] }. |
DELETE |
/sandboxes/:id/code-contexts/:contextId |
Destroy a context and its kernel. |
POST |
/sandboxes/:id/run-code |
Body { code, contextId?, language?, timeoutMs? } (default timeout 30000 ms). Returns { stdout, stderr, results, error }. |
Backups
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/backups |
Snapshot /workspace. Returns 201 with { backupId, sandboxId, createdAt, bytes }. |
GET |
/sandboxes/:id/backups |
This sandbox's backups. |
POST |
/sandboxes/:id/restore |
Body { backupId }. Replaces /workspace. |
GET |
/backups |
All backups. |
DELETE |
/backups/:backupId |
Delete a backup. |
Metrics
| Method | Path | Description |
|---|---|---|
GET |
/sandboxes/:id/metrics?live=0 |
{ status, live, usage, cost }. live is present only while running; live=0 skips the live snapshot. Doesn't count as activity. |
GET |
/sandboxes/:id/metrics/history |
{ samples: [{ at, cpuPercent, memBytes, netRxBytes, netTxBytes, pids }] }. |
Egress tokens
| Method | Path | Description |
|---|---|---|
POST |
/sandboxes/:id/egress-tokens |
Body is an optional policy: ttlMs, expiresAt, spendCapUsd, rateLimit: { calls?, tokens?, windowMs }, models, providers. Returns 201 with { token, policy, providers }. |
GET |
/sandboxes/:id/egress-tokens |
{ tokens: [{ token, policy, spendUsd, spendRemaining }], providers }. |
DELETE |
/sandboxes/:id/egress-tokens/:token |
Revoke. |
See Egress for the gateway itself, which runs on a separate port.
Auto-resume
Operations that need a live sandbox (exec, files, processes, ports, code, backups, terminal) resume a paused sandbox first. If the resume doesn't fit the host budget, they return 503. A stopped sandbox returns 409 until you call /start.