WebSocket Frame Reference
All frames are JSON objects with a "type" string discriminator. Unknown frame
types MUST be silently ignored (forward-compatible extension point).
Ordering rule: The first frame from the client MUST be ATTACH_SESSION.
The server MUST close (code 1003) if any other frame arrives first.
Endpoint: ws[s]://<host>/v1/sbp/ws/{session_id}
Client → Server
ATTACH_SESSION
Must be sent as the very first frame. Authenticates the surface and declares its capabilities.
{
"type": "ATTACH_SESSION",
"session_id": "uuid",
"session_token": "string",
"surface_context": {
"device_type": "mobile",
"max_output_tokens": 300,
"ui_capabilities": ["markdown", "streaming"],
"locale": "en-US",
"surface_id": "uuid-or-fingerprint",
"mcp_tools": ["camera", "gps"]
}
}
| Field | Required | Description |
|---|---|---|
session_id |
Yes | Must match the URL path parameter. |
session_token |
Yes | Authentication credential for this session. |
surface_context |
No | See SurfaceContext schema. |
DETACH
Graceful disconnect. The server activates the Tether on receipt.
{ "type": "DETACH" }
TOOL_RESULT
Reply to a TOOL_CALL from the server. Must be sent within the timeout
(default: 30 seconds).
{
"type": "TOOL_RESULT",
"call_id": "uuid",
"result": { ... },
"error": null
}
| Field | Required | Description |
|---|---|---|
call_id |
Yes | Must match the call_id from the corresponding TOOL_CALL. |
result |
No | Tool output object. Structure is tool-specific. |
error |
No | Error string if the tool failed; null on success. |
PONG
Reply to a server PING. Must be sent within 10 seconds or the server MAY
close the connection.
{ "type": "PONG" }
Server → Client
SESSION_ATTACHED
Sent immediately after successful ATTACH_SESSION validation.
{
"type": "SESSION_ATTACHED",
"session_id": "uuid",
"surface_id": "uuid or null",
"device_type": "mobile",
"queued_turns": 3,
"mcp_tools_registered": ["camera", "gps"],
"sbp_version": "1.2",
"sbp_level": "L5"
}
queued_turns is the number of Tether turns about to be delivered. If > 0,
TETHER_TURN frames follow immediately.
SESSION_NOT_FOUND
{ "type": "SESSION_NOT_FOUND", "detail": "No session with ID abc123" }
Server closes after sending (code 4004).
FORBIDDEN
{ "type": "FORBIDDEN", "detail": "session_token invalid or expired" }
Server closes after sending (code 4003).
PROTOCOL_ERROR
{ "type": "PROTOCOL_ERROR", "detail": "Expected ATTACH_SESSION as first frame, got 'PONG'" }
Server closes after sending (code 1003).
TETHER_TURN
Delivers a buffered turn from the Tether queue. Sent in chronological order
after SESSION_ATTACHED when queued_turns > 0.
{
"type": "TETHER_TURN",
"turn_index": 0,
"role": "assistant",
"content": "Here is what I found while you were away...",
"model_used": "gpt-4o",
"created_at": "2026-05-10T15:05:00Z"
}
TURN_CHUNK
Streaming output chunk. Part of a multi-chunk turn.
{
"type": "TURN_CHUNK",
"chunk_id": "uuid",
"delta": "partial text content..."
}
TURN_COMPLETE
Signals the end of a streaming turn.
{ "type": "TURN_COMPLETE", "chunk_id": "uuid" }
TOOL_CALL
Sent when the agent requests a surface-side MCP tool call. The surface MUST
reply with TOOL_RESULT within the timeout (default: 30 seconds).
{
"type": "TOOL_CALL",
"call_id": "uuid",
"tool_name": "camera",
"tool_input": { "mode": "photo", "facing": "back" }
}
Multiple TOOL_CALL frames may be in-flight simultaneously; they are
correlated by call_id.
PING
Keepalive. Client MUST reply with PONG within 10 seconds.
{ "type": "PING" }
WebSocket close codes
| Code | Meaning |
|---|---|
| 1000 | Normal closure |
| 1003 | Protocol error (bad frame type or ordering) |
| 4003 | Forbidden (invalid session_token) |
| 4004 | Session not found |