---
title: "JavaScript SDK"
description: "Control the Chat Thing website widget with window.chatThing - open it, send messages, identify users, add page context and client-side power-ups."
canonical_url: "https://chatthing.ai/docs/developers/javascript-sdk"
last_updated: "2026-09-25"
---

# 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](https://chatthing.ai/docs/channels/website/install). 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](https://chatthing.ai/docs/channels/website/customise-with-code).

## Methods

| Method | What it does | Needs **Advanced SDK features** |
| --- | --- | --- |
| [`show()`, `hide()`, `toggle()`](https://chatthing.ai/docs/developers/javascript-sdk#open-and-close-the-chat-window) | Open, close or toggle the chat window | No |
| [`showTrigger()`, `hideTrigger()`, `toggleTrigger()`](https://chatthing.ai/docs/developers/javascript-sdk#show-or-hide-the-chat-button) | Show or hide the floating chat button | No |
| [`sendMessage()`, `newConversation()`](https://chatthing.ai/docs/developers/javascript-sdk#send-messages-and-start-new-conversations) | Send a message, or start a new conversation | No |
| [`getMessages()`, `onMessage()`](https://chatthing.ai/docs/developers/javascript-sdk#read-the-conversation) | Read the conversation | No |
| [`isLoading()`, `onLoading()`](https://chatthing.ai/docs/developers/javascript-sdk#track-when-the-bot-is-replying) | Know when the bot is replying | No |
| [`showPreview()`, `hidePreview()`](https://chatthing.ai/docs/developers/javascript-sdk#show-a-message-preview-bubble) | Show or hide a message bubble above the chat button | No |
| [`extendTheme()`](https://chatthing.ai/docs/developers/javascript-sdk#change-the-theme) | Change light or dark mode and colours | No |
| [`identifyUser()`](https://chatthing.ai/docs/developers/javascript-sdk#identify-the-user) | Tell the bot who the user is | No |
| [`reload()`](https://chatthing.ai/docs/developers/javascript-sdk#reload-the-widget) | Reload the widget and its settings | No |
| [`extendContext()`](https://chatthing.ai/docs/developers/javascript-sdk#give-the-bot-page-context) | Give the bot information about the page | Yes |
| [`systemMessage()`](https://chatthing.ai/docs/developers/javascript-sdk#change-the-bots-instructions-for-this-page) | Add to or replace the bot's instructions on this page | Yes |
| [`registerPowerUp()`](https://chatthing.ai/docs/developers/javascript-sdk#add-client-side-power-ups) | 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

```typescript
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:

```html
<button onclick="window.chatThing.show()">Chat with us</button>
```

## Show or hide the chat button

```typescript
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

```typescript
// 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.

```typescript
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;
```

```javascript
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

```typescript
// 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;
```

```javascript
const unsubscribe = window.chatThing.onLoading((loading) => {
  document.querySelector("#my-loader").hidden = !loading;
});
```

## Show a message preview bubble

```typescript
// 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](https://chatthing.ai/docs/channels/website/appearance).

## Change the theme

```typescript
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:

```javascript
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.

```typescript
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;
```

```javascript
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](https://chatthing.ai/docs/developers/webhooks#conversation-started-webhook) as `userData`.
- If the bot has a [pre-chat form](https://chatthing.ai/docs/channels/website/lead-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

```typescript
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.

```typescript
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;
```

```javascript
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:

```html
<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:

```html
<span data-chat-thing-context="price">£12</span>
```

## Change the bot's instructions for this page

```typescript
// 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;
```

```javascript
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.

```typescript
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:

```javascript
async function addToCart(itemId, qty) {
  // your existing add-to-basket code
}
```

Register it as a power-up so the bot can use it:

```javascript
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.

```javascript
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.

```javascript
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](https://chatthing.ai/docs/developers/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](https://chatthing.ai/docs/channels/website/install)), 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](https://chatthing.ai/docs/developers/javascript-sdk#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](https://chatthing.ai/docs/channels/website/install): 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](https://chatthing.ai/docs/channels/website/customise-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](https://chatthing.ai/docs/developers/webmcp): Let your Chat Thing bot use the WebMCP tools your web page already exposes through document.modelContext, as client-side power-ups.
