Skip to content

WebSocket Events

Connect to the WebSocket endpoint:

ws(s)://your-host:3000/v1/ws

All messages are binary-encoded using BSATN (Binary SpacetimeDB Typed Notation) by default. A JSON mode is available for development (append ?format=json).

MessageDescription
AuthenticateProvide a JWT token
CallReducerInvoke a reducer
SubscribeSubscribe to a SQL query
UnsubscribeCancel a subscription
OneOffQueryExecute a single read-only SQL query
MessageDescription
IdentityTokenAuth confirmation, token, identity
SubscribeAppliedInitial state snapshot for a new subscription
TransactionUpdateIncremental delta (inserts + deletes) for a committed transaction
ReducerCallResponseResult of a reducer call
ErrorProtocol or reducer error
Client Server
│──── Connect ────────────────►│
│◄─── IdentityToken ──────────│
│ │
│──── Subscribe(query) ───────►│
│◄─── SubscribeApplied ───────│ (initial snapshot)
│ │
│──── CallReducer ────────────►│
│◄─── ReducerCallResponse ────│
│◄─── TransactionUpdate ──────│ (delta pushed to all subscribers)
│ │
│──── Unsubscribe ────────────►│

For debugging, use ?format=json:

const ws = new WebSocket('ws://localhost:3000/v1/ws?format=json');
ws.send(JSON.stringify({
type: 'Authenticate',
token: 'eyJ...'
}));
ws.send(JSON.stringify({
type: 'Subscribe',
query: 'SELECT * FROM agent_state',
query_id: 1
}));
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.type, msg);
};
{
"type": "TransactionUpdate",
"subscription_id": 1,
"tx_id": 12345,
"timestamp": 1735689600000000,
"reducer": "record_agent_message",
"identity": "abc123",
"tables": {
"agent_state": {
"inserts": [
{ "id": 42, "session_id": "session-abc", "role": "user", "content": "Hello" }
],
"deletes": []
}
}
}

If a client cannot consume deltas fast enough, the server applies backpressure:

  1. Outgoing buffer fills to ws_send_buffer_bytes (default: 1 MB)
  2. Server pauses delta delivery for that client
  3. Client is given ws_backpressure_timeout_ms (default: 5000) to drain the buffer
  4. If timeout is exceeded, the connection is closed with a 4008 Backpressure code

Tune in config.toml:

[server]
ws_send_buffer_bytes = 2097152 # 2 MB
ws_backpressure_timeout_ms = 10000

The TypeScript SDK handles reconnection automatically with exponential backoff. Manual reconnection should:

  1. Re-authenticate with Authenticate
  2. Re-subscribe to queries — the server returns a fresh SubscribeApplied snapshot
  3. Process any in-flight TransactionUpdate messages from the server (the server may have buffered them)