Realtime frames
The WebSocket and SSE wire format between the SDK and the runtime, including auth, replay, switch, and close codes.
Endpoint wss://api.kletso.ai/v1/realtime, subprotocol kletso.v1. The first frame must be auth; the token never goes in the URL. Frames are JSON objects discriminated by t, at most 256 KB.
Client → server
t | Fields | Notes |
|---|---|---|
auth | token (kst_…), conversationId?, after? (default 0) | Without a conversation the socket attaches to the user (notifications, conversation.created); with one, the runtime replays events with seq > after |
message | clientId, text?, value? | A user turn; clientId deduplicates retries |
action | surfaceId, componentId, actionId, clientId, value? | A surface interaction (agent, submit {formId, values}, confirm {toolCallId, approve}) |
context | merge? or replace? | Update the session context |
track | name, properties? | Silent app event; evaluated against rules. The SDK sends kletso.chat_opened { conversationId, messages, userMessages, presentation } itself when the chat opens |
screen | name | Silent screen view; evaluated against rules |
switch | conversationId, after? | Re-attach to another conversation; the runtime answers with a fresh ready and replay |
typing | Reserved | |
ping | Heartbeat |
Server → client
t | Fields |
|---|---|
ready | seq, conversationId? |
event | event: a kletso.events/v1 envelope |
pong | |
error | code, message, retryable?, retryAfterMs? |
Example exchange:
→ { "t": "auth", "token": "kst_…", "conversationId": "conv_01J8DEMO000001", "after": 41 }
← { "t": "ready", "seq": 41, "conversationId": "conv_01J8DEMO000001" }
→ { "t": "message", "text": "I need a flight to Osaka", "clientId": "c_01J8CLIENT0002" }
← { "t": "event", "event": { "seq": 42, "type": "message.created", "data": { "role": "user", … } } }
← { "t": "event", "event": { "seq": 43, "type": "agent.typing", "data": {} } }
← { "t": "event", "event": { "seq": 44, "type": "tool.started", "data": { "name": "search_flights", … } } }
Close codes
| Code | Meaning | Client behaviour |
|---|---|---|
| 1000 | normal | none |
| 1001 | going away (deploy, tab closed) | reconnect with backoff |
| 4000 | heartbeat timeout (client-initiated) | reconnect |
| 4401 | auth failed or session revoked; also sent when no auth arrives within 10 s | stop; do not retry with the same token |
| 4403 | token expired | refresh the session, then reconnect |
SSE alternative
GET /v1/conversations/:id/events?after=N&stream=1 with Accept: text/event-stream and the session token. The stream sends event: ready, then event: event lines carrying envelopes (with id: <seq>), keepalive comments every 20 seconds, and event: error. Outbound frames become REST calls (/messages, /actions, /context, /events). SSE needs an existing conversation.