Technical docs

Chat Widget

Overview

Support reference for the current `window.ChatWidget` loader and public webchat widget runtime.

Global loader API

  • `window.ChatWidget` currently exposes:
  • `init(options)`
  • `open()`
  • `close()`
  • `destroy()`
  • `sendMessage(content)`
  • `applySettings(settings)`
  • `updateStyle(variables)`

`init(...)` options

  • Required:
  • `apiKey`
  • Optional:
  • `apiUrl`
  • `wsUrl`
  • `eventId`
  • `contact`
  • `language` = `en` or `ru`
  • `isOpen`
  • `positioning` = `fixed` or `absolute`
  • `container`
  • URL resolution order is:
  • explicit `init(...)` value
  • env URL derived from the widget script host
  • build env fallback
  • Loader init hard-fails if `apiKey`, `apiUrl`, or `wsUrl` resolve to empty values.

Loader lifecycle

  • `init(...)` creates:
  • the outer widget container
  • the button iframe
  • the chat iframe container shell
  • The chat iframe is still created lazily on first open.
  • Greeting iframe code exists, but greeting is disabled with:
  • `GREETING_ENABLED = false`
  • In `absolute` mode, the loader appends into the configured container and forces that container to `position: relative` if needed.
  • In `fixed` mode, the loader appends to `document.body`.
  • When the user opens chat before the chat iframe is ready, the loader now shows a loading shell inside the chat container until the iframe finishes initializing.
  • When the widget is closed, the launcher button can now be dragged horizontally to the left or right side.
  • The chosen launcher side is persisted in widget local storage and reused on the next load.

Loader messaging and settings

  • The loader bootstraps the button iframe first.
  • The button iframe fetches widget settings from:
  • `GET /api/public/webchat/init`
  • When those settings load, the button iframe posts them back to the loader as `settingsLoaded`.
  • The chat iframe is initialized separately on first open with:
  • `apiUrl`
  • `wsUrl`
  • `apiKey`
  • `eventId`
  • `contact`
  • current settings
  • `language`
  • `isOpen`
  • optional `parentColorScheme`
  • `applySettings(...)` pushes settings into button/chat/greeting iframes.
  • `updateStyle(...)` only pushes CSS variable overrides to:
  • button iframe
  • chat iframe
  • For system-themed widgets, the loader now derives parent color scheme from the host page using this order:
  • root inline `style.colorScheme`
  • root `data-theme`
  • root `dark` / `light` class
  • computed `color-scheme`
  • browser `prefers-color-scheme` fallback

Resize and iframe behavior

  • Chat iframe permissions are:
  • `microphone`
  • `camera`
  • `autoplay`
  • `display-capture`
  • `clipboard-write`
  • `clipboard-read`
  • Desktop resize handles are attached only on the side opposite the current launcher position:
  • launcher on right: `n`, `w`, `nw`
  • launcher on left: `n`, `e`, `ne`
  • Resize handles are disabled on viewports at `666px` wide and below.
  • Minimum chat size is:
  • `400 x 400`
  • Resize clamping keeps the widget inside the viewport/container with:
  • `25px` edge padding
  • `110px` bottom padding
  • On fixed-position mobile viewports, opening the widget currently locks parent-page scroll until the chat is closed.

Runtime bootstrap and session persistence

  • The widget now bootstraps conversation state from:
  • `GET /api/public/webchat/conversations`
  • Current list query params are:
  • `event_id`
  • `page`
  • `limit`
  • optional `status`
  • optional `contact_external_id`
  • optional `contact_email`
  • optional `contact_phone`
  • The widget still sends the stored `webchat-token` header when present.
  • If a stored token becomes invalid because the request contact scope no longer matches it, the widget clears the token locally and retries the request once without it for recoverable `400`/`401`/`403` responses.
  • The current widget stores public-session state in local storage under:
  • `alloy-webchat`
  • The same storage key is shared by loader state and chat runtime state.
  • The current loader also stores launcher-side preference inside that same widget storage state.

Conversation management

  • The widget now supports multiple public webchat conversations for the current contact/session scope.
  • Welcome state shows recent active conversations before a thread is opened.
  • A dedicated history panel lists active and archived conversations.
  • The widget currently calls:
  • `GET /api/public/webchat/conversations`
  • `POST /api/public/webchat/conversations/new`
  • `PATCH /api/public/webchat/conversations/{conversation_id}/title`
  • `PATCH /api/public/webchat/conversations/{conversation_id}/archive`
  • `PATCH /api/public/webchat/conversations/{conversation_id}/unarchive`
  • `DELETE /api/public/webchat/conversations/{conversation_id}`
  • Conversation mutation requests currently include:
  • `event_id`
  • optional `contact`
  • Archive/unarchive/title/delete currently require a live session token on the backend.
  • Deleting a conversation is optimistic in the widget UI.
  • If a conversation disappears while the widget is viewing it, the widget clears local thread state and falls back to welcome state.

Message loading and send flow

  • Opening a conversation now loads:
  • `GET /api/public/webchat/messages`
  • Current message query params are:
  • `event_id`
  • `conversation_id`
  • `limit`
  • optional `limiter`
  • optional `contact_external_id`
  • optional `contact_email`
  • optional `contact_phone`
  • Older-history loading now uses cursor-style `limiter` pagination, not `page`.
  • Message send now requires an active conversation and sends:
  • `message`
  • `conversation_id`
  • `event_id`
  • optional `contact`
  • optional `attachments`
  • Voice-session creation also now requires an active conversation and sends:
  • `conversation_id`
  • optional `contact`
  • Reaction updates now send:
  • `conversation_id`
  • `event_id`
  • optional `contact`
  • `reaction`
  • Current widget reaction path still calls:
  • `PUT /api/public/webchat/messages/{message_id}/reaction`
  • The backend still also exposes:
  • `PUT /api/public/webchat/messages/{message_id}/feedback`

Message behavior

  • System events render as centered system-log rows rather than regular assistant or customer bubbles.
  • Current system-log icon variants distinguish operator joined, AI rejoined, waiting-for-operator, and generic system notices.
  • The widget listens for:
  • `webchat.message_sent`
  • `webchat.message_updated`
  • `webchat.message_deleted`
  • `webchat.conversation_updated`
  • conversation summary updates and deletes for the history list
  • Incoming websocket events are ignored if they do not match the current `conversation_id`.
  • The widget hides messages when:
  • `status === 'in_progress'`
  • content is empty and there are no attachments
  • Assistant messages can trigger the typing indicator after a contact message when `aiAgentEnabled` is true.
  • The typing indicator is cleared when:
  • a non-system assistant-side message arrives
  • the conversation updates to `has_assigned_operator = true`
  • During assistant typewriter rendering, the widget now preserves the currently streamed partial message instead of clearing it during intermediate re-renders.
  • The header title prefers:
  • assigned operator name
  • and appends AI employee name when both exist

Navigation behavior

  • Desktop uses a header action to open the conversation history panel.
  • On mobile, the widget currently has three navigation screens:
  • `welcome`
  • `history`
  • `chat`
  • On mobile, users can swipe right to go back to the previous screen in that local navigation stack.
  • The widget also reacts to the loader's `mobileBrowserBack` message to step back through that same mobile navigation flow.

Theming and dark mode

  • The widget theme uses runtime CSS variables such as:
  • `--chat-background`
  • `--primary-text-color`
  • `--secondary-text-color`
  • `--customer-text-color`
  • `--primary-color`
  • Current chat styles also derive neutral overlay colors from `--primary-text-color` so hover, search, button-state, and history-row surfaces stay visible in both light and dark themes.
  • In dark theme, the welcome-state avatar icon now contrasts against the brand-colored avatar tile using `--chat-background` instead of a hardcoded light icon color.
  • In dark theme, user-bubble timestamps are derived from `--customer-text-color` so they stay readable even when the customer bubble is light.
  • Loader and conversation-switch screens now use `--chat-background` instead of a hardcoded white background.
  • Welcome-state `View all` text and history-search / header button states now inherit theme-adaptive colors instead of fixed dark-on-light assumptions.

Attachments

  • Current widget client supports:
  • file picker
  • drag and drop
  • screen capture
  • Client-side limits are:
  • up to `5` files
  • max `10 MB` per file
  • Images render inline.
  • PDFs open in a new tab.
  • Other files download through a generated anchor click.

Formatting and previews

  • Conversation rows now use normalized preview text from the latest message.
  • The current widget strips fenced-code language labels out of conversation preview text.
  • Strikethrough formatting is rendered again in the current chat message formatter.

Voice behavior

  • Voice session creation currently requires `eventId`.
  • Voice setup uses:
  • `POST /chat/realtime/create-session/{eventId}`
  • websocket connect to `/chat/realtime?session={sessionId}`
  • Voice websocket can send:
  • binary audio
  • JSON `user_message` payloads with optional attachments
  • Voice event handling currently reacts to:
  • `session.created`
  • `speech_started`
  • `speech_stopped`
  • `response.cancelled`
  • `error`

Start building your AI team