This is for connecting a client — typically a browser — to one specific session, in addition to your server-to-server integration with
POST /v1/chat/inbound. It’s not a replacement for the REST API.1. Mint a token
CallPOST /v1/chat/session/{id}/ws_token with your API-Token to get a short-lived token and the URL to connect to:
With an
API-Token, stream must be "user" — a 403 is returned for any other value. The session must not be closed (409 session_closed); a dormant session can still be minted for, since the next customer message reactivates it.2. Connect
Open the WebSocket atws_url exactly as returned (don’t build the host yourself), adding token and your own client_id as query parameters:
3. Send frames
Send JSON text frames. Two shapes are accepted:typedefaults to"message"if omitted.message_idis optional — a client-chosen id echoed back on the acknowledgement, useful for matching a send to its ack in your UI.idempotency_keyis also accepted on either frame shape and behaves the same as onPOST /v1/chat/inbound/async— resending the same key and content is safe.- For interaction responses, send
option_id(a button/list/carousel choice) orresponse(free-form input like a date), matching what the interactive message asked for — see Interactive messages. - Only text frames are accepted; sending binary data closes the connection.
4. Read acknowledgements and events
Every frame you send gets exactly one acknowledgement back:payload carries the same fields and state values as the synchronous Send Message response — reply, mode, and help_requested. A few states are specific to the socket:
Every ack echoes
message_id back if your frame included one, and carries turn_id once a turn exists for it.
Events
Beyond acknowledgements, the connection pushes events as they happen in the session:
Every event is wrapped as
{ type, session_id, account_id, ts, event_id, payload }. Use event_id to de-duplicate — a reconnect can replay a short window of recent events so you don’t miss anything that happened while briefly disconnected.
Close codes
Reconnecting
- On an unexpected drop, reconnect with exponential backoff — roughly 1s, 2s, 4s, 8s, up to a 30s cap — and give up after a handful of attempts, surfacing an error in your UI.
- Mint a new token ahead of the 5-minute expiry rather than waiting for the connection to be closed, so an active conversation doesn’t interrupt the customer.
- Reuse the same
client_idacross reconnects (persist it locally, e.g. insessionStorage) so you resume as the same client. Only sendclaim=1when the user explicitly wants to take over from another tab/session holding the connection — not on every routine reconnect, or you’ll evict yourself on the next tab. - On a
1008close with the agent inactive, don’t reconnect immediately in a tight loop — poll less aggressively (e.g. every 20s) until the agent is active again. On a1008close from a claim conflict, don’t auto-reconnect at all — surface a “Resume here?” action and only reconnect withclaim=1if the customer confirms.
