Connection, offline and replay

WebSocket and SSE transports, automatic fallback, heartbeat, backoff, the outbound queue and sequence replay.

Transports

TransportHowWhen
KletsoWebSocketTransportwss://api.kletso.ai/v1/realtime, subprotocol kletso.v1, auth as the first frame, resolves on readyDefault primary
KletsoSseTransportGET /v1/conversations/:id/events?after=N&stream=1 with Accept: text/event-stream; outbound frames become REST callsNetworks that block WebSockets
KletsoAutoTransportWebSocket first; after 2 non-auth failures it latches to SSE for the life of the clientKletsoConfig.transport: auto (default)

Both transports refuse frames above 256 KB and enforce the connectTimeout handshake budget.

Heartbeat and reconnect

  • A ping frame every heartbeatInterval (25 s); no pong within heartbeatTimeout (10 s) closes with code 4000 and reconnects.
  • Backoff uses full jitter between 0 and min(backoffCap, backoffBase × 2^attempt), reset on ready.
  • Close code 4401 means the session is invalid: the SDK stops and surfaces KletsoAuthException. 4403 means the token expired: the SDK refreshes the session (and calls your onTokenExpired for host JWTs) and reconnects.

Replay and dedupe

Every server event has a seq. On reconnect the SDK sends the last seq it saw in the auth (or switch) frame and the runtime replays everything after it before going live. Duplicates and out-of-order events are dropped by seq and event id, so a flaky network never duplicates a bubble.

Offline queue

Frames sent while disconnected (messages, actions, context, track, screen) are queued and flushed after ready with their original clientId, so the runtime deduplicates retries. Beyond outboundQueueLimit (100) the oldest frame is dropped and a KletsoQueueOverflowException is emitted on the client’s error stream.

Observing the connection

ValueListenableBuilder(
  valueListenable: Kletso.instance.connection.asFlutter(),
  builder: (_, state, __) => Text(state.name),   // closed, connecting, open, reconnecting
)

KletsoChat already shows a connection banner and a “new messages” pill; you only need this for your own UI.

Last updated 2026-09-28 · Report an issue with this page