# Dialoge Docs > Everything needed to integrate the Dialoge chatbot, track chatbot analytics, and work against the chatbot API. import { ApiReference } from "../components/ApiReference"; ## Chatbot Analytics This document covers the analytics events emitted by the chatbot script and how to consume them in your own tracking setup, for example Google Tag Manager. ### Prerequisites The chatbot script must be loaded on the page: ```html ``` No additional scripts or configuration are needed. Analytics events are emitted automatically. ### How It Works The chatbot dispatches `CustomEvent`s on the global `window` object whenever key interactions occur. These events are intentionally decoupled from any specific analytics platform, so you keep full control over how to forward and map the data. ### Available Events | Event Name | Fired When | Extra Detail Fields | | ------------------------------ | ----------------------------------------------- | --------------------- | | `chat_open` | The user opens the chat window | None | | `chat_response` | The chatbot returns a response | `response_latency_ms` | | `chat_recommendation` | The chatbot returns product recommendations | None | | `chat_product_click` | The user clicks a product link in the chat | None | | `chat_conversation_classified` | The conversation is classified after a response | `classification` | All events include `chat_type: "shopping_assistant"` in their `detail` payload. ### Listening to Events ```js window.addEventListener("chat_open", (e) => { console.log("Chat opened", e.detail); }); window.addEventListener("chat_response", (e) => { console.log("Response latency:", e.detail.response_latency_ms, "ms"); }); window.addEventListener("chat_recommendation", (e) => { console.log("Recommendation shown", e.detail); }); window.addEventListener("chat_product_click", (e) => { console.log("Product clicked", e.detail); }); window.addEventListener("chat_conversation_classified", (e) => { console.log("Classification:", e.detail.classification); }); ``` ### Google Tag Manager Integration Push events into the GTM `dataLayer` by listening to the chatbot events and mapping them: ```html ``` Then create corresponding triggers in GTM using **Custom Event** with the event names above. ### Event Detail Payloads Every event carries a `detail` object accessible via `e.detail`: **`chat_open`** ```json { "chat_type": "shopping_assistant" } ``` **`chat_response`** ```json { "chat_type": "shopping_assistant", "response_latency_ms": 1234 } ``` **`chat_recommendation`** ```json { "chat_type": "shopping_assistant" } ``` **`chat_product_click`** ```json { "chat_type": "shopping_assistant" } ``` **`chat_conversation_classified`** ```json { "chat_type": "shopping_assistant", "classification": "Produktinformation" } ``` ### postMessage from Chatbot Iframe The chatbot iframe sends `postMessage` events to the parent window. You can listen to these instead of, or in addition to, the `CustomEvent`s above. Use this when you need raw access to iframe messages or when the chatbot script is not loaded on your page. **Origin:** `https://chatbot.dialogintelligens.dk` (or `http://localhost:3002` in development) **Payload format:** Each message has an `action` field. Some actions include extra fields. | Action | Extra Fields | When | | ------------------------ | ------------------------------------- | ------------------------------------- | | `productClick` | None | User clicks a product in the chat | | `navigate` | `url` | Parent should navigate to product URL | | `userMessageSubmitted` | None | User submitted a message | | `firstMessageSent` | `chatbotID` | User sent their first message | | `assistantFirstToken` | None | First token of AI response arrived | | `productRecommendation` | None | Product recommendations shown | | `conversationClassified` | `emne` | Conversation classified by AI | | `purchaseReported` | `chatbotID`, `totalPrice`, `currency` | User reported a purchase | | `expandChat` | None | Chat expanded | | `collapseChat` | None | Chat collapsed | | `closeChat` | None | Chat closed | | `toggleSize` | None | User toggled chat size | **Example listener:** ```js const CHATBOT_ORIGIN = "https://chatbot.dialogintelligens.dk"; window.addEventListener("message", (event) => { if (event.origin !== CHATBOT_ORIGIN) return; const data = event.data ?? {}; if (data.action !== "productClick") return; window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "chat_product_click", chat_type: "shopping_assistant", }); }); ``` **Mapping to CustomEvents:** The chatbot script on the parent translates some postMessage actions into CustomEvents. If the script is loaded, you get both the raw `postMessage` and the `CustomEvent`. Actions that map to CustomEvents: `productClick` -> `chat_product_click`, `assistantFirstToken` -> `chat_response`, `productRecommendation` -> `chat_recommendation`, `conversationClassified` -> `chat_conversation_classified`. ### Notes * Events are dispatched automatically. No initialization or opt-in is required. * The `response_latency_ms` value is the round-trip time in milliseconds from when the user sends a message until the chatbot response arrives. * The `chat_conversation_classified` event fires a few seconds after each response, once the backend finishes classifying the conversation. The `classification` value is the topic or category assigned by the AI, for example `"Ordre"`, `"Produktinformation"`, or `"Reklamation"`, or `null` if classification failed. * The implementation is decoupled from GTM on purpose. You are responsible for listening, mapping, and pushing data to your analytics platform. * Events are standard `CustomEvent`s, so they work with any analytics tool that can listen to DOM events. ## Chatbot API Flow This guide explains how the public chatbot API fits together from an integrator's point of view. It focuses on the flow and data model so you can build a client without guessing how responses should be interpreted. For the full schema, field-level validation, and complete endpoint reference, see the [API reference](/api). ### Flow at a glance 1. Your backend calls `POST /api/v1/chat/auth` with the chatbot API key and a `chatbot_id`. 2. The API returns a visitor Bearer token. 3. Your client calls `GET /api/v1/chat/config` to load branding and startup configuration. 4. Your client sends user input to `POST /api/v1/chat/messages` and receives a streamed response. 5. The stream emits structured content as `part_delta` and `part` events, then ends with `done` or `error`. 6. If the assistant returns an actionable marker, your client collects the required data and sends it to `POST /api/v1/chat/actions`. 7. You can optionally read message history, save a conversation rating, or delete the visitor's data. ### Auth and config The API has two authentication layers: | Step | Who calls it | Purpose | | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------ | | `POST /api/v1/chat/auth` | Your backend | Exchange the API key for a visitor token | | Visitor-scoped endpoints | Your client or trusted backend | Use the returned Bearer token for config, messages, actions, history, rating, and deletion | Keep the API key on your server. Do not expose it in browser code, mobile apps, or public frontend bundles. `/auth` uses HTTP Basic Auth with: * the API key as the username * an empty password When the token expires, call `/auth` again to create a new visitor token. There is no refresh token flow. #### Example: create a visitor token ```bash curl -X POST "https://api.dialogintelligens.dk/api/v1/chat/auth" \ -u "$CHATBOT_API_KEY:" \ -H "Content-Type: application/json" \ -d '{ "chatbot_id": "shop-bot" }' ``` The response contains a JWT token: ```json { "token": "eyJhbGciOiJIUzI1NiIs..." } ``` After that, your client can load config with the Bearer token. `GET /api/v1/chat/config` returns the public UI configuration for the chatbot, including: * name * avatar * welcome message * theme colors * popup messages ### How message data is structured Every conversation item is a `message`. A message contains ordered `parts`. For user input, `message.parts` is how you send text and attachments in one request. For assistant output, `message.parts` is also how you render the response. Parts are already structured for you, so the client should render them directly instead of trying to parse raw text. #### Mental model | Term | Meaning | | --------- | -------------------------------------------------------- | | `message` | One user, assistant, or system entry in the conversation | | `part` | A top-level renderable unit inside a message | | `block` | A section inside a `rich_text` part or table cell | | `span` | Inline formatted text inside a paragraph or bullet item | ```text message `-- parts[] |-- rich_text | `-- blocks[] | |-- paragraph | | `-- spans[] -> text | bold | strike | link | `-- bullet_list |-- image |-- table |-- products `-- show_contact_form ``` #### Parts Common assistant part types are: * `rich_text` for formatted text * `image` for image content * `table` for structured tables * `products` for product cards * marker parts such as `show_contact_form` or `request_image_upload` The important detail is that all of these are siblings in the same `parts` array. That means a single assistant message can look like: 1. Some text 2. A marker telling the client to open a form 3. More text after the marker #### Blocks and spans Inside a `rich_text` part: * `blocks` describe larger sections such as paragraphs and bullet lists * `spans` describe inline formatting inside those blocks, such as plain text, bold text, struck text, and links This lets a client render formatted content without having to parse markdown or custom marker syntax. #### Example: a message with text and an action ```json { "message_id": "msg_123", "role": "assistant", "parts": [ { "type": "rich_text", "part_id": "part_1", "blocks": [ { "type": "paragraph", "spans": [{ "type": "text", "text": "Need help with your order?" }] } ] }, { "type": "show_contact_form", "part_id": "part_2", "fields": [ { "key": "name", "label": "Your name", "required": true }, { "key": "email", "label": "Email address", "type": "email", "required": true } ] }, { "type": "rich_text", "part_id": "part_3", "blocks": [ { "type": "paragraph", "spans": [{ "type": "text", "text": "Fill in the form and we will follow up." }] } ] } ] } ``` In this example, the form is not separate from the message. It is one ordered part of the message. ### How streaming works `POST /api/v1/chat/messages` returns a Server-Sent Events stream. Because this is a `POST` endpoint, the browser's built-in `EventSource` API is not a good fit. Use `fetch()` directly or a helper such as `@microsoft/fetch-event-source`. The stream uses five public event types: | Event | Meaning | | ------------ | ------------------------------------------------- | | `status` | Lifecycle updates such as connected or processing | | `part_delta` | Incremental updates for a streamed part | | `part` | A finalized structured part | | `done` | The final assembled assistant message | | `error` | A terminal stream error | Typical flow: ```text status(connected) status(processing) part_delta / part ... done or error ``` `part_delta` is useful when you want progressive rendering for content such as: * rich text * products * tables `part` gives you a finalized structured part. Marker parts typically arrive this way because they do not need progressive updates. `done.message` is the final source of truth for the assistant response. If you rendered draft content while streaming, replace or reconcile it with the message from `done`. #### Example: consume the message stream ```ts import { fetchEventSource } from "@microsoft/fetch-event-source"; await fetchEventSource("https://api.dialogintelligens.dk/api/v1/chat/messages", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ message: { parts: [{ type: "text", text: "What is your return policy?" }], }, context: { page: window.location.href, }, }), onmessage(event) { const data = JSON.parse(event.data); switch (event.event) { case "status": updateStatus(data.status); break; case "part_delta": applyPartDelta(data.part_id, data.delta); break; case "part": renderFinalPart(data.part); break; case "done": replaceDraftMessage(data.message); break; case "error": showError(data.message); break; } }, }); ``` ### How markers and actions work together Markers are assistant parts that tell the client to do something beyond rendering text. The key link is `part_id`: * the assistant sends a marker part with a `part_id` * your client renders the related UI * when the user completes the action, your client sends that same `part_id` to `/actions` #### Marker behavior | Marker part | What the client does | Follow-up | | ---------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------- | | `show_contact_form` | Render the fields from the marker | Submit `action.type = "contact_form"` to `/actions` | | `show_support_ticket` | Render the ticket form and attachment rules from the marker | Submit `action.type = "support_ticket"` to `/actions` | | `request_image_upload` | Prompt the user to upload an image that matches the marker constraints | Send a later `/messages` request with an `image` part | | `request_human_agent` | Show a handoff option in your UI | Client-defined flow | | `custom` | Handle your own `key` and optional `payload` | Client-defined flow | Only `show_contact_form` and `show_support_ticket` are submitted to `POST /api/v1/chat/actions`. #### Example: submit an action ```json { "part_id": "part_2", "action": { "type": "contact_form", "fields": { "name": "Jane Doe", "email": "jane@example.com", "message": "I need help with my order" } } } ``` The same pattern applies to `support_ticket`, but with the support ticket payload shape defined in the API reference. ### History, feedback, and deletion Once a visitor is authenticated, you can also use: * `GET /api/v1/chat/messages` to load paginated message history * `POST /api/v1/chat/rate` to save a `1`-`5` conversation rating and optional feedback * `DELETE /api/v1/chat` to remove all data for the current visitor These endpoints use the same Bearer token as `config`, `messages`, and `actions`. ## Chatbot Integration This document covers how to open and interact with the chatbot from your own custom elements, buttons, links, search bars, or any other UI. ### Prerequisites The chatbot script must be loaded on the page: ```html ``` ### Option 1: JavaScript API (`window.DialogIntelligens`) The chatbot script exposes a global `window.DialogIntelligens` object with the following methods: | Method | Description | | --------------- | ---------------------------------------------------- | | `open()` | Opens the chat window | | `open(message)` | Opens the chat window and sends a pre-filled message | | `hide()` | Hides the chat button and iframe completely | | `show()` | Shows the chat button (closed state) | | `destroy()` | Removes the chatbot from the page entirely | #### Examples **Custom button:** ```html ``` **Link/anchor:** ```html Need help? ``` **Open with a pre-filled message:** ```html ``` ### Option 2: URL Parameters Append `?chat=open` to any page URL where the chatbot is loaded. The chatbot will automatically open on page load and the parameters are stripped from the URL. ```text https://example.com/page?chat=open ``` You can also include a `message` parameter to send a pre-filled message when the chat opens: ```text https://example.com/page?chat=open&message=I+need+help+with+my+order ``` Using `message` alone (without `chat=open`) also works. The chat will open automatically: ```text https://example.com/page?message=What+are+your+opening+hours%3F ``` This is useful for email campaigns, QR codes, FAQ links, or any link where you want the chat to open with a specific question. ### Option 3: Inline Search Bar Widget An inline search bar can be embedded anywhere on the page. When the user types a question and presses Enter or clicks send, the chatbot opens and receives the message. #### Setup 1. Load the search bar script **after** the chatbot script: ```html ``` 2. Add a container element where you want the search bar to appear: ```html
``` Or use a class for multiple instances: ```html
``` #### Manual initialization with custom config ```html ``` ### Option 4: Send a Message via `postMessage` You can open the chatbot and send a pre-filled message by posting a message directly to the chatbot iframe. This is how the inline search bar works internally. ```html ``` ### Notes * `window.DialogIntelligens` is available as soon as the chatbot script loads. * The `open()` method is safe to call even if the chatbot is already open. It will not toggle it closed. * `open(message)` opens the chat and sends the message. If the chat is already open, the message is still delivered. * The `?chat=open` and `?message=` URL parameters only trigger once per page load and are removed from the URL bar. * When using `postMessage` to send a message, use a timeout of roughly `1000ms` to let the iframe initialize after opening.