Skip to content

WebSocket API Reference

ws[s]://{host}/ws/chat?session_id={uuid}&token={jwt}
ParameterTypeDescription
session_idUUIDThe session to operate on
tokenstringSupabase JWT access token
  • Transport: WebSocket (RFC 6455)
  • Encoding: JSON text frames
  • Authentication: JWT validated on connection upgrade
  • Multiplexing: All chats in a session share one connection, distinguished by chat_id
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = window.location.host;
const wsUrl = `${protocol}//${host}/ws/chat?session_id=${sessionId}&token=${accessToken}`;

Messages sent from the frontend to the backend.

Send a message to a single chat and stream the response.

{
"type": "query",
"chat_id": "550e8400-e29b-41d4-a716-446655440000",
"content": "Explain quantum computing"
}
FieldTypeDescription
type"query"Message type discriminator
chat_idUUID stringTarget chat
contentstringUser message text

Backend behavior:

  1. Verifies the chat belongs to the authenticated user
  2. Inserts the user message into the messages table
  3. Loads the full conversation history
  4. Calls provider.send_stream() with the history
  5. Sends token messages as tokens arrive
  6. Inserts the assistant message and sends done

Send the same message to multiple chats simultaneously.

{
"type": "query_all",
"content": "Explain quantum computing",
"chat_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002"
]
}
FieldTypeDescription
type"query_all"Message type discriminator
contentstringUser message text (sent to all chats)
chat_idsUUID string[]Target chats

Backend behavior: Spawns a separate tokio::spawn task per chat_id. Each task executes the same flow as a single query. Responses stream concurrently and are interleaved on the same WebSocket connection, tagged with chat_id.

Messages sent from the backend to the frontend.

A single token (or token chunk) from the LLM provider’s streaming response.

{
"type": "token",
"chat_id": "550e8400-e29b-41d4-a716-446655440000",
"content": "Quantum"
}
FieldTypeDescription
type"token"Message type discriminator
chat_idUUID stringWhich chat this token belongs to
contentstringToken text (may be one or more characters)

Signals that streaming is complete for a chat. Includes the full assembled response.

{
"type": "done",
"chat_id": "550e8400-e29b-41d4-a716-446655440000",
"full_content": "Quantum computing is a type of computation..."
}
FieldTypeDescription
type"done"Message type discriminator
chat_idUUID stringWhich chat completed
full_contentstringComplete assistant response

An error occurred during processing.

{
"type": "error",
"chat_id": "550e8400-e29b-41d4-a716-446655440000",
"message": "Model 'GPT-4' not available"
}
FieldTypeDescription
type"error"Message type discriminator
chat_idUUID string or nullWhich chat the error relates to (null for connection-level errors)
messagestringHuman-readable error description

Common errors:

MessageCause
"Unauthorized"Invalid or expired JWT token
"Chat not found"Chat ID does not exist or is not owned by the user
"Model 'X' not available"Model is not in the plugin registry
"Stream error: ..."LLM provider returned an error during streaming
"Invalid message: ..."Client sent malformed JSON
sequenceDiagram
participant C as Client
participant S as Server
C->>S: Connect ws://host/ws/chat?session_id=...&token=...
Note over S: Validate JWT
S-->>C: Connection established
C->>S: {"type":"query","chat_id":"abc","content":"Hello"}
S-->>C: {"type":"token","chat_id":"abc","content":"Hi"}
S-->>C: {"type":"token","chat_id":"abc","content":" there"}
S-->>C: {"type":"token","chat_id":"abc","content":"!"}
S-->>C: {"type":"done","chat_id":"abc","full_content":"Hi there!"}
sequenceDiagram
participant C as Client
participant S as Server
C->>S: {"type":"query_all","content":"Hello","chat_ids":["a","b"]}
Note over S: Spawn task for chat "a" (GPT-4)
Note over S: Spawn task for chat "b" (Mistral)
S-->>C: {"type":"token","chat_id":"a","content":"Hi"}
S-->>C: {"type":"token","chat_id":"b","content":"Hello"}
S-->>C: {"type":"token","chat_id":"a","content":" there"}
S-->>C: {"type":"token","chat_id":"b","content":" there"}
S-->>C: {"type":"done","chat_id":"b","full_content":"Hello there"}
S-->>C: {"type":"token","chat_id":"a","content":"!"}
S-->>C: {"type":"done","chat_id":"a","full_content":"Hi there!"}

Tokens from different chats are interleaved. The client demultiplexes using the chat_id field.

sequenceDiagram
participant C as Client
participant S as Server
C->>S: {"type":"query","chat_id":"abc","content":"Hello"}
S-->>C: {"type":"token","chat_id":"abc","content":"Hi"}
S-->>C: {"type":"error","chat_id":"abc","message":"Stream error: connection reset"}

When an error occurs mid-stream, the partial response is not saved to the database.

The frontend WebSocket client automatically reconnects after 3 seconds when the connection drops:

  • The connected store is set to false
  • Pending streaming state is preserved in Svelte stores
  • A new connection is established with the same session and token

There is no built-in rate limiting on the WebSocket endpoint. In production, consider implementing rate limiting at the nginx reverse proxy level or in the application.