Technical docs
Chat Widget
Public reference generated from tech docs/chat-widget.md.
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`.
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` - `applySettings(...)` pushes settings into button/chat/greeting iframes. - `updateStyle(...)` only pushes CSS variable overrides to: - button iframe - chat iframe
Resize and iframe behavior
- Chat iframe permissions are: - `microphone` - `camera` - `autoplay` - `display-capture` - `clipboard-write` - `clipboard-read` - Desktop resize handles are attached only on: - `n` - `w` - `nw` - Resize handles are disabled at `1024px` wide and below. - Minimum chat size is: - `320 x 360` - Resize clamping keeps the widget inside the viewport/container with: - `25px` edge padding - `110px` bottom padding
Runtime bootstrap and persistence
- The chat runtime bootstraps conversation state from: - `GET /api/public/webchat/messages` - Current query params used by the chat runtime are: - `event_id` - `page` - `contact_external_id` - The runtime also sends the stored `webchat-token` header when present. - The `/messages` bootstrap response is used to persist: - `webchatToken` - `wsToken` - `wsUrl` - `conversation` - `voice_enabled` - `ai_agent_enabled` - `ai_employee_name` - The widget stores public-session state in local storage under: - `alloy-webchat` - The same storage key is shared by loader state and chat runtime state.
What the widget does not currently use
- The chat runtime does not call: - `GET /api/public/webchat/conversations` - The send path does not currently send: - `conversation_id` - `multi_conversations` - `reply_to_message_id` - Older-history loading repeats `GET /messages?page=N` without a `conversation_id`. - Result: the current widget runtime does not expose conversation switching and relies on whichever conversation the backend resolves for the current session/contact scope.
Message behavior
- The widget listens for: - `webchat.message_sent` - `webchat.message_updated` - `webchat.message_deleted` - `webchat.conversation_updated` - 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` - The header title prefers: - assigned operator name - and appends AI employee name when both exist
Reactions
- Reaction UI is shown only for: - `role === 'ai'` - The widget toggles the clicked reaction off on a second click. - Current API drift: - widget calls `/api/public/webchat/messages/{message_id}/reaction` - backend exposes `/api/public/webchat/messages/{message_id}/feedback` - The widget's websocket update handler does not currently reconcile reaction fields from `webchat.message_updated`. - See `message-reactions.md` for the full mismatch details.
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.
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`