JavaScript SDK
When you add the Chat Thing chat widget to your site, you also get a JavaScript SDK on window.chatThing. Use it to open and close the widget from your own buttons, send messages, read the conversation, tell the bot who the user is, give it context about the page, and let it take actions on your site.
Before you start
The SDK comes with the chat widget script. Add the widget to your site first: see Install the chat widget. The SDK isn't available when you embed the bot as an iframe. To change the widget's look and position with window.chatThingConfig, see Customise the widget with code.
Methods
| Method | What it does | Needs Advanced SDK features |
|---|---|---|
show(), hide(), toggle() | Open, close or toggle the chat window | No |
showTrigger(), hideTrigger(), toggleTrigger() | Show or hide the floating chat button | No |
sendMessage(), newConversation() | Send a message, or start a new conversation | No |
getMessages(), onMessage() | Read the conversation | No |
isLoading(), onLoading() | Know when the bot is replying | No |
showPreview(), hidePreview() | Show or hide a message bubble above the chat button | No |
extendTheme() | Change light or dark mode and colours | No |
identifyUser() | Tell the bot who the user is | No |
reload() | Reload the widget and its settings | No |
extendContext() | Give the bot information about the page | Yes |
systemMessage() | Add to or replace the bot's instructions on this page | Yes |
registerPowerUp() | Let the bot run functions on your page | Yes |
Calls you make before the chat has finished loading are queued and run once it's ready. window.chatThing itself exists once the widget script has loaded, so check for it (or call the SDK from the script's load event) if your code might run first.
Open and close the chat window
window.chatThing.show(): void; // open the chat window
window.chatThing.hide(): void; // close it
window.chatThing.toggle(): void; // open it if closed, close it if open
For example, open the chat from your own "Contact us" button:
<button onclick="window.chatThing.show()">Chat with us</button>
Show or hide the chat button
window.chatThing.showTrigger(): void; // show the floating chat button
window.chatThing.hideTrigger(): void; // hide it
window.chatThing.toggleTrigger(): void; // toggle it
Send messages and start new conversations
// send a message as the user
window.chatThing.sendMessage(message: string): void;
// start a new conversation, optionally sending a first message
window.chatThing.newConversation(message?: string): void;
Read the conversation
Read the conversation from your page, either as a snapshot or by subscribing to new messages.
type TPublicChatMessage = {
id: string;
role: string; // "user" or "assistant"
message: string;
createdAt: string; // ISO 8601
};
// a snapshot of the conversation (user and assistant messages)
window.chatThing.getMessages(): TPublicChatMessage[];
// the callback runs once for each message already in the conversation,
// then again for each new message. Returns a function that unsubscribes.
window.chatThing.onMessage(
callback: (message: TPublicChatMessage) => void
): () => void;
const unsubscribe = window.chatThing.onMessage((message) => {
console.log(`[${message.role}] ${message.message}`);
});
// stop listening later
unsubscribe();
// or read the whole conversation when you need it
const transcript = window.chatThing.getMessages();
Messages match what's shown in the widget. The bot's messages are delivered once they've finished, not word by word. If you call reload(), register your onMessage handler again afterwards.
Track when the bot is replying
// true while the bot is replying
window.chatThing.isLoading(): boolean;
// the callback runs straight away with the current state, then whenever it
// changes. Returns a function that unsubscribes.
window.chatThing.onLoading(callback: (loading: boolean) => void): () => void;
const unsubscribe = window.chatThing.onLoading((loading) => {
document.querySelector("#my-loader").hidden = !loading;
});
Show a message preview bubble
// show a bubble above the chat button, after an optional delay in seconds
window.chatThing.showPreview(message: string, delay?: number): void;
// hide the bubble
window.chatThing.hidePreview(): void;
The bubble doesn't appear while the chat window is open. If you leave out delay, the widget's Message preview delay setting is used. To show the bot's welcome message as a bubble without code, see Customise the widget's appearance.
Change the theme
type TExtendThemeData = {
theme?: "dark" | "light";
colours?: {
primaryColour?: string;
primaryColourInverted?: string;
secondaryColour?: string;
secondaryColourInverted?: string;
};
};
window.chatThing.extendTheme(data: TExtendThemeData): void;
For example, follow your site's dark mode:
window.chatThing.extendTheme({ theme: "dark" });
Identify the user
If your visitors are signed in to your site, tell the bot who they are, so they don't have to introduce themselves.
type TIdentifyUserData = {
id?: string | number; // your own id for the user
name?: string;
email?: string;
tel?: string;
timeZone?: string; // IANA time zone, for example "Europe/London"
};
window.chatThing.identifyUser(data: TIdentifyUserData): void;
window.chatThing.identifyUser({
id: "user_1234",
name: "Alex Doe",
email: "alex@example.com",
});
What it does:
- The details are saved on the conversation with the next message the user sends, and appear with it in Chat Thing.
- The bot sees them, so it can greet the user by name or use their email without asking.
- They're included in your Conversation started and Conversation escalated webhooks as
userData. - If the bot has a pre-chat form, identifying the user skips it, and the details are used instead.
- Forms the bot shows in the chat, such as a booking form, are filled in with the details you provide.
timeZonehelps the bot work out dates and times, such as "3pm tomorrow". The widget sends the browser's time zone automatically, so you only need it to override that.
identifyUser doesn't verify anyone: any script on your page can call it with any details. Don't use it to decide what a user is allowed to see or do.
Reload the widget
window.chatThing.reload(): void;
Reloads the chat and its settings, for example after you've changed window.chatThingConfig. Register any onMessage and onLoading handlers again afterwards.
Turn on Advanced SDK features
extendContext(), systemMessage() and registerPowerUp() can change what your bot says and does, so they're off by default. To turn them on:
- Open your bot and go to the Channels tab.
- Open the settings for the website (web) channel.
- Under Advanced features, turn on Advanced SDK features.
- Click Update settings.
Only turn this on for pages you control
With Advanced SDK features on, any code running on a page where the widget is embedded can change the bot's behaviour, including replacing its instructions. Only turn it on if you control every page the widget appears on.
If the setting is off, these calls are ignored and an error is logged in the browser console.
Give the bot page context
Tell the bot what the user is looking at, so it can tailor its answers. The context is sent with each message.
type TMessageContext = {
pageMeta?: {
url: string;
title?: string;
description?: string;
"og:title"?: string;
"og:description"?: string;
};
custom?: Record<string, string | number | boolean>;
markdown?: string;
};
window.chatThing.extendContext(data: TMessageContext): void;
window.chatThing.extendContext({
custom: { product: "Blue mug", price: "£12", inStock: true },
});
Instead of calling extendContext yourself, you can let the widget collect context automatically with the context option in window.chatThingConfig. It updates whenever the page URL changes, and also needs Advanced SDK features turned on:
<script>
window.chatThingConfig = {
context: {
pageMeta: true, // send the page's URL, title and description
dataAttributes: true, // send the text of elements with data-chat-thing-context
custom: { plan: "Pro" },
markdown: "Extra information for the bot",
},
};
</script>
<!-- then your chat widget script -->
With dataAttributes: true, each element with a data-chat-thing-context attribute is sent as a custom value, named by the attribute and set to the element's text:
<span data-chat-thing-context="price">£12</span>
Change the bot's instructions for this page
// add instructions to the bot's existing ones
window.chatThing.systemMessage("extend", message: string): void;
// replace the bot's instructions entirely
window.chatThing.systemMessage("override", message: string): void;
window.chatThing.systemMessage(
"extend",
"The user is on the checkout page. Keep answers short and focused on payment and delivery.",
);
Add client-side power-ups
Client-side power-ups let the bot run functions on your page for the user, such as adding an item to their basket, filtering a list or opening a page. You describe the function and its inputs, and the bot decides when to call it.
window.chatThing.registerPowerUp(data: TRegisterPowerUpData): TRegisteredPowerUp;
type TRegisterPowerUpData = {
name: string; // shown to the bot, so make it clear
description: string; // tell the bot when to use it
handler?: (args: Record<string, any>) =>
string | number | boolean | object | Promise<string | number | boolean | object>;
timeoutMs?: number; // how long to wait for the handler: default 10000, from 1000 to 120000
} & (
| { parameters: Record<string, TPowerUpParameter>; inputSchema?: never }
| { inputSchema: Record<string, unknown>; parameters?: never }
);
type TPowerUpParameter =
| {
type: "string" | "number" | "boolean";
description: string;
required: boolean;
values?: (string | number | boolean)[]; // optional list of allowed values
}
| {
type: "enum";
description: string;
required: boolean;
values: (string | number | boolean)[];
}
| {
type: "object";
description: string;
required: boolean;
properties: Record<string, TPowerUpParameter>;
}
| {
type: "array";
description: string;
required: boolean;
items: TPowerUpParameter | TPowerUpParameter[];
};
type TRegisteredPowerUp = {
id: string;
enabled: boolean;
setEnabled: (enabled: boolean) => void; // turn it off and on again
destroy: () => void; // remove it
handler?: (args: Record<string, any>) => unknown;
};
Describe the inputs with either parameters (a short form, shown below) or inputSchema (raw JSON Schema), not both.
The handler's return value is passed back to the bot, so return something it can use: a short confirmation, the result, or an error message if something went wrong. If the handler returns nothing, the bot is told it succeeded. If it takes longer than timeoutMs, the bot is told the request timed out.
Example: add to basket
Say your site already has an addToCart function:
async function addToCart(itemId, qty) {
// your existing add-to-basket code
}
Register it as a power-up so the bot can use it:
const addToCartPowerUp = window.chatThing.registerPowerUp({
name: "Add to cart",
description: "Add a product to the user's shopping cart",
parameters: {
itemId: {
type: "string",
description: "The product's unique id",
required: true,
},
qty: {
type: "number",
description: "How many to add",
required: true,
},
},
handler: async ({ itemId, qty }) => {
try {
await addToCart(itemId, qty);
return `Added ${qty} of ${itemId} to the cart.`;
} catch (error) {
// tell the bot what went wrong so it can explain
return `Couldn't add to cart: ${error.message}`;
}
},
});
// later, turn it off or remove it
addToCartPowerUp.setEnabled(false);
addToCartPowerUp.destroy();
Limit a parameter to fixed values
When an input only accepts certain values, list them with values. The bot is told which values are valid, so it's far less likely to invent one.
window.chatThing.registerPowerUp({
name: "Filter products",
description: "Filter the product list by size",
parameters: {
size: {
type: "string",
description: "The size to filter by",
required: true,
values: ["small", "medium", "large"],
},
},
handler: async ({ size }) => filterProducts(size),
});
values works on string, number and boolean inputs. type: "enum" with a values list does the same thing.
Use raw JSON Schema
parameters covers types, descriptions, required inputs, nested objects, arrays and lists of values. For anything else, such as pattern, minimum, format, default, integer, oneOf or additionalProperties, use inputSchema and write JSON Schema directly. It's passed to the bot unchanged. It's also the easy option when you already have a schema, for example from zod or an MCP tool.
window.chatThing.registerPowerUp({
name: "Book a table",
description: "Reserve a table for the user",
inputSchema: {
type: "object",
properties: {
date: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$" },
seats: { type: "integer", minimum: 1, maximum: 8, default: 2 },
contact: { type: "string", format: "email" },
},
required: ["date", "seats"],
additionalProperties: false,
},
handler: async (args) => bookTable(args),
});
The schema is checked before it's used. It must:
- be valid JSON Schema
- survive being converted to JSON and back (no
undefined, functions orNaN) - only reference itself (
$refmust start with#) - stay within size limits: at most 4,000 characters of text, 300 values and 12 levels of nesting
A schema that breaks any of these is dropped along with its power-up. The limits exist because the schema is sent with every request for the rest of the conversation.
Connect WebMCP tools
If your page already exposes tools through WebMCP, the widget can offer them to the bot as client-side power-ups automatically. See WebMCP.
Troubleshooting
window.chatThing is undefined
The widget script hasn't loaded yet, or isn't on the page. Check the script tag is present (see Install the chat widget), and call the SDK after it has loaded. The SDK doesn't work with the iframe embed.
extendContext, systemMessage or registerPowerUp does nothing
Turn on Advanced SDK features for the bot's website channel. When it's off, the browser console logs an error saying the feature is disabled.
The bot says my power-up timed out
The handler took longer than timeoutMs (10 seconds by default). Raise timeoutMs for handlers that do real work, up to 120 seconds.
Related
- Add the chat widget to your website
Copy your bot's embed code and paste it into your site as a chat widget or embedded chat, with steps for WordPress, Shopify, Webflow, Wix and more.
- Customise the chat widget with code
Every chatThingConfig option for the Chat Thing widget - position, colours, launcher label, auto-open, per-language greetings and full CSS overrides.
- Connect WebMCP tools to your bot
Let your Chat Thing bot use the WebMCP tools your web page already exposes through document.modelContext, as client-side power-ups.
Last updated