Skip to content

Live updates

ChatNotificationsClient opens the realtime stream so your app shows new messages without polling. This guide covers connecting, the events, the connection state and what to do after a reconnect.

Append new messages to the open thread, update the inbox, and recover cleanly when the connection drops.

  • A getAccessToken that returns the user’s Chat token. See Get the user’s Chat token.
  • A browser with EventSource. Realtime works for signed-in users only; a machine token is refused.

Create one client per tenant application and call connect(). It reads the token through getAccessToken on every (re)connect.

live.ts
import { ChatNotificationsClient } from '@central-code-station/ccs-chat-notifications-client';
import { context } from './fe-guide-context';
declare function appendToThread(conversationId: string, message: unknown): void;
declare function dropConversation(conversationId: string): void;
declare function reloadOpenThreadAndInbox(): Promise<void>;
const live = new ChatNotificationsClient(context);
live.on('message.created', ({ conversation_id, message }) => appendToThread(conversation_id, message));
live.on('conversation.access_revoked', ({ conversation_id }) => dropConversation(conversation_id));
// Events published while the stream was reopening are not replayed: reload after every reconnect.
let connectedBefore = false;
live.on('connectionStateChange', (state) => {
if (state !== 'connected') return;
if (connectedBefore) void reloadOpenThreadAndInbox();
connectedBefore = true;
});
live.connect();
/** A PUBLIC channel you can read but are not a member of: its events do not arrive on their own. */
export function viewPublicChannel(conversationId: string): () => void {
live.subscribeToConversation(conversationId);
return () => live.unsubscribeFromConversation(conversationId);
}
export function signOut(): void {
live.disconnect();
}

One connection carries the user’s personal events and the channels they are a member of (up to 100 channels). The server issues a stream credential that lasts at most 10 minutes. The client reopens the stream with a fresh one 30 seconds early by default (tokenRefreshBufferSeconds).

message.created carries the full message, in the same shape as messages.list, so you render it without another request. Other events cover edits, deletes, reactions, members, conversations, typing and presence. See Realtime events for every payload. Use conversation.access_revoked to remove a conversation the user lost.

connectionStateChange reports disconnected, connecting, connected or error. Show a quiet “reconnecting” hint on connecting and a clear one on error.

4. View a public channel you have not joined

Section titled “4. View a public channel you have not joined”

Events for a PUBLIC channel arrive only if the user is a member. To watch one without joining, call subscribeToConversation(id) while it is open and unsubscribeFromConversation(id) when it closes. The client holds one such channel at a time; a new call replaces the previous one. Joining or creating a channel needs no call: the client reopens the stream by itself when the user gains a channel.

A short network drop is retried by the browser. If the stream fails outright, for example the token was rejected, the client retries up to six times, 1 s, 2 s, 4 s, 8 s, 16 s and 30 s apart. Then it stays in error.

Events published while the stream was reopening are not replayed. Reload the open thread and the inbox each time the state becomes connected again. The client also reopens the stream on its own before the token expires, so keep that reload cheap, for example one page of 50 messages.

To start again after the client gave up, call disconnect() first. A bare connect() does nothing, because the client still counts as started.

restart.ts
import { ChatNotificationsClient } from '@central-code-station/ccs-chat-notifications-client';
import { context } from './fe-guide-context';
const live = new ChatNotificationsClient(context);
// After the library gives up (state 'error'), connect() alone is ignored: disconnect first.
export function restartLiveUpdates(): void {
live.disconnect();
live.connect();
}

Pass debug: true while you develop. Stream errors are otherwise swallowed on purpose, so a flaky connection never throws into your app.

Open the app as coach-ana and send a message to client-john from another browser. The message appears in John’s open thread without a reload and connectionState is connected.

You see Cause Fix
State stays error Expired or missing token, wrong context, or realtime is not available. Turn on debug, then check the realtime-token request. See Troubleshooting.
403 client_context_mismatch on the token request tenantId or applicationId is not your application’s. Use your application’s ids.
Messages missing after a drop Events during a reconnect are not replayed. Reload after connected.
No events for a PUBLIC channel The user is not a member. Call subscribeToConversation.