Skip to main content
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 the X-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:
Keys match ^[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 q nor !: treat the outcome as unknown.
Send 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

This prints 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.