window.chatbase, so you post the same messages to the iframe yourself.
Your page and the chat talk only through postMessage, in both directions. You send messages such as identify or resetChat, and the chat sends events such as assistant-message back.
Almost everything in the Features section works in an iframe, and in a mobile app WebView.
When to use the iframe
Use the iframe when:- The chat sits inline in your page, such as a help center section, a dashboard panel or a full-page chat.
- You build your own launcher, layout or open and close behavior.
- Your platform blocks third-party scripts but allows iframes.
- You add the chat to a mobile app. See Mobile apps.
Quick start
1
Copy the iframe code
- Go to your Chatbase Dashboard and select your AI agent.
- Open Channels, click Manage on the Chat bubble card.
- Click Deploy and choose Website iframe.
- Copy the code.
2
Add an id
Give the iframe an If you use a custom domain, the
id so your code can find it. Add allow="microphone" if you use voice or dictation.src uses that domain. Every example on this page reads the origin from src, so they work with either.3
Connect your page
Paste the connector in a script right after the iframe, then log the agent’s replies:
4
Test it
Load the page and send a message.
The agent answers in the iframe and its reply appears in your browser console.
Connect to the iframe
Save this connector asconnect-chatbase.js, or paste it into your page. Every example on this page uses it.
connect-chatbase.js
- Posts only to the iframe’s origin, and ignores messages from anywhere else.
- Holds messages until the chat is ready. Anything posted before
iframeReadywould otherwise be dropped. - Calls your
onhandlers with the event’s payload as the first argument and the full message as the second. - Works with custom domains, because it reads the origin from
src.
export in front of function.
Timing rules
- Call
connectChatbaseright after the iframe element, before the iframe loads. If it runs later, it missesiframeReadyandsendnever delivers. - Don’t use the iframe’s
loadevent. It can fire before the chat is listening. - If the iframe reloads, the chat posts
iframeReadyagain. Put setup such asidentifyinchat.on("iframeReady", ...)so it runs again too.
Readiness signals
Without the connector
The connector is optional. With plainpostMessage:
- Post with the agent origin as
targetOrigin, for examplehttps://www.chatbase.co."*"also works, but never use it for a message that carries a token. - Wait for
iframeReadybefore posting. - In your
messagelistener, checkevent.source === iframe.contentWindowandevent.origin. - Payloads sit under
event.data.data.
Common tasks
Each recipe assumesconst chat = connectChatbase(iframe) from Connect to the iframe.
Identify a logged-in user
Render a JWT from your server into the page, then send it when the chat is ready. See Identity and user data.identify
Reset the conversation on every load
conversationReady fires again after the reset. If you also identify the user, identify must arrive within the 500 ms window, or the reset applies to the anonymous conversation.
Reference: resetChat
Reset the conversation from a button
resetChat
Show the chat only when it’s ready
Build your own open and close button
focusInput, widgetClosed, close
Ask a question for the user
options: { hidden: true } to send it without showing it. The agent’s reply still shows.
Reference: sendMessage
Give the agent context about the page
Log the user out
Track messages in analytics
Set the chat language
setWidgetLanguage
Fit the chat to mobile screens
windowInnerWidth
Feature support
Everything works in an iframe except the floating launcher, floating initial messages and automatic sizing.Works in the iframe
Embed script only
These are features of the bubble wrapper, not of the chat. Build them yourself if you need them.Messages you can send
Every message is an object with atype. The examples use chat.send from the connector.
resetChat
Starts a new conversation. The old one stays in Recent chats.sendMessage
Sends a message as if the user typed it.string
required
The message text. Blank messages are ignored.
When
true, the message is sent but not shown in the chat. The agent’s reply is shown.setInitialMessages
Replaces the agent’s initial messages for this session. They show in new conversations, including afterresetChat.
string[]
required
Non-empty strings. Text past 1000 characters in total is cut off.
setOptions and resetOptions
Override display options at runtime.params takes the same fields as setOptions in the embed script.
resetOptions takes the fields to clear, each set to true. An invalid payload is ignored and logs [CHATBASE]: Invalid setOptions payload in the iframe’s console.
The iframe posts optionsChanged once the change is on screen.
focusInput
Focuses the message box, unless something else in the iframe already has focus. Send it when you show the iframe.widgetClosed
Stops voice mode. Send it when you hide the iframe.windowInnerWidth
Reports your window width. Below 768 pixels the chat shows mobile initial messages. Below 640 pixels it uses its mobile layout. This message has notype.
setWidgetLanguage
Sets the chat language. Applies only when the agent’s language source is set to the website.lang is a language code, or null to fall back to the default.
hostLang URL parameter instead.
Events you receive
The iframe posts these to your page. With the connector,chat.on(type, (data) => ...) receives the payload as data.
type in tool-call and tool-result is the action type. name is the tool name. tool-result can arrive twice for the same call, so dedupe on toolCallId:
Identity and user data
The iframe supports the same identity verification as the embed script. Verification happens on the Chatbase server, so it is as secure in an iframe as in the bubble.Choose when to identify
Pick one:- Render the token into the page (recommended). Your server signs the JWT and writes it into the HTML. Send
identifyoniframeReady. See the PHP example. - Single-page apps: wait to render the iframe. Fetch the token first, then render the iframe. See the React example.
- Token arrives late. Send
{ type: "initConfig", willIdentify: true }oniframeReady, thenidentifyonce you have the token. The chat waits with no user until then.initConfigalso shows the chat’s close button, which postsclose, so handle it.
Methods
JWT
Sign the JWT on your server with your agent’s secret key. See Generating the JWT payload for claims and server code in Node.js, Python, PHP and Ruby.string
required
The signed JWT. Claims such as
email, name, phonenumber, custom_attributes and stripe_accounts update the contact. The agent can’t read them.object
String values the agent can read. Leave out sensitive data.
In the embed script, every
identify field except token becomes a public attribute. In the iframe you put them in publicAttributes yourself.User hash
Compute the hash on your server: HMAC-SHA256 of the user ID with your secret key, hex encoded.Invalid user id or user hash. Please check them.
Metadata without verification
Senduser_metadata alone to give the agent context.
- Values are strings. Other values are converted to JSON text.
- The agent reads up to 1000 characters of metadata.
- Anyone using the page can change this data. Don’t use it for anything that needs trust, such as account actions.
Log out
Clear the identity when the user logs out, then reset so the next user doesn’t see the conversation.Conversation history
The chat stores the current conversation in the iframe’s own browser storage. Browsers keep that storage separate for each site that embeds the iframe.
Clearing site data or blocking third-party storage starts anonymous users fresh. Identify users to keep their history.
Prefilling forms
Client-side custom actions
Configure the action in the dashboard first. See Client-Side Custom Actions. When the agent calls the action, the iframe postsCHATBOT_TOOL_CALL. Reply with CHATBOT_TOOL_RESULT and the same toolName.
result is one of:
{ status: "success", data }{ status: "error", error }
- The chat waits 60 seconds, then fails the call with
Tool call timeout. - A result larger than 20 KB, by default, fails with
Tool result too large. A malformed one fails withInvalid tool result. - Results are matched by
toolName. Two calls to the same tool at once can receive each other’s result. - Actions set not to wait for a response never post
CHATBOT_TOOL_CALL. Listen fortool-callinstead.
Client-side custom forms
Configure the form action in the dashboard first. See Client-Side Custom Forms for the schema fields.toolName matching apply. An invalid schema fails with Invalid form schema.
Widget functions
Widget functions call your page from inside a widget.referrerpolicy="no-referrer-when-downgrade" on the iframe so it can detect that origin in every browser.
URL parameters
The iframe reads one URL parameter.
The iframe ignores every other parameter, including
name, email, user_id, user_metadata and token. URLs end up in browser history, server logs and referrer headers, so identity goes through identify instead. To build the iframe on your server, render the token into the page and post it, as in the PHP example.
Complete examples
- HTML
- React
- PHP
Identifies the user, resets on load, logs replies and handles a client-side action. The token request starts while the iframe loads.
If the token request can finish more than 500 ms after
iframeReady, render the token into the page instead. See Choose when to identify.Mobile apps
The simplest way to add your agent to a mobile app is to load the chat in a WebView. Users get the same chat as on your website, including actions, forms, tickets and voice, and there’s no chat UI to build. Use the iOS SDK or Android SDK only when you want your own native chat UI.How it works
The WebView loads the chat URL directly, with no iframe and no HTML of your own:- A bridge script. Inject it before the page loads. It forwards chat events to your native code and sends
identifyoniframeReady, inside the 500 ms window. - Native sends. To send any message from Messages you can send, run
window.postMessage(message, location.origin)in the WebView.
Examples
- iOS
- Android
- React Native
- Flutter
WKWebView in a view controller. To use it from SwiftUI, wrap it in a UIViewControllerRepresentable.ChatbaseViewController.swift
Usage
NSMicrophoneUsageDescription to your Info.plist.Things to know
- Pass the token when you open the chat. The bridge reads it when the page loads. If the user logs in or out while the chat is open, either reload the WebView with the new token or send
identifyfrom native code. - History. The WebView keeps its own storage, so the conversation survives app restarts. Identified users also see past conversations under Recent chats. Conversations in the WebView are website chat conversations, so the SDKs’ conversation methods don’t list them.
- The
closeevent. It fires when the user ends the chat. Dismiss the screen when you receive it. - Specific domains doesn’t apply. Nothing frames the chat, so the allowed domains setting has no effect in a WebView.
- Client-side actions and forms. Your bridge already forwards
CHATBOT_TOOL_CALLandGET_FORM_SCHEMAto native code. Reply withCHATBOT_TOOL_RESULTorFORM_SCHEMA_RESULTthrough a native send. - Using your own HTML page instead. You can also load a page of your own that holds the iframe and the connector. Then everything above this section applies as on the web, and your domain must be in the allowed domains list if you turned it on.
Security
Origins
- Post to the agent origin, never
"*", when a message carries a token or user data. The connector does this for you. - Check
event.sourceandevent.originin every listener. Any script or frame can post to your window. The connector does this for you. - The iframe accepts messages from any page that embeds it. Your allowed domains setting controls which pages can embed it.
Allowed domains
In the dashboard, open Deploy, turn on Specific domains and add every site that embeds the iframe. Browsers then refuse to show the iframe on other sites.Secrets and data
- Sign JWTs and user hashes on your server. Never put the secret key in browser or app code.
- Set
expon every JWT. - Public attributes and
user_metadataare visible to the agent and editable by the user. Put sensitive data in the JWT.
Iframe attributes
Troubleshooting
See the iframe’s console
Errors from the chat, such asInvalid setOptions payload or Invalid user id or user hash, log inside the iframe. In your browser’s developer tools, open the console’s context menu, which is labeled top by default, and select the Chatbase frame. For a WebView, use Safari’s Develop menu on iOS or chrome://inspect on Android.
Common problems
The iframe is blank or refuses to connect
The iframe is blank or refuses to connect
- Specific domains is on and your site isn’t in the list. Add the exact domain the page runs on.
- The visitor is in a country you blocked. The iframe returns 404.
- The
srchas the wrong agent ID.
Messages have no effect
Messages have no effect
- The message went out before
iframeReady. Use the connector’ssend, or send from aniframeReadyhandler. targetOrigindoesn’t match the iframe’s origin, so the browser dropped it silently. Usenew URL(iframe.src).origin.typeis misspelled. Types are case sensitive, for exampleresetChat, notresetchat.setOptionswas invalid. Check the iframe’s console.
iframeReady never arrives
iframeReady never arrives
- The connector or listener was added after the iframe loaded. Add it in a script right after the iframe element.
- Your origin check rejects it. With a custom domain, the origin is your custom domain, not
https://www.chatbase.co.
The user isn't recognized
The user isn't recognized
identifyarrived more than 500 ms afteriframeReady. See Choose when to identify.- The JWT is expired, or signed with the wrong secret or algorithm. Use HS256 and the secret from this agent.
- The user hash is wrong. The iframe logs
Invalid user id or user hash. Please check them. - You passed public fields at the top level of
params. In the iframe they go inpublicAttributes.
Event data is undefined
Event data is undefined
Without the connector, the payload sits one level deeper than in the embed script. Read
event.data.data.content, not event.data.content.Reset on load loops
Reset on load loops
conversationReady fires again after each reset. Reset only once, guarded by a flag.A client-side action times out
A client-side action times out
- No listener replied with
CHATBOT_TOOL_RESULTwithin 60 seconds. - The reply’s
toolNamedoesn’t match the call. resultisn’t{ status: "success", data }or{ status: "error", error }.
The conversation is gone after a reload
The conversation is gone after a reload
The browser cleared or blocked the iframe’s storage. Anonymous history lives only in that storage. Identify users to keep their history in Recent chats.
WebView: links in replies do nothing
WebView: links in replies do nothing
On iOS,
WKWebView ignores links that open a new window. Implement webView(_:createWebViewWith:for:windowFeatures:) and open the URL in the system browser, as in the iOS example.WebView: no events reach native code
WebView: no events reach native code
- The bridge was injected after the page loaded. Inject it at document start.
- On Android,
DOCUMENT_START_SCRIPTisn’t supported by the device’s WebView. Update Android System WebView. - On Android,
domStorageEnabledorjavaScriptEnabledis off.
Next steps
Identity Verification
JWT claims and server code
Event Listeners
Use cases for chat events
Client-Side Custom Actions
Configure actions that run in the browser
Chat Interface Control
Runtime options reference