Skip to main content
Everything you render comes from one immutable state snapshot. This page explains each part and how to handle common cases: quick actions, Human Agent handoff, and closed conversations.

The state snapshot

chat.getState() returns the current snapshot. It changes identity only when something changed, so it works directly with React’s useSyncExternalStore, Svelte stores, and Vue refs.
Message[]
Greetings, history, live messages, and messages still sending, in order.
Activity
What to show as a typing indicator.
Conversation | null
The open conversation, or null before the first message.
string
idle, connecting, connected, disconnected, suspended, failed, or closed. Show a “Reconnecting…” hint for disconnected and suspended.
ChatConfig | null
Your chat configuration from the platform. null until it loads: resume() loads it, or call chat.getConfig().
boolean
true while the stored conversation loads. Show a skeleton instead of an empty list.

Subscribe to changes

chat.subscribe(listener) fires after every change with the new snapshot. To react to one kind of change, use chat.on(event, listener):
Both return a function that unsubscribes. For every event, see the API reference.

Render by kind

Most messages are text. Handle the other kinds so the visitor sees the right prompt: content is markdown. To show formatting and links, render it with a markdown library and sanitize the HTML. If you only need plain text, render text.

Sending and failed messages

When the visitor sends a message, it appears at once with status: "sending". When the server confirms it, its id changes and messageUpdated fires with the old id as previousId. Render with dedupeKey ?? id as the key so the element isn’t recreated.
The SDK never retries a failed send, because sending isn’t idempotent. A failed message stays in messages with status: "failed". Show that it wasn’t delivered and let the visitor send it again.

Quick actions

AI Agent messages can offer buttons, written as tags in content. To answer one, send its message (or its label, if there’s no message) with sendMessage.
Show the buttons only for the latest message, and only when it isn’t from the visitor. Selection lists and forms work the same way: send the chosen option’s text.

Typing indicators

Show activity below the last message, and send the visitor’s own typing state to Human Agents:

Handoff and closed conversations

  • A Human Agent joined. When conversation.isHandledByHuman is true, show it in the header. activity stays idle until they start typing. Their messages have role: "agent" and an authorName.
  • The conversation closed. When conversation.isClosed is true, replace the composer with one of these:
    • chat.reopen() lets the visitor keep writing in the same conversation, when canReopen is true.
    • chat.startNewConversation() closes it and starts a fresh one.
  • The visitor ends it. For a close button, call chat.close().

Suggestions before the first message

Show config.suggestions as buttons while there’s no conversation yet:
Suggestions.tsx