> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nanovm.dev.lithosai.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP API

> Every route, the sandbox object, snapshots, exports, templates, registries, usage, and every status code.

Base URL `https://sk.nanovm.dev.lithosai.cloud`, `Authorization: Bearer <key>`.
JSON bodies; times are `*_unix_ns`. (The archive/unarchive verbs travel as the
original `/stop` and `/start` routes.)

## Sandboxes

| Route                                                              | Body                                                                                                                                                                                                                                                                                             | Response                                                                                                                                                                                                                                                                                                    |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /vms`                                                        | `image` **or** `template` (name, `name:version`, or template id) **or** `snapshot_id`; optional `cpus`, `memory_mb`, `writable_size_bytes`, `disable_internet`, `runtime` (`container`/`vm`). Empty body = default sandbox.                                                                      | `201` sandbox object                                                                                                                                                                                                                                                                                        |
| `GET /vms` · `GET /vms/{id}` · `DELETE /vms/{id}`                  | —                                                                                                                                                                                                                                                                                                | `{"vms": […]}` · sandbox object · `204`                                                                                                                                                                                                                                                                     |
| `POST /vms/{id}/exec`                                              | `{"args": ["cmd", "…"], "stdin_b64": "<base64, ≤4 MiB>"}` — or session form `{"session_id", "command", "close_session"}`                                                                                                                                                                         | `{"exit_code", "stdout", "stderr"}`; session form: cwd/env/background processes persist per `session_id`                                                                                                                                                                                                    |
| `POST /vms/{id}/pause` · `/resume`                                 | —                                                                                                                                                                                                                                                                                                | `204`                                                                                                                                                                                                                                                                                                       |
| `POST /vms/{id}/sleep`                                             | —                                                                                                                                                                                                                                                                                                | `{"slept": true}`; any later call wakes it                                                                                                                                                                                                                                                                  |
| `POST /vms/{id}/stop` (= archive)                                  | —                                                                                                                                                                                                                                                                                                | `{"stopped": true}`; memory + disk archived durably                                                                                                                                                                                                                                                         |
| `POST /vms/{id}/start` (= unarchive)                               | —                                                                                                                                                                                                                                                                                                | `{"started": true, "node": "…"}`                                                                                                                                                                                                                                                                            |
| `POST /vms/{id}/reboot`                                            | —                                                                                                                                                                                                                                                                                                | updated sandbox object; keeps the disk, kills processes/sessions, new guest IP, exposed ports re-pointed; `409` on an archived sandbox (unarchive first)                                                                                                                                                    |
| `POST /vms/{id}/snapshot`                                          | `{"leave_paused": false}`                                                                                                                                                                                                                                                                        | `201 {"snapshot_id", "vm_id", "parent_snapshot_id", "state", "image", "created_unix_ns"}`                                                                                                                                                                                                                   |
| `POST /vms/{id}/branch` (= fork)                                   | optional `{"count": N}` (2–4 at launch); a single fork may carry `child_vm_id` (client UUID; retry-safe). A fork is a live checkpoint: the child resumes the parent's running processes and open connections; forking mid-command can leave the child unhealthy for \~1–2 s — fork between calls | `201` sandbox object; with `count`, `{"vms": […]}` — N siblings from one point in time                                                                                                                                                                                                                      |
| `GET /vms/{id}/snapshots`                                          | —                                                                                                                                                                                                                                                                                                | `{"snapshots": […]}`                                                                                                                                                                                                                                                                                        |
| `GET /vms/{id}/events?limit=`                                      | —                                                                                                                                                                                                                                                                                                | `{"events": [{"kind", "detail", "node", "at"}, …]}` newest first — the lifecycle timeline; survives delete until retention                                                                                                                                                                                  |
| `GET /vms/{id}/logs?tail_bytes=`                                   | —                                                                                                                                                                                                                                                                                                | `{"content", "truncated"}` — serial console tail (≤256 KiB); never wakes the guest; archived sandboxes answer 404 (no live console)                                                                                                                                                                         |
| `GET /vms/{id}/metrics?since_unix=&max_samples=`                   | —                                                                                                                                                                                                                                                                                                | `{"samples": [{"at_unix", "interval_s", "cpu_ms", "mem_mb", "rx_bytes", "tx_bytes", "state"}, …], "cpus"}` — 30 s host-side samples, \~2 h kept                                                                                                                                                             |
| `GET /vms/{id}/metrics/history?hours=` (or `start=&end=`, RFC3339) | —                                                                                                                                                                                                                                                                                                | `{"points": […]}` — per-minute ledger history (≤ 15 days back; `hours` 1–360); works for archived and deleted sandboxes; a point with `"measured": false` is a gap, not a zero                                                                                                                              |
| `GET /audit?limit=&before=`                                        | —                                                                                                                                                                                                                                                                                                | `{"records": [{"id", "key_id", "action", "resource", "outcome", "request_id", "at"}, …], "next_before"}` — the org's mutating API calls, newest first                                                                                                                                                       |
| `POST /vms/{id}/expose`                                            | `{"guest_port": 8080, "public_port": 0}`                                                                                                                                                                                                                                                         | `201 {"ingress_id", "guest_port", "gateway_node", "public_port", "public_url"}` — live once the guest runs; exposing an archived sandbox is allowed (live on unarchive); a deleted sandbox's URL can take \~30 s to fail fast; across archive/unarchive the URL may 404 briefly while the route re-installs |
| `GET /vms/{id}/ingress` · `DELETE /ingress/{iid}`                  | —                                                                                                                                                                                                                                                                                                | `{"ingresses": […]}` · `204`                                                                                                                                                                                                                                                                                |

**Sandbox object:** `vm_id`, `tenant`, `state` (`creating`, `running`,
`paused` — you called `pause`, only `resume` undoes it; `slept` — the idle
policy paused it, the next call wakes it; `archived` — parked to object
storage: after your `archive()` only `unarchive()` undoes it
(`SandboxStoppedError` otherwise); when the idle policy parked it, the next
call wakes it in seconds; `interrupted` — its host died, contact support to
recover from a durable checkpoint; `migrating`, `deleting`, `failed`), `image`,
`cpus`, `memory_mb`, `guest_ip`, `runtime`, `created_unix_ns`, `parent_vm_id`
(set on a fork child), `node`, `shard`, `disable_internet`. While archived,
`node`/`guest_ip`/`mgmtd_addr` are last-known values, not a live placement.

## Snapshots and exports

| Route                                              | Response                                                                                                                                                                                                                                                                                                                                                                                     |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /snapshots`                                   | `{"snapshots": [{"snapshot_id", "vm_id", "parent_snapshot_id", "state", "durable", "image", "logical_bytes", "created_unix_ns"}]}` — `durable` flips `true` when the upload finishes (seconds); restore works before that, export does not                                                                                                                                                   |
| `GET /snapshots/{sid}` · `DELETE /snapshots/{sid}` | snapshot object · `204`                                                                                                                                                                                                                                                                                                                                                                      |
| `POST /snapshots/{sid}/export`                     | `202 {"export_id"}`; `409 snapshot is not durable yet` until `durable` (retry in seconds). **Experimental at launch.**                                                                                                                                                                                                                                                                       |
| `GET /exports/{eid}`                               | `{"export_id", "snapshot_id", "state", …}`; `pending` → `ready` (`files` = `memory.img.zst`, `disk.img.zst` — zstd; each entry has `bytes` + `raw_bytes` — plus `state.json`, `manifest.json` v2, presigned, own TTL) or `failed` (with the scrubbed reason after three attempts; SDK `exports.wait(eid)` raises `ExportFailed`). Deleting the snapshot afterwards does not revoke an export |

## Templates

| Route                                              | Body                                                                                    | Response                                                                                                                                                                                         |
| -------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `POST /templates`                                  | `{"name", "image", "version"?, "cpus"?, "memory_mb"?, "disable_internet"?, "runtime"?}` | `201` template object (status `building`)                                                                                                                                                        |
| `GET /templates`                                   | —                                                                                       | `{"templates": […]}` — yours plus the shared public-image templates your creates used, marked `"shared": true` (read-only; a failed shared template is rebuilt by the next create of that image) |
| `GET /templates/{tid}` · `DELETE /templates/{tid}` | —                                                                                       | template object (also for a shared id a `503` handed you) · `204` (own templates only)                                                                                                           |

Template object: `template_id`, `name`, `version`, `source_image`, `status`
(`building` → `ready` | `failed`), `status_detail`, `runtime`, `base_snapshot_id`,
`created_unix_ns`, `last_used_unix_ns`.

## Registries

| Route                       | Body                                                                                                                                                                                                   |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `POST /registries`          | `{"host": "ghcr.io", "kind": "static", "username": "…", "secret": "…"}` or `{"host": "<acct>.dkr.ecr.<region>.amazonaws.com", "kind": "ecr-assume-role", "role_arn": "…", "external_id": "…"}` → `204` |
| `GET /registries`           | `{"registries": [{"host", "kind", "created_unix_ns", "updated_unix_ns"}]}` (never returns secrets)                                                                                                     |
| `DELETE /registries/{host}` | `204`                                                                                                                                                                                                  |

## Usage

`GET /usage?start=<RFC3339>&end=<RFC3339>` (default last 24 h) →
`{"start", "end", "states": [{"state", "vm_seconds", "cpu_seconds",
"memory_mb_seconds"}, …], "snapshot_byte_seconds"}` — per-state rows;
whole-minute samples (short-lived sandboxes round up); window capped at 32 days;
empty `states` is a valid no-usage answer. `snapshot_byte_seconds` integrates
held snapshots' logical bytes (deleted ones stop counting).
`GET /usage/series?hours=` (or `start=&end=`) is the activity-graph time series
behind the console's Usage charts. `GET /usage-export` is operator-only
(`USAGE_EXPORT_SUBJECTS`; normal keys get 403).

## Status codes

| Code                                                            | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`                                                           | malformed body or field, e.g. `image "…" is not a valid image reference`; also one exec argument over 131,071 bytes (pass data via stdin — the SDK refuses client-side)                                                                                                                                                                                                                                                                                                                                                               |
| `401` / `403`                                                   | missing or invalid key / not permitted                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `404`                                                           | no such sandbox, snapshot or template in your organization                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `409`                                                           | precondition failed: template not ready, runtime disagrees with the template, an operation on an archived sandbox (`sandbox is stopped` → unarchive), a guest operation on a paused sandbox (`sandbox is paused` → `resume`; SDK `SandboxPausedError`), a checkpoint of a guest whose agent is not answering (`sandbox agent unreachable` → `reboot` or delete), a snapshot delete while an export is in progress, or — rarely — a guest RPC still in flight (retry)                                                                  |
| `429` + `Retry-After`, body `rate limited`                      | the per-organization request budget for this **class** (acquire = create/fork/restore; ops = everything else) is spent this minute; wait and retry. Fixed-window bucket per gateway replica (two today): each holds half the per-minute figure, refilled in full at its minute — a sustained load can pass up to 2× the nominal figure without a 429, and after a 429 the budget returns all at once. The nominal figures are the per-organization guarantee, not a ceiling. `x-ratelimit-*` headers are advisory, not a live counter |
| `403` `{"error":"quota","kind":"cap"}`                          | organization concurrent-sandbox cap: **20** running, creating, **paused and slept** per organization (they keep RAM/vCPU resident; archived ones don't count) — delete or archive first; waiting does not help (SDK `QuotaError.kind == "cap"`)                                                                                                                                                                                                                                                                                       |
| `503 sandbox agent unreachable`                                 | the guest stopped answering (fork bomb, OOM inside it); others unaffected — `reboot()` (keeps disk) or delete; archive would checkpoint and a starved guest cannot be checkpointed (SDK `SandboxUnhealthyError`)                                                                                                                                                                                                                                                                                                                      |
| `503` + `Retry-After: 30`, `{"error": "template building", …}`  | first use of this image at this shape; poll `GET /templates/{template_id}` until `ready`, then retry                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `503` `{"error":"capacity","kind":"placement"}` + `Retry-After` | no host can fit the shape right now (or a fork fan-out's parent host can't take all children); SDK `CapacityError` — transient, retry with backoff. Distinct from the cap (403) and rate limits (429)                                                                                                                                                                                                                                                                                                                                 |
| `503` with `connection error` / `Unavailable`                   | a control-plane component is restarting; the request did not reach the sandbox — retry in seconds                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `5xx`                                                           | platform failure; idempotent requests can be retried                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
