Log in

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

MethodWhat it doesNeeds Advanced SDK features
show(), hide(), toggle()Open, close or toggle the chat windowNo
showTrigger(), hideTrigger(), toggleTrigger()Show or hide the floating chat buttonNo
sendMessage(), newConversation()Send a message, or start a new conversationNo
getMessages(), onMessage()Read the conversationNo
isLoading(), onLoading()Know when the bot is replyingNo
showPreview(), hidePreview()Show or hide a message bubble above the chat buttonNo
extendTheme()Change light or dark mode and coloursNo
identifyUser()Tell the bot who the user isNo
reload()Reload the widget and its settingsNo
extendContext()Give the bot information about the pageYes
systemMessage()Add to or replace the bot's instructions on this pageYes
registerPowerUp()Let the bot run functions on your pageYes

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.
  • timeZone helps 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:

  1. Open your bot and go to the Channels tab.
  2. Open the settings for the website (web) channel.
  3. Under Advanced features, turn on Advanced SDK features.
  4. 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 or NaN)
  • only reference itself ($ref must 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.

  • 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