Overview
VIZOCHOK uses a WebSocket-based protocol for real-time streaming chat. The protocol is designed around three principles:- Auth via first message — credentials are sent as the first WebSocket message, not as query parameters (which would appear in server logs).
- Structured messages — all messages are JSON objects with a
typefield for routing. - Streaming text — LLM responses are delivered token-by-token via
text_deltamessages for real-time display.
Connection Lifecycle
1
WebSocket Connect
Client establishes WebSocket connection. Server accepts.
2
Authentication
Client sends
{"type":"auth","token":"pk_..."} (must be first message).
Server responds with {"type":"auth_ok"}.3
Session Initialization
New conversation: Server sends
{"type":"conversation_started", "conversation_id":"..."}Reconnection: Server sends {"type":"session_restored", "conversation_id":"...", "selected_items":[...]} with cart state and pending tools.4
Message Exchange
Client sends
{"type":"message","text":"..."}.
Server responds with status updates, text_delta chunks (repeated), text_end, and response_complete.5
Disconnect
Client closes connection or disconnects. Session is saved to Redis for reconnection.
Endpoint
https:// to wss://.
Client to Server Messages
auth
Must be the first message sent after connection. The server waits up to 10 seconds for this message before closing the connection with code4001.
message
Send a text message from the user.action
Send a user action in response to an interactive tool (product selection, quick reply, checklist, meal plan).ping
Client-initiated keepalive message. The SDK sends this every 30 seconds.pong
Response to a server-initiatedping. Must be sent within 60 seconds to avoid heartbeat timeout.
Server to Client Messages
auth_ok
Confirms successful authentication. Sent immediately after a validauth message.
conversation_started
Sent when a new conversation is created (first message with noconversation_id).
session_restored
Sent on reconnect when the client provides an existingconversation_id that has an active Redis session. Contains the current cart state and any pending interactive tool.
The
pending_tool object varies by tool type:
text_delta
A single token (or small chunk) of streaming text from the LLM. Multipletext_delta messages form a complete text block, terminated by text_end.
text_end
Signals the end of a streaming text block. After this, the streaming cursor should stop.text
A complete text block (non-streaming). Used for short, pre-formed responses.status
Progress indicator during tool execution. Replaced in the UI when a new status arrives for the same message.
Status codes:
product_cards
Display a set of products for the user to select from.quick_replies
Display quick reply buttons for the user to choose from.confirmation
Confirms a cart operation (add, remove, update).ingredient_checklist
Display a recipe ingredient checklist for user selection.
Each
IngredientItem:
meal_plan
Display a generated meal plan for the user to approve or modify.session_summary
Display a summary of the current session’s cart.cart_changed
Notification that the cart was modified. This is a UI-only event — the actual cart state lives on the client’s backend.response_complete
Signals the end of an agent response. The conversation is ready for the next user message.error
An error occurred while processing the message.event
Internal server events. Currently ignored by the SDK but can be used for analytics.ping / pong
Server-initiated heartbeat. The client must respond with{"type": "pong"} within 60 seconds.
Close Codes
Reconnection Strategy
The SDK implements automatic reconnection with exponential backoff and jitter:Reconnection Rules
- Reconnects on: Network errors, unexpected disconnects, heartbeat timeouts
- Does not reconnect on: Auth errors (4001, 4002, 4003, 4004), clean close, explicit
disconnect()call - Attempt counter resets: On successful connection (before auth)
- Network awareness: Listens for browser
online/offlineevents; reconnects when the network comes back
Session Restore on Reconnect
When the client reconnects with aconversation_id:
- Server loads the session from Redis (if still within the 1-hour TTL)
- Server sends a
session_restoredmessage with cart state and any pending tool - Client can continue the conversation seamlessly
Heartbeat
Both client and server send periodic heartbeat messages to detect dead connections:
The server closes the connection with code
4008 if no pong is received within 60 seconds.
Message Size Limits
The SDK checks message size before sending and returns a
message_too_large error via onError without transmitting the message. The server also enforces the 64 KB limit and responds with an error if exceeded.
Origin Validation
The server checks theOrigin header of the WebSocket connection against the configured allowed origins list:
- If the origin is not in the allowed list, the connection is closed with code
4003 - This prevents unauthorized websites from connecting to the WebSocket API
- The origin list is configured via the
CORS_ORIGINSenvironment variable
Due to a Starlette/FastAPI limitation, the WebSocket must be accepted before the origin can be checked. The connection is accepted first, then immediately closed with a custom code if the origin is not allowed.