Managed Processes
Managed process resources let you start commands that keep a process ID inside a sandbox. Unlike /exec, a managed process can be listed, inspected, reattached to, sent input, waited on, signaled, resized when it is a PTY, and stopped later.
Use this API for long-running commands, reconnectable output, background jobs, persistent shell sessions, and commands that need lifecycle controls after they start.
At a glance
- Base URL:
https://api.sb.createos.sh - Auth:
X-Api-Key: <token>header. Get a token - Response envelope: Buffered responses use JSend,
{"status": "...", "data": ...} - Stream format:
/connectreturnsapplication/x-ndjson
When to use exec, process, or PTY
| Need | Use |
|---|---|
| Quick one-shot command | exec: POST /v1/sandboxes/{id}/exec |
| Reconnectable command with retained output | process: POST /v1/sandboxes/{id}/processes without pty |
| Background command you will inspect or stop later | process, then use GET, /connect, /wait, /signal, or DELETE |
| Interactive shell, REPL, curses/full-screen app, or command that checks for a terminal | PTY: include the pty object |
Pipe process resources keep stdout and stderr separate. PTY processes combine terminal output into a single pty stream and support resize.
Output retention
Managed process output is retained for replay and reconnect, but it is bounded:
| Limit | Value |
|---|---|
| Per process output journal | 1 MiB |
| Aggregate process output budget per sandbox | 32 MiB |
| Maximum single output event | 32 KiB |
When a process or sandbox exceeds these limits, the oldest retained output is discarded first. If you reconnect with an after sequence number older than the retained window, /connect returns 410 Gone with output_offset_expired and the oldest available sequence number.
cmd is one executable name or path and args contains its individual arguments. Commands are executed directly, without implicit shell parsing. For pipes, redirects, globbing, or shell syntax, run a shell explicitly:
JSON1{2 "cmd": "/bin/bash",3 "args": ["-lc", "echo hello > /tmp/out.txt"]4}
POST /v1/sandboxes/{id}/processes
Create a managed pipe process or PTY in a running sandbox.
Path parameters
| Parameter | Description |
|---|---|
id | Sandbox id |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
cmd | string | No for PTY, yes for pipe process | Program to execute. If omitted for PTY, the agent starts /bin/bash -i -l when Bash exists, otherwise /bin/sh -l. |
args | string[] | No | Argument list. |
cwd | string | No | Working directory inside the sandbox. |
env | object | No | Per-process environment overrides. |
pty | object | No | Include to create a PTY instead of separate stdout/stderr pipes. |
pty.rows | integer | No | Initial terminal rows. Defaults to 24 when omitted or zero. |
pty.cols | integer | No | Initial terminal columns. Defaults to 80 when omitted or zero. |
Create a pipe process
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes \2 -H "X-Api-Key: $CREATEOS_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{5 "cmd": "bash",6 "args": ["-lc", "echo hello; echo warning >&2"],7 "cwd": "/root",8 "env": { "EXAMPLE": "value" }9 }'
JSON1{2 "status": "success",3 "data": {4 "process_id": "proc_dGVzdF9wcm9jZXNzX2lkMQ",5 "kind": "process",6 "pid": 123,7 "state": "running",8 "leader_exited": false,9 "tree_exited": false,10 "created_at": "2026-08-19T12:00:00.123456Z",11 "finished_at": null,12 "exit_code": null,13 "output": {14 "oldest_seq": 0,15 "newest_seq": 0,16 "bytes": 017 }18 }19}
Create a PTY
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes \2 -H "X-Api-Key: $CREATEOS_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{5 "cmd": "/bin/bash",6 "args": ["-i", "-l"],7 "cwd": "/root",8 "env": { "TERM": "xterm-256color" },9 "pty": { "rows": 24, "cols": 80 }10 }'
For a default shell PTY, the body can be as small as:
JSON1{2 "pty": {3 "rows": 24,4 "cols": 805 }6}
Both PTY create forms return a standard JSend response with kind: "pty":
JSON1{2 "status": "success",3 "data": {4 "process_id": "proc_cHR5X3Byb2Nlc3NfaWQ",5 "kind": "pty",6 "pid": 124,7 "state": "running",8 "leader_exited": false,9 "tree_exited": false,10 "created_at": "2026-08-19T12:00:01.123456Z",11 "finished_at": null,12 "exit_code": null,13 "output": {14 "oldest_seq": 0,15 "newest_seq": 0,16 "bytes": 017 }18 }19}
GET /v1/sandboxes/{id}/processes
List managed pipe processes and PTYs in a sandbox.
Bash1curl https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes \2 -H "X-Api-Key: $CREATEOS_API_KEY"
JSON1{2 "status": "success",3 "data": {4 "processes": [5 {6 "process_id": "proc_dGVzdF9wcm9jZXNzX2lkMQ",7 "kind": "process",8 "pid": 123,9 "state": "running",10 "leader_exited": false,11 "tree_exited": false,12 "created_at": "2026-08-19T12:00:00.123456Z",13 "finished_at": null,14 "exit_code": null,15 "output": {16 "oldest_seq": 1,17 "newest_seq": 2,18 "bytes": 1419 }20 }21 ]22 }23}
GET /v1/sandboxes/{id}/processes/{process_id}
Inspect one managed process.
Bash1curl https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123 \2 -H "X-Api-Key: $CREATEOS_API_KEY"
JSON1{2 "status": "success",3 "data": {4 "process_id": "proc_123",5 "kind": "process",6 "pid": 123,7 "state": "exited",8 "leader_exited": true,9 "tree_exited": true,10 "created_at": "2026-08-19T12:00:00.123456Z",11 "finished_at": "2026-08-19T12:00:02.456789Z",12 "exit_code": 0,13 "output": {14 "oldest_seq": 1,15 "newest_seq": 2,16 "bytes": 1417 }18 }19}
Possible lifecycle states are starting, running, terminating, exited, and failed.
For a signal exit, exit_code is null and signal carries the signal name.
Output summary fields
Create, list, get, wait, and delete responses include an output summary:
JSON1"output": {2 "oldest_seq": 1,3 "newest_seq": 2,4 "bytes": 145}
| Field | Description |
|---|---|
oldest_seq | Oldest retained output event sequence number currently available for replay. |
newest_seq | Newest retained output event sequence number currently available for replay. |
bytes | Retained output bytes currently held for this process. |
Use these fields to decide where to attach. For example, pass ?after=<newest_seq> to follow only new output, or pass ?after=0 to replay everything still retained. If you request output before oldest_seq, /connect returns 410 Gone with output_offset_expired.
GET /v1/sandboxes/{id}/processes/{process_id}/connect
Replay and follow ordered output events.
Bash1curl -N https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123/connect \2 -H "X-Api-Key: $CREATEOS_API_KEY"
Response headers:
HTTP1Content-Type: application/x-ndjson2Cache-Control: no-store
Pipe-process output preserves stdout and stderr as separate ordered events:
JSON1{"type":"data","seq":1,"stream":"stdout","data_base64":"aGVsbG8K"}2{"type":"data","seq":2,"stream":"stderr","data_base64":"d2FybmluZwo="}3{"type":"exit","exit_code":0}
PTY output is a single terminal stream:
JSON1{"type":"data","seq":1,"stream":"pty","data_base64":"cm9vdEBzYW5kYm94OiNfIA=="}
Idle streams emit heartbeats:
JSON1{"type":"heartbeat"}
To reconnect after output you already consumed, pass the last sequence number:
HTTP1GET /v1/sandboxes/sb-01K.../processes/proc_123/connect?after=42
Only events with seq > 42 are replayed. If the requested offset has been evicted from the retained output window, the API returns 410 Gone with output_offset_expired.
POST /v1/sandboxes/{id}/processes/{process_id}/input
Write bytes to pipe stdin or PTY input.
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123/input \2 -H "X-Api-Key: $CREATEOS_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{"data_base64": "aGVsbG8gd29ybGQK"}'
JSON1{2 "status": "success",3 "data": {4 "input_seq": 15 }6}
input_seq increases for each successful non-empty write. Concurrent writes are serialized in submission order. Each decoded input request is limited to 256 KiB.
POST /v1/sandboxes/{id}/processes/{process_id}/stdin/close
Close stdin for a pipe process. This operation is only valid for pipe processes. A PTY returns 409 wrong_process_kind.
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123/stdin/close \2 -H "X-Api-Key: $CREATEOS_API_KEY"
JSON1{2 "status": "success",3 "data": {}4}
POST /v1/sandboxes/{id}/processes/{process_id}/resize
Resize a managed PTY. A pipe process returns 409 wrong_process_kind.
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123/resize \2 -H "X-Api-Key: $CREATEOS_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{"rows": 40, "cols": 120}'
JSON1{2 "status": "success",3 "data": {}4}
POST /v1/sandboxes/{id}/processes/{process_id}/signal
Send a signal to a pipe process group or PTY foreground process group.
Bash1curl -X POST https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123/signal \2 -H "X-Api-Key: $CREATEOS_API_KEY" \3 -H "Content-Type: application/json" \4 -d '{"signal": "SIGINT"}'
JSON1{2 "status": "success",3 "data": {}4}
Supported signals are SIGHUP, SIGINT, SIGQUIT, SIGKILL, SIGTERM, SIGUSR1, SIGUSR2, and SIGWINCH.
GET /v1/sandboxes/{id}/processes/{process_id}/wait
Wait for a managed process to exit.
| Query parameter | Description |
|---|---|
scope | leader waits for the top-level process. tree waits until every process it started has also exited. Defaults to leader. |
timeout_ms | Optional long-poll timeout in milliseconds. Omitted or zero uses the server's 30-second deadline. Maximum is 30000. |
HTTP1GET /v1/sandboxes/sb-01K.../processes/proc_123/wait?scope=tree&timeout_ms=30000
JSON1{2 "status": "success",3 "data": {4 "process_id": "proc_123",5 "kind": "process",6 "pid": 123,7 "state": "exited",8 "leader_exited": true,9 "tree_exited": true,10 "created_at": "2026-08-19T12:00:00.123456Z",11 "finished_at": "2026-08-19T12:00:02.456789Z",12 "exit_code": 0,13 "output": {14 "oldest_seq": 1,15 "newest_seq": 5,16 "bytes": 51217 }18 }19}
If the wait times out while the process is still running, the API returns 408 wait_timeout. Call wait again to continue.
DELETE /v1/sandboxes/{id}/processes/{process_id}
Terminate a managed process and everything it started.
| Query parameter | Description |
|---|---|
grace_ms | Milliseconds to wait after SIGTERM before force-killing remaining descendants. Defaults to 1000; valid range is 0 to 60000. |
Bash1curl -X DELETE "https://api.sb.createos.sh/v1/sandboxes/sb-01K.../processes/proc_123?grace_ms=1000" \2 -H "X-Api-Key: $CREATEOS_API_KEY"
JSON1{2 "status": "success",3 "data": {4 "process_id": "proc_123",5 "kind": "process",6 "pid": 123,7 "state": "exited",8 "leader_exited": true,9 "tree_exited": true,10 "created_at": "2026-08-19T12:00:00.123456Z",11 "finished_at": "2026-08-19T12:01:00.123456Z",12 "exit_code": null,13 "signal": "SIGTERM",14 "output": {15 "oldest_seq": 1,16 "newest_seq": 5,17 "bytes": 51218 }19 }20}
The agent sends SIGTERM, waits for the grace period, kills the process cgroup if descendants remain, waits for the complete tree to exit, and returns final process details. Repeated deletion is idempotent while the process record exists.
Common errors
| Status | Meaning |
|---|---|
400 | Invalid request, process ID, signal, wait options, or grace period |
404 | Sandbox or managed process not found |
408 | Wait/control operation timed out or was cancelled |
409 | Sandbox not running, wrong process kind, process exited, or stdin closed |
410 | Requested output sequence was evicted from the replay journal |
429 | Per-sandbox managed-process limit reached |
502 | Owning host or guest process agent unavailable |
Common failure codes include process_not_found, wrong_process_kind, process_exited, stdin_closed, and process_limit_reached.