Embedding the Widget
How to add the Chat9 chat widget to your site, plus identified sessions for advanced integrations.
Basic embed
Copy the snippet from the Dashboard and paste it before the closing </body> tag. It loads a small script from your API host and points the iframe UI at the Chat9 app.
Example (placeholders — the Dashboard fills in your real bot public ID and URLs):
data-bot-id carries your bot's public_id, not the secret API key. It is safe to ship in page HTML.
You can choose between two modes on the Embed page:
- Bubble (default) — floating chat button in the bottom-right corner.
- Inline — renders the widget inside an existing container on your page. Add
data-mode="inline"anddata-target="<elementId>", and put an empty<div id="<elementId>"></div>where you want the widget.
What the widget does
- A floating chat iframe appears in the bottom-right corner of your page (about 400×600px).
- Users type questions and receive answers from your documentation.
- Session continuity works across messages in the same visit.
- If the widget starts a brand-new empty conversation, it can show a default greeting instead of an error or blank state.
- If the bot needs one missing detail, it can ask a single follow-up question (embedded in the same text reply) instead of guessing.
- The widget works in any language.
Language behavior — short version: the bot replies in the language of the user's question once they've typed one; before that, it uses your KYC locale if provided, then the browser locale, then English. See Language Support for the full contract, including sticky retention and the dashboard escalation-language override.
Where to get the embed code
- Log in to your Dashboard at https://getchat9.live.
- Copy the embed block from the Dashboard home (
/dashboard). The widget loads your bot'spublic_idfrom thedata-bot-idattribute on the script tag.
CORS
The widget is designed to work on arbitrary sites because the public loader injects an iframe hosted by Chat9. You do not need to expose your private API key in page HTML.
Session continuity
The widget keeps a session_id so the bot can maintain context across follow-up questions.
Current behavior:
- anonymous sessions are stored in the browser and can continue in the same browser for up to 24 hours
- identified sessions can also be resumed by Chat9 on
POST /widget/session/initfor the sameuser_idif the previous chat is still open and was active within 24 hours - closed chats are not resumed
If a chat is explicitly closed by support/escalation flow, the widget shows Start new chat. The old transcript stays visible, the widget marks the next section as a new conversation, and the next message starts a fresh session below the old history.
Greeting behavior:
- when the widget opens a brand-new session with no resumed transcript, it can show the default greeting automatically
- if an existing session is resumed from the last 24 hours, the greeting is not shown again
- after
Start new chat, the next fresh empty session shows the greeting again
Clarification behavior:
- Chat9 may ask one clarification question when the user's request is ambiguous or missing a critical parameter, but at most one blocking clarifying question per conversation.
- The clarifying question is part of the assistant's normal text reply — there is no separate structured payload and no quick-reply buttons.
- Clarification replies are treated as part of the same conversation context, not as a brand-new standalone question.
- Empty follow-up turns after the conversation has already started are still rejected as invalid input.
Security
The bot public_id in the data-bot-id attribute identifies which bot to load; it is intended to appear in page HTML. Keep your signing secret (used to generate identity tokens server-side) and your API key (for dashboard/API use) secret — do not expose them in client-side code or commit them to public repos.
Identified sessions (optional)
By default the standard widget embed is anonymous. Chat9 knows which bot should answer, but does not know who the end user is.
Identified sessions are an advanced integration path that lets you attach verified user context to a widget session.
How it works
- In Dashboard → Settings → Widget API, generate a signing secret.
- Store that secret on your server — never in client-side code.
- Your server generates a short-lived signed
identity_tokenfor each logged-in user. - Your page sets
window.Chat9Config.identityTokenbefore the embed script loads. For anonymous visitors, set it tonull. - The widget calls
POST /widget/session/initwithbot_id+identity_token. - Chat9 validates the token and returns
session_id+mode(identifiedoranonymous). - All subsequent
POST /widget/chatrequests use thatsession_id.
The token is HMAC-signed. Any tampering is detected. The payload is encoded, not encrypted — do not put passwords, card data, or other secrets inside it.
If the same identified user returns later, Chat9 may resume the previous chat if it is still open and was active within the last 24 hours.
Step 1 — Get a signing secret
Go to Dashboard → Settings → Widget API and click Generate secret. Copy the value and store it in your server environment (e.g. CHAT9_SIGNING_SECRET). Never expose it in client-side code or commit it to a repository.
Step 2 — Generate a token server-side
Required payload fields:
user_id— any non-empty stringexp— expiry time, Unix timestamp in secondsiat— issued-at time, Unix timestamp in seconds
Optional payload fields currently supported by Chat9:
emailnameplan_tieraudience_tagcompanylocale
Example payload:
Step 3 — Pass the token to the embed script
In your server-rendered page template, inject the token before the embed script:
The widget automatically calls POST /widget/session/init with your bot_id and identity_token. No private API key is ever sent from the browser.
The token is delivered to the widget iframe over an internal postMessage handshake — never via URL parameters — so signed tokens never appear in browser history, server access logs, or Referer headers. You don't need to do anything for this; it's how embed.js and the hosted widget talk to each other.
If you prefer to call session/init directly (e.g. from a custom integration without embed.js):
Successful response:
If the token is missing or invalid, Chat9 still returns a session but with:
Step 4 — Send chat messages with the returned session
Use the session_id from session/init in later chat requests:
Public widget responses may include:
- a normal answer derived from your knowledge base
- the default localized greeting when a new empty conversation starts
- a partial answer with a caveat when the bot is only partially confident
- a single clarifying question (embedded in the same text reply) when the bot needs one missing detail to answer reliably
- an escalation handoff message when the bot cannot answer safely
The reply is a JSON object whose message content lives in a single text field (alongside session_id, chat_ended and an optional ticket_number). There is no structured clarification payload and the widget does not render quick-reply buttons.
Token field reference
| Field | Required | Type | Constraints |
|---|---|---|---|
user_id | yes | string | non-empty, non-whitespace |
exp | yes | integer | Unix timestamp in seconds |
iat | yes | integer | Unix timestamp in seconds |
email | — | string | optional |
name | — | string | optional |
plan_tier | — | string | optional |
audience_tag | — | string | optional free-form segment label |
company | — | string | optional |
locale | — | string | optional, e.g. en, en-US, ru-RU |
What mode means
mode | Meaning |
|---|---|
identified | The token was valid and Chat9 attached the user context to the session |
anonymous | No token was provided, or validation failed, so the widget continues without KYC context |
plan_tier and escalation priority
When the bot escalates a conversation to a human, Chat9 uses plan_tier from the token to set the ticket priority:
| Trigger | plan_tier not set | plan_tier: "pro" or "enterprise" |
|---|---|---|
| User explicitly requests a human | High | Critical |
| Bot can't find a relevant answer | Medium | High |
| Answer rejected by quality check | Medium | Medium |
Pass plan_tier in the identity token to make sure premium users' escalation tickets surface first in your Escalations inbox.
What Chat9 uses from KYC
Stored in chat context:
user_idemailnameplan_tieraudience_tagcompanylocale
Used in the LLM prompt:
plan_tierlocaleaudience_tag
Used for richer escalation metadata:
user_idemailnameplan_tier
Security checklist
- Generate the token on your server, never in browser JavaScript
- Store the signing secret in an environment variable, not in source code
- Use a short TTL (300 s is a reasonable default)
- Rotate the secret from the Dashboard if it is ever exposed
- Treat invalid tokens as a fallback to anonymous mode, not as a widget outage
- Do NOT put passwords, card numbers, or other secrets in the token payload