Reference / Agent Sandboxes

Open a terminal

Upgrade to a WebSocket for a running sandbox's interactive terminal.

GET/v1/customer/tenants/{tenantId}/sandboxes/{sandboxId}/terminal

Base URL: https://api.runzivo.dev

Authentication

Send Authorization: Bearer <token> with a customer access token or workspace API key.

API key permission: sandboxes:write. These are permissions, not legacy scopes. A key with the all:all scope also works, limited by its creator's current workspace permissions.

A browser can instead use a single-use terminal ticket, without an Authorization header.

Parameters

Path and query parameters
FieldDescription
tenantIdstring · pathRequiredYour workspace's tenant ID. Replace the placeholder with an ID your token can access.
sandboxIdstring · pathRequiredThe id returned when the sandbox was created: sbx_ followed by 24 lowercase hexadecimal characters.
ticketstring · queryOptionalSingle-use browser terminal ticket. Supply this without Authorization; API and CLI clients send a bearer token instead.

Request body

No request body is required.

Behavior and limits

  • Requires a WebSocket client. The cURL example shows the HTTP upgrade request only; it is not an interactive terminal client.
  • Terminal bytes travel as binary frames in both directions, never base64. The only text frame is a resize object: {"type":"resize","cols":80,"rows":24}; each dimension must be 1–1000.
  • Answer server pings (every 30 seconds) with pong. Up to two terminals per sandbox. A new connection creates a new shell; a disconnected session does not resume.
  • Close codes: 1000 for normal/lifecycle closure or 30-minute idle timeout; 1003 invalid text frame; 1008 input_rate; 1009 frame over 64 KiB; 1011 server fault or output_overflow.

Responses

HTTP response codes
StatusMeaning
101 Switching ProtocolsWebSocket
400 Invalid requestMalformed JSON, unknown fields or invalid values. Terminal upgrades also reject malformed handshakes.
401 UnauthorizedMissing or invalid bearer token. A ticketed terminal request can instead return ticket_invalid.
403 ForbiddenThe caller lacks workspace membership or permission, or the API key is not allowed. Codes include tenant_membership_required, permission_required and api_key_scope_required.
404 Not foundNo sandbox with this ID exists in the authorized workspace.
409 ConflictThe operation is not allowed in the current sandbox state. See the operation notes for other possible conflict codes.
426 Upgrade requiredThe request did not contain a WebSocket upgrade.
500 Internal errorThe server could not complete the request.
503 UnavailableSandbox service is unavailable. Exec and terminal operations can also return sandbox_exec_unavailable.

Examples use synthetic IDs, dates and values. Read the actual response and status in your workspace.

Sandbox limits and billing →

Page tools

View as Markdown
Ask AI about this pageOpen in ChatGPTOpen in ClaudeOpen in Cursor

Claude shows a caution banner on any prefilled prompt. That is expected: review the prompt, then send it.

Request example

Illustrative request. Set RUNZIVO_TOKEN and TENANT_ID, and SANDBOX_ID in your shell. Replace placeholders with your own values. This page does not send requests.

Set WEBSOCKET_KEY to a fresh base64-encoded 16-byte nonce. This cURL request shows the HTTP upgrade only; use a WebSocket client for terminal frames.

cURL
curl --http1.1 --request GET \
  "https://api.runzivo.dev/v1/customer/tenants/${TENANT_ID}/sandboxes/${SANDBOX_ID}/terminal" \
  --header "Authorization: Bearer ${RUNZIVO_TOKEN}" \
  --header "Connection: Upgrade" \
  --header "Upgrade: websocket" \
  --header "Sec-WebSocket-Version: 13" \
  --header "Sec-WebSocket-Key: ${WEBSOCKET_KEY}"

Response example

WebSocket · An upgraded socket has no JSON response body. Illustrative protocol exchange:

101 · WebSocket
Client → server: binary terminal input bytes
Client → server: text {"type":"resize","cols":80,"rows":24}
Server → client: binary terminal output bytes
Server → client: ping (client replies pong)

Browser connection with a ticket

Illustrative JavaScript after obtaining a ticket. Use its returned URL, do not log it, and send terminal input as binary frames.

JavaScript
// ticketResponse is the terminal-ticket response.
const url = new URL(ticketResponse.url);
url.searchParams.set("ticket", ticketResponse.ticket);
const socket = new WebSocket(url);
socket.binaryType = "arraybuffer";
socket.addEventListener("open", () => {
  socket.send(JSON.stringify({ type: "resize", cols: 80, rows: 24 }));
});
All response examples (also available without JavaScript)

101 Switching Protocols

An upgraded socket has no JSON response body. Illustrative protocol exchange:

WebSocket
Client → server: binary terminal input bytes
Client → server: text {"type":"resize","cols":80,"rows":24}
Server → client: binary terminal output bytes
Server → client: ping (client replies pong)

400 Invalid request

Malformed JSON, unknown fields or invalid values. Terminal upgrades also reject malformed handshakes.

application/json
{
  "error": {
    "code": "invalid_request"
  }
}

401 Unauthorized

Missing or invalid bearer token. A ticketed terminal request can instead return ticket_invalid.

application/json
{
  "error": {
    "code": "unauthorized"
  }
}

403 Forbidden

The caller lacks workspace membership or permission, or the API key is not allowed. Codes include tenant_membership_required, permission_required and api_key_scope_required.

application/json
{
  "error": {
    "code": "permission_required"
  }
}

404 Not found

No sandbox with this ID exists in the authorized workspace.

application/json
{
  "error": {
    "code": "sandbox_not_found"
  }
}

409 Conflict

The operation is not allowed in the current sandbox state. See the operation notes for other possible conflict codes.

application/json
{
  "error": {
    "code": "sandbox_wrong_state"
  }
}

426 Upgrade required

The request did not contain a WebSocket upgrade.

application/json
{
  "error": {
    "code": "websocket_required"
  }
}

500 Internal error

The server could not complete the request.

application/json
{
  "error": {
    "code": "internal_error"
  }
}

503 Unavailable

Sandbox service is unavailable. Exec and terminal operations can also return sandbox_exec_unavailable.

application/json
{
  "error": {
    "code": "sandboxes_unavailable"
  }
}

Ask Docs

Ask a question and open it in your AI assistant. The prompt tells it to answer from the Runzivo docs and link the pages it used.

Try asking

Your question opens in the assistant you pick, under its own terms. Runzivo does not see or store it.