inis.run
API reference

Sessions

Persistent sandboxes that survive across agent tool calls.

Sessions are the primary primitive for agents. Create once, call exec/files/expose many times, destroy when done.

All requests require Authorization: Bearer inis_... and Content-Type: application/json. Path parameter {id} is the session ID.

Prefer the CLI (inis sessions, inis exec --session) or an SDK for day-to-day use.

Create

POST/v1/sessions

Request body

{
  "size": "medium",
  "volume_id": "vol_...",
  "max_lifetime_ms": 3600000,
  "idle_timeout_ms": 300000,
  "on_detach": "pause",
  "paused_ttl_ms": 86400000
}
FieldRequiredDescription
sizenosmall (default), medium, or large
volume_idnoAttach a persistent volume
max_lifetime_msnoHard cap on session lifetime
idle_timeout_msnoAuto-pause after idle period
on_detachnoWhat to do when the last client detaches: pause (default), keep_live, or destroy
paused_ttl_msnoAuto-destroy a paused session after this long. Omit/0 for the server default; negative for never

Response 201

{
  "session_id": "2476b0a6-722e-4bd9-9f25-771ccbdd53bd",
  "state": "live",
  "created_at": "2026-06-22T13:46:00Z"
}

Get

GET/v1/sessions/{id}

Response 200 — session metadata (session_id, state, volume_id, created_at, on_detach, paused_ttl_ms).

List

GET/v1/sessions

List your sessions, newest first. Filter with ?state= (live, paused, creating, or ended) and paginate with ?limit= and ?cursor=.

state=ended returns the history of destroyed sessions — terminal rows that carry ended_at and end_reason (client_destroy, shell_exit, on_detach_destroy, max_lifetime, paused_ttl, or error). History is metadata only; the session's disk and snapshot are already gone.

Destroy

DELETE/v1/sessions/{id}

Optionally pass ?reason= (client_destroy — the default — or shell_exit) to record why the session ended on its history row.

Response 204 — empty body.

Exec

POST/v1/sessions/{id}/exec

Request body

{
  "command": ["python3", "-c", "print(42)"],
  "cwd": "/workspace",
  "timeout_ms": 30000
}

Response 200

{
  "stdout": "42\n",
  "stderr": "",
  "exit_code": 0,
  "duration_ms": 41,
  "timed_out": false,
  "sandbox_id": "c0ea18af-3c3e-4491-8f17-5b7a8d4a65a6"
}

Files

POST/v1/sessions/{id}/files

Single endpoint; set op to read, write, or list.

Write

{
  "op": "write",
  "path": "/workspace/out.txt",
  "content": "hello"
}

Read

{
  "op": "read",
  "path": "/workspace/out.txt"
}

Response includes "content": "hello".

List

{
  "op": "list",
  "path": "/workspace"
}

Response includes "entries": ["out.txt", "data.csv"].

Expose

POST/v1/sessions/{id}/expose

Start an HTTPS preview URL for a port inside the guest. The process must bind to 0.0.0.0, not 127.0.0.1.

Request body

{
  "port": 8080,
  "visibility": "token",
  "auth": "bearer"
}

auth controls access to the preview URL: none (default) leaves it open, bearer requires every inbound request to carry an Authorization: Bearer <auth_token> header.

Response 200

{
  "session_id": "2476b0a6-722e-4bd9-9f25-771ccbdd53bd",
  "port": 8080,
  "preview_url": "https://8080-2476b0a6-722e-4bd9-9f25-771ccbdd53bd.ams1.sb.inis.run",
  "guest_ip": "10.42.3.2",
  "auth": "bearer",
  "auth_token": "qN3kf2..."
}

When auth is bearer, auth_token is returned once here and never again — store it. Inbound requests without a matching Authorization: Bearer header are rejected with 401. The active mode shows up as auth on each entry in exposed_previews from a session fetch, but the token itself is never re-surfaced.

Preview URLs follow https://{port}-{session_id}.{node-domain}.

Pause and resume

POST/v1/sessions/{id}/pause
POST/v1/sessions/{id}/resume

Pause stops billing for compute; storage for the snapshot continues. Any exec/files/expose call on a paused session wakes it automatically, as does attaching with inis ssh.

Pause also happens on its own: when the last client detaches (if the session's on_detach is pause, the default) and after the idle_timeout_ms window. A paused session is auto-destroyed once it sits past its paused_ttl_ms.

Fork

POST/v1/sessions/{id}/fork

Clone a live session into N independent children — useful for parallel eval or RL rollouts.

Request body

{
  "count": 10
}

Response 200

{
  "parent_session_id": "2476b0a6-722e-4bd9-9f25-771ccbdd53bd",
  "children": ["...", "..."]
}

Checkpoints

A checkpoint is a named, retained snapshot of a live session. It survives the session's destruction and is deleted only when you ask. See Concepts for checkpoint vs fork vs pause.

POST/v1/sessions/{id}/checkpoints

Capture a checkpoint. The source session keeps running.

Request body

{
  "name": "clean"
}

Response 200

{
  "checkpoint_id": "ckpt_3f9a...",
  "parent_session_id": "...",
  "name": "clean",
  "size_bytes": 268435456,
  "created_at": "2026-06-23T10:00:00Z"
}
GET/v1/sessions/{id}/checkpoints

List checkpoints captured from a session.

GET/v1/checkpoints/{cid}

Fetch a checkpoint's metadata.

DELETE/v1/checkpoints/{cid}

Delete a checkpoint and free its disk.

Restore in place

POST/v1/sessions/{id}/restore

Roll a session back to a checkpoint, reusing the same session ID. The "run untrusted code, then roll back to clean" flow.

Request body

{
  "checkpoint_id": "ckpt_3f9a..."
}

New session from a checkpoint

POST/v1/checkpoints/{cid}/sessions

Start a fresh, independent session from a checkpoint (the template path). You can also pass from_checkpoint to POST /v1/sessions.

Lifecycle

creating → live ⇄ paused → destroyed → (ended history)
              ↑   ↓
       wake-on-op  auto-pause on detach / idle

live → paused happens on detach (on_detach: pause) or idle timeout; paused → live on any operation or attach. A session reaches destroyed explicitly, at max_lifetime, or when a paused session passes its paused_ttl — and leaves an ended history row behind either way.

See Concepts for when to use sessions vs stateless execute.

On this page