GET /v1/sandboxes/{id}/attach upgrades to a WebSocket and runs one process in a running sandbox. Use it for commands longer than the 55-second limit of POST /v1/sandboxes/{id}/exec, for more than 1 MiB of stdin, for streamed output, and for interactive shells. zode ssh, zode exec and the SDK’s spawn use it.
{id} is the sandbox’s sbx_ id. Authenticate with the Authorization: Bearer zode_tok_... header. The socket is for servers and CLIs: a request whose Origin is not the API’s own host gets 403 forbidden, so browser pages cannot open it. Keys with the read_only scope cannot attach.
Query parameters
argv runs as given, with no shell. To run a shell line, send argv=bash&argv=-lc&argv=npm ci && npm test (URL-encoded). The process runs as root.
Environment header
Extra variables go in theX-Zode-Env header, never in the URL, because URLs end up in logs. The value is the unpadded base64url encoding of a JSON array of KEY=VALUE strings:
^[A-Za-z_][A-Za-z0-9_]{0,127}$. At most 200 entries and 64 KB. The process gets a root login environment, then the sandbox’s environment, then these variables. A malformed header gets 400 invalid_request.
Before the upgrade
If the API refuses the session, it answers with an HTTP status and the usual JSON error instead of upgrading:Frames
Every message is binary. The first byte is the frame type, as an ASCII character; the rest is the payload.
A client message can be at most 1 MiB; send large stdin in chunks (the CLI and SDK use 32 KiB). Empty messages and unknown frame types are ignored.
The
q payload:
Ending a session
- The process exits: you get
q, then a normal close (code 1000). - The session fails: you get
!, then a normal close. - You close the socket: the process is killed. There is no detach.
- The socket closes with neither
qnor!: treat the outcome as unknown.
e once you have no more input. A command that reads stdin, such as cat or npm init, waits until it gets end-of-file. The API pings the client every 30 seconds to keep idle sessions open through load balancers; WebSocket libraries answer pings for you.
Example with Node and ws
hello on stdout and oops on stderr, and exits 3.
With the SDK
The TypeScript SDK wraps this protocol.sandbox.spawn(command, options) opens a session and returns a handle: iterate it for output chunks, call sendStdin, closeStdin, resize and kill, and await handle.wait() for the exit result. See the SDK’s exec page.