> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ada.cx/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ada.cx/_mcp/server.

# SDK API Reference

The Messaging Web SDK exposes an `adaEmbed` interface with corresponding [settings](#settings) and [actions](#actions). Get the interface from [`loadAdaMessaging`](/messaging/web/getting-started#install-from-npm) in the npm package, from `createAdaEmbedInterface` in the [module script](/messaging/web/getting-started#embed-with-a-module-script), or from the `window.adaEmbed` global that the Chat SDK script tag continues to provide.

All three paths return the same interface. The `window.adaEmbed` global remains fully supported for existing installs, but is deprecated for new installations, which should hold the interface in a private scope instead. See [Security best practices](/messaging/web/security-best-practices). The examples on this page call `window.adaEmbed` for brevity; substitute your own scoped reference.

## Frame layout

Module scripts, npm, and `embed2.js` use the same DOM IDs and default frame geometry.
Messaging creates `#ada-entry` with direct frame children. Existing page elements with matching IDs remain unchanged.
The container mounts inside `parentElement` when you set it. Otherwise, it mounts on the page body.

| Element           | ID                      | Class and name     |
| ----------------- | ----------------------- | ------------------ |
| Launcher frame    | `ada-button-frame`      | `ada-button-frame` |
| Chat frame        | `ada-chat-frame`        | `ada-chat-frame`   |
| Bubble frame      | `ada-intro-frame`       | `ada-intro-frame`  |
| Optional mask div | `ada-drawer-mask-frame` | None               |

The mask follows the launcher. A rule for `#ada-entry div:first-child` does not hide or transform the frames.

| Frame          | Default geometry                                                                                                                                                                                          |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Launcher       | Aligned edge `0`; `bottom:0`; 24px transparent band around the button, including room for the unread badge                                                                                                |
| Desktop drawer | Aligned edge `24px`; `bottom:24px`; `width:100%`; `max-width:min(375px, calc(100vw - 48px))`; `height:calc(var(--ada-messaging-viewport-height, 100vh) - 48px)`; `max-height:720px`; no resting transform |
| Mobile drawer  | `top:0`; `left:0`; `width:100%`; `height:var(--ada-messaging-viewport-height, 100%)`; no bottom inset                                                                                                     |
| Bubble         | Aligned edge `0`; bottom offset equals the visible launcher height minus 20px; 52px bottom band and 24px side band                                                                                        |

The launcher clips the transparent strip above the badge, so it does not intercept bubble clicks.
Page CSS can override frame offsets and dimensions. For example, `#ada-chat-frame { max-width:500px !important; }` widens the desktop drawer.
On mobile, `top` and `height` determine the drawer box. The SDK leaves `bottom` unset.
The viewport height updates when the keyboard changes the visible area.
Explicit custom-app frame dimensions, limits, and insets keep their [frame style API behavior](/messaging/custom-apps/operations#setframestyles).

## Settings

Configure the SDK by passing a settings object to [`start`](#start) or to `loadAdaMessaging`:

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  language: "fr",
});
```

Pages that load the SDK through the legacy script tag can keep defining a `window.adaSettings` object before the script loads. Both forms accept the same settings, and `window.adaSettings` remains fully supported for existing installs. For new installations it is deprecated: pass settings to `start()` or `loadAdaMessaging` instead. See [Security best practices](/messaging/web/security-best-practices#prefer-module-imports-for-new-installations).

> **Note**
>
> Settings merge in priority order: values passed to `start(settings)` override `window.adaSettings`. A few newer settings are read only from `start(settings)` and not from `window.adaSettings`; each one is marked below.

For legacy callback compatibility, `window.adaSettings.analyticsCallback` receives [`ada:analytics`](#subscribeevent) events as `{ event: string; data: object }`.
For console diagnostics, see [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget).

The following are all of the available settings for the Messaging Web SDK.

### adaReadyCallback

`adaReadyCallback?(params: { isRolledOut: boolean }): void;`

Specifies a callback function that the SDK calls when it has finished setting up. `isRolledOut` is `false` when the browser session falls outside the configured [rollout](#rolloutoverride) percentage; in that case the widget does not render.

**`HTML`**

```html HTML
<script type="text/javascript">
  window.adaSettings = {
    adaReadyCallback: ({ isRolledOut }) => {
      console.log("Ada is ready. Rolled out:", isRolledOut);
    }
  }
</script>
```

The equivalent event is [`ada:start`](#subscribeevent), which delivers the same `{ isRolledOut }` payload to subscribers.

### align

`align?: "right" | "left" | "auto";`

Sets the side of the page that the launcher button and the chat drawer anchor to. Defaults to `"right"`. With `"auto"`, the SDK anchors left when the configured [`language`](#language) is Arabic (`ar`) or Hebrew (`he`), and right otherwise.

### allowMetaFieldsInReset

`allowMetaFieldsInReset?: boolean;`

Controls whether a full [`reset({ metaFields })`](#reset) applies caller-supplied meta fields to the new session. Defaults to `true`, so reset meta fields apply unless you opt out. Set it to `false` to restore the meta fields you passed to `start()` instead.

Calling `adaEmbed.stop()` and then `adaEmbed.start()` creates a new client that can use different settings. Scripts with a client reference can still call `start()`, `setMetaFields()`, and `setSensitiveMetaFields()`. This setting does not authenticate the script that calls these methods.

History-preserving resets (`resetChatHistory: false`) apply reset meta fields regardless of this setting, because the current end user session survives the reset.

### appUrl

`appUrl?: string;`

URL of a customer-hosted conversation app that the SDK mounts in place of Ada's default conversation UI.

Your handle's **Allowed websites** list controls custom apps. In your dashboard, go to **Channels** > **Messaging**. Before your transition, use **Channels** > **Chat**. Add your app's origin. A URL on an origin that the list does not allow is dropped with a console warning, and the default app mounts instead. An empty list disables custom apps for the handle. For local development, a loopback `appUrl` is allowed implicitly when the page that embeds the widget also runs on a loopback host, so pure local development needs no entry. A deployed page gets no implicit loopback allowance: expose your development server through an `https:` tunnel and add the tunnel origin to Allowed websites, because the dashboard accepts only `https://` entries. Chromium 142 and later, and Firefox 149 and later, also gate the loopback navigation behind a Local Network Access permission prompt. The SDK delegates that permission down its frame chain for a loopback `appUrl`, and never for other values. Set `appUrl` in the settings that the page starts with. Click **Allow** when the browser asks. A missing or denied permission surfaces as a `handshake_timeout` fallback. The `https:` tunnel path needs no permission and works in every browser. See [Local development](/messaging/custom-apps/getting-started#local-development).

The value must be an absolute `https:` URL without embedded credentials, on an origin you control. For local development, `http:` is accepted on loopback hosts such as `localhost` and `127.0.0.1`. Any other value (non-loopback `http:`, `javascript:`, `data:`, relative, credentialed, or on an Ada-hosted origin such as `*.ada.support`) is dropped with a console warning, and the default app mounts instead. The SDK validates the URL again inside its core frame, so the check cannot be bypassed from the page. The core-frame check also rejects a URL on the Messaging frame's own origin, and enforces the Allowed websites check after the handle's configuration loads.

The custom app frame:

* Runs in a sandboxed iframe **with** `allow-same-origin`, so the document keeps its own origin. Cookies and storage work inside the frame, partitioned by the embedding site. See [Sandbox, origin, and storage](/messaging/custom-apps/overview#sandbox-origin-and-storage).
* Receives all conversation state through `@ada-cx/messaging-bridge`. The SDK never appends query parameters or tokens to a custom app URL.
* Must complete the bridge `app.initialize` handshake within 15 seconds after core mounts the frame. The budget includes loading your document. The handshake requires `document.referrer`: the SDK mounts the frame with `referrerpolicy="origin"`, so your app document always receives the Messaging frame's origin, and never its full URL. This includes apps served over `http:` on a loopback host during local development. A browser extension or proxy that strips the `Referer` header entirely breaks the handshake, and the SDK falls back to the default app.
* Stays pinned to its first document. A navigation or reload, even to the same origin, disconnects the bridge permanently.

When the custom frame fails to load or complete its handshake, or when its origin is not allowed by your handle's Allowed websites list, the SDK publishes the [`ada.customApp.fallback`](#subscribeevent) event with `{ appUrl, reason, code, retryable }` and logs a console warning with the reason. It destroys the failed frame and mounts the default app in its place. After one of these fallbacks, the SDK stays on the default app for the rest of the page's lifetime. Set [`appUrlFallback`](#appurlfallback) to `false` to fail closed instead.

One fallback reason is transient: the handle's configuration did not load in time to verify the Allowed websites list. That fallback applies only to the one mount attempt. The next mount after a `reset()` checks the loaded list again.

`appUrl` and `appUrlFallback` are locked on the first configuration. A runtime re-configuration or `reset()` cannot redirect the app frame to a different URL.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  appUrl: "https://chat-app.example.com/ada-frame/",
});

window.adaEmbed.subscribeEvent("ada.customApp.fallback", ({ appUrl, code, reason }) => {
  console.log("Custom app fell back to default:", appUrl, code, reason);
});
```

> **Note**
>
> This setting is read only from `start(settings)` or the `loadAdaMessaging` settings object. It is not read from `window.adaSettings`.

### appUrlFallback

`appUrlFallback?: boolean;`

Controls what happens when a custom [`appUrl`](#appurl) frame fails to load, fails its bridge handshake, or fails the Allowed websites check. It also applies when your handle's configuration does not load in time to verify the Allowed websites list. Defaults to `true`, which mounts Ada's default app in place of the failed custom frame.

When set to `false`, the SDK does not fall back. The activation fails through the normal activation error path, and later activation attempts retry the custom URL until three consecutive default-app failures latch the activation. The [`ada.activation.error`](#subscribeevent) event's data then carries a `code` field with the same values as the [`ada.customApp.fallback`](#subscribeevent) event. In fail-closed mode, the next activation retries every cause, including `origin_not_allowlisted`. The `retryable` flag exists only on the fallback event.

Only meaningful together with `appUrl`. Like `appUrl`, this setting is read only from `start(settings)`, not from `window.adaSettings`, and is locked on the first configuration.

### branding

`branding?: AdaMessagingBranding;`

Overrides the appearance settings from your Ada dashboard for the page that sets it. Use it when one AI Agent serves several brands, and each brand's site needs its own name, colors, and launcher. Every field is optional. A field that you do not set keeps its dashboard value.

| Field                | Type                                                            | Overrides                                                                                              |
| :------------------- | :-------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------- |
| `aiAgentDescription` | [`AdaMessagingLocalizedText`](#adamessaginglocalizedtext)       | The description under the Agent's name, at most 300 characters per language. Turns the description on. |
| `aiAgentName`        | [`AdaMessagingLocalizedText`](#adamessaginglocalizedtext)       | The Agent's name in the chat header, at most 100 characters per language.                              |
| `avatarUrl`          | `string`                                                        | The Agent's avatar image. Switches the avatar from a letter to the image.                              |
| `cornerStyle`        | `"round" \| "rectangular"`                                      | The corner style of message bubbles and the composer.                                                  |
| `headerColor`        | `string`                                                        | The header background color. Turns header coloring on.                                                 |
| `headerTextColor`    | `string`                                                        | The header text and icon color. Turns header coloring on.                                              |
| `launcher`           | [`AdaMessagingBrandingLauncher`](#adamessagingbrandinglauncher) | The launcher button's background color, icon, size, and text label.                                    |
| `textSize`           | `"small" \| "default" \| "large"`                               | The base text size. An end user's own text size preference still takes priority.                       |
| `tintColor`          | `string`                                                        | The accent color of the chat window. The launcher keeps its own color.                                 |

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  branding: {
    aiAgentName: { en: "Aurora Concierge", fr: "Concierge Aurora" },
    aiAgentDescription: { en: "Help for Aurora guests" },
    avatarUrl: "https://aurora.example.com/avatar.png",
    headerColor: "#123456",
    headerTextColor: "#ffd166",
    cornerStyle: "rectangular",
    tintColor: "#1a73e8",
    launcher: { backgroundColor: "#123456", size: 56 },
  },
});
```

The SDK checks each field and ignores a field that fails, with a console notice that names the field and the rule. The other fields still apply. The notice never includes the value that you set. The rules are:

* Colors are 3- or 6-digit hex values, for example `#1a73e8`.
* URLs are absolute `https:` URLs.
* `launcher.size` is an integer from 32 to 120.
* Text fields are objects that map a language code to a string. Each map must include the Agent's default language. A text field without an entry for that language is ignored.
* `headerTextColor` needs a header background color, from `headerColor` or from a dashboard that has header coloring turned on. Without one, it is ignored.

A key that is not part of `branding` is ignored with a console notice.

Text fields show the end user's current language, or the Agent's default language when the map has no entry for it. `aiAgentName` and `aiAgentDescription` update when the language changes, for example after [`setLanguage`](#setlanguage). The launcher text also updates when the language changes. The dashboard's launcher text behaves the same way.

When `launcher` includes both `iconUrl` and `text`, the launcher shows the text. `launcher.backgroundColor` changes the launcher color. `tintColor` does not.

`branding` stays in effect after [`reset`](#reset) and [`deleteHistory`](#deletehistory). A [custom app](#appurl) receives the resulting values in the same state keys that carry the dashboard values, so it needs no changes to show them.

### chatterTokenCallback

`chatterTokenCallback?(chatterToken: string): void;`

Specifies a callback for when the end user token has been set. The equivalent event is [`ada:chatter_token`](#subscribeevent).

### cluster

`cluster?: string | null;`

Specifies the regional cluster your AI Agent runs on. Accepts either a regional segment (for example, `"eu"`) that the SDK combines with [`domain`](#domain), or a full base domain (for example, `"eu.ada.support"`) used as-is.

**Set this only if your Agent is hosted on a non-default cluster.** If the Agent is on the default cluster, leave this unset.

> **Info**
>
> Do not change this value unless instructed by your Ada team.

### conversationEndCallback

`conversationEndCallback?(event?: Record<string, unknown>): void;`

Specifies a callback function that the SDK calls when the end user ends the conversation. The callback receives the same payload as the [`ada:end_conversation`](#subscribeevent) event.

**`HTML`**

```html HTML
<script type="text/javascript">
window.adaSettings = {
  conversationEndCallback: (event) => {
    // perform an action after the end user has ended the conversation
  }
};
</script>
```

### crossWindowPersistence

`crossWindowPersistence?: boolean;`

Persists the chat drawer open state across page refreshes and tabs while a live agent conversation is active, using the `Window.sessionStorage` API. When the browser is closed, the state is forgotten. Has no effect in [`headless`](#headless) or [`parentElement`](#parentelement) mode.

Defaults to `true`. Set to `false` to disable.

`window.adaSettings`, `new AdaMessagingClient(config)`, and `adaEmbed.start(settings)` also accept the legacy object form `{ enabled: boolean }`.
Set `enabled: false` to disable persistence.
For invalid values, the SDK logs a console warning once per page load.
If a later `start()` call passes an invalid value, the SDK keeps the earlier setting or `window.adaSettings` fallback.
Invalid constructor values preserve the `window.adaSettings` fallback.
Without a valid setting, persistence defaults to on.

> **Note**
>
> On the legacy Chat SDK this behavior was opt-in. On the Messaging SDK it is on by default.
> The legacy Chat SDK enables persistence for `{ enabled: false }`. The Messaging SDK disables it.

### domain

`domain?: string;`

Overrides the Ada base-domain segment (for example, `"ada"`). The SDK combines it with [`handle`](#handle) and any regional [`cluster`](#cluster) into the API host `https://<handle>.[<cluster>.]<domain>.support`. For a full URL override, use [`endpoint`](#endpoint) instead.

Unless directed by an Ada team member, you do not need to change this value.

### enableProgrammaticControl

`enableProgrammaticControl?: boolean;`

Opt-in to the transcript-bearing programmatic surface. When set to `true`, your page can:

* Call [`getConversation`](#getconversation), [`getMessages`](#getmessages), [`selectQuickReply`](#selectquickreply), [`sendMessage`](#sendmessage), [`setComposerText`](#setcomposertext), [`setDelegate`](#setdelegate), and [`triggerPlaybook`](#triggerplaybook).
* Receive the [`ada:message:sent`](#subscribeevent) and [`ada:message:received`](#subscribeevent) events on host-page subscribers.

While this setting is `false` (the default), those methods reject with `ProgrammaticControlNotEnabled` and the events are not delivered to host subscribers. The value is locked on the first configuration and cannot be changed at runtime.

> **Warning**
>
> Every script that lives in your host page (analytics tags, GTM, ad pixels, session replay tools, and any third-party SaaS pixel) inherits your page's trust and can subscribe to these events or call these methods. They will see full message bodies, including any PII your end users type. Have your product and security teams accept that exposure risk explicitly before turning this on.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  enableProgrammaticControl: true,
});
```

### endpoint

`endpoint?: string;`

Full base URL for the Ada API. Overrides all default host derivation from [`handle`](#handle), [`cluster`](#cluster), and [`domain`](#domain).

> **Info**
>
> Do not set this value unless instructed by your Ada team.

### eventCallbacks

`eventCallbacks?: Record<string, AdaSdkEventCallback>;`

A map of event key to callback, registered eagerly before the first startup event fires. Two kinds of keys are supported:

* Public `ada:*` event keys, registered exactly like [`subscribeEvent`](#subscribeevent) subscriptions.
* Custom JavaScript event names configured in your Ada dashboard, keyed by the event's `event_name`. The key `"*"` receives every custom JavaScript event.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  eventCallbacks: {
    "ada:end_conversation": (data) => console.log("Conversation ended", data),
    my_custom_event: (data) => console.log("Custom JS event", data),
  },
});
```

### greeting

`greeting?: string;`

Customizes the greeting that new end users see. The value is the response id of the Answer to trigger, which you can find in the URL of the corresponding Answer in the dashboard. This is useful for setting page-specific greetings across your app.

### handle

`handle?: string;`

Your AI Agent's handle. Required: pass it in `start(settings)`, in the `loadAdaMessaging` settings, or in `window.adaSettings`. Pages migrating from the Chat SDK script tag can keep the `data-handle` attribute on that script.

### headless

`headless?: boolean;`

When set to `true`, the SDK connects to the AI Agent in the background without rendering the default launcher button, intro popup, or chat drawer. Use this when your page renders its own chat UI and drives the conversation through `getConversation`, `getMessages`, `selectQuickReply`, `sendMessage`, and `subscribeEvent`.

Headless sessions are typically paired with [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Without it, the methods needed to drive a custom UI reject at runtime.

Methods that act on the built-in UI are not available in headless mode:

* [`setComposerText`](#setcomposertext) rejects with `HeadlessModeError`. There is no built-in composer to write to.
* [`toggle`](#toggle), [`open`](#open), and [`close`](#close) are no-ops. There is no drawer to open or close.
* [`isOpen`](#isopen) always resolves `false`.

In headless mode, call [`start`](#start) to activate the session, or let the first [`sendMessage`](#sendmessage) activate it for you.

> **Note**
>
> Headless sessions run with no visual indicator. The end user token is created and persisted to local storage the same way as a visible session. If your page is compromised, an attacker can run conversation activity under the visiting user's token without any user-facing signal. Host pages should treat enabling headless as a meaningful trust decision.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  headless: true,
  enableProgrammaticControl: true,
});
```

### hideMask

`hideMask?: boolean;`

Controls the page mask behind the open drawer on desktop. By default, no mask appears, and users can interact with your page while the chat window is open.

Set `hideMask` to `false` to show the mask. The mask dims your page, and a click on it minimizes the drawer instead of reaching the page.

> **Note**
>
> Leave `hideMask` unset, or set it to `true`, when users need to navigate the page while the chat window is open.

### identityToken

`identityToken?: string;`

A short-lived, single-use identity token that your backend mints through Ada's Platform API for a known end user. Pass it here to start an identified session instead of an anonymous one. See the [identity guide](/messaging/identity/getting-started) for the full flow.

Your server can supply optional `verified_email` and `name` values when it mints the token.
Ada links only when the mint request includes `verified_email`.
Without either field, mint, exchange, and handoff keep their existing behavior.
Ada stores the values encrypted on the session's end user.
Ada uses them only in the first 7 days after the exchange.
Ada deletes them after the first Zendesk Messaging handoff uses them and Sunshine initialization succeeds.
If no handoff uses them, Ada deletes them at the next handoff or token exchange after the 7 days end.

How the exchange works:

* The SDK exchanges the token once with the AI Agent backend, before it restores any persisted session state. The exchange request carries the token in the JSON body as `identity_token`. The token never appears in a URL.
* The token is **one-shot**. After a successful exchange, the SDK clears its cached copy and the server will not accept the same token again. A later [`reset()`](#reset) without a new token starts an anonymous session.
* To identify a new user (for example, after a login change), pass a fresh token to `reset({ identityToken })`. A reset that carries an identity token is always a full reset.

On exchange failure, the SDK clears identity-sensitive storage and continues with an anonymous session. It publishes [`ada:identity_token:error`](#subscribeevent) with a `reason` from the following list. An expired token additionally publishes [`ada:identity_token:expired`](#subscribeevent).

| Reason                             | Description                                                      |
| :--------------------------------- | :--------------------------------------------------------------- |
| `chatter_not_found`                | The token names an end user that does not exist.                 |
| `identity_token_already_used`      | The token has already been used.                                 |
| `identity_token_exchange_failed`   | The exchange failed.                                             |
| `identity_token_expired`           | The token has expired.                                           |
| `identity_token_state_unavailable` | The exchange could not complete. Retry later with a fresh token. |
| `invalid_identity_token`           | The token is invalid.                                            |
| `missing_identity_token`           | The request has no identity token.                               |
| `not_configured`                   | The identity service is unavailable.                             |

**`JavaScript`**

```javascript JavaScript
const { token } = await fetchIdentityTokenFromYourBackend();

await window.adaEmbed.start({
  handle: "<your-handle>",
  identityToken: token,
});

window.adaEmbed.subscribeEvent("ada:identity_token:error", (data) => {
  console.warn("Identity exchange failed:", data.reason);
});
```

### language

`language?: string;`

Sets the initial display language. Takes a language code in [ISO 639-1 format](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), for example `"fr"`.

To use the same language for AI Agent replies, first turn languages on in your dashboard, under **Customization** > **Languages**. See [Support multiple languages in the same AI Agent](/docs/setup/languages/about-multilingual-support).

On each page load, a configured `language` takes precedence over a saved language choice. A language picker or [`setLanguage`](#setlanguage) change during that visit is still saved when session persistence is enabled. The configured language takes precedence again on the next load. Omit this setting to allow a saved choice to restore.

Pages on the same site share the saved session for an AI Agent. If some pages configure `language` and others omit it, a configured page can change the saved language used by the other pages.

When a configured page activates and restores that session, it synchronizes the configured language if the Agent supports it. If the reply language changes, other open tabs using that session apply the update live, even during a conversation. An embedded widget can activate automatically on page load; a floating widget's initial load alone does not send this update.

Later language updates from the Agent or another tab sharing the session can change the display language. With session persistence enabled, the widget saves its current display language.

If the Agent does not support the display language, or the language update fails, the display language can differ from the Agent's reply language.
An unsupported configured value can still replace a supported saved choice when session persistence is enabled.

> **Check**
>
> Alternatively, you can use the [setLanguage](#setlanguage) action, which changes the language without clearing the chat history.

### lazy

`lazy?: boolean;`

Defers loading until your page calls `start()`.

* On the Chat SDK script tag, add the `data-lazy` attribute to the `<script id="__ada">` element.
* Without that element, set `lazy: true` in `window.adaSettings`.

Passing `lazy` to `start()` or `loadAdaMessaging` has no effect. Those calls start the SDK.
Call `start()` before `open()` or `triggerProactive()` on the `adaEmbed` interface.

### metaFields

`metaFields?: Record<string, MetaFieldValue>;`

Use `metaFields` to pass information about an end user to Ada. This can be useful for tracking information about your users, as well as personalizing their experience. For example, you may wish to track the `phone_number` and `name` for conversation attribution. (See [Variables](https://docs.ada.cx/variables) for more information.)

Values must be a string, number, boolean, or `null`. The SDK has no metadata count limit. It accepts keys up to 256 characters and string values up to 100,000 characters. It drops longer keys and truncates longer values with a console notice.

The SDK sets `initialURL`, `introShown`, and `test_user` itself. It ignores these keys when your page passes them. `getMetaFields` can return them. To mark a test user, use [`testMode`](#testmode).

To change these values after setup, use the [setMetaFields](#setmetafields) action.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  metaFields: {
    phone_number: "(123) 456-7890",
    name: "Ada Lovelace",
  },
});
```

> **Note**
>
> Meta field keys should not include whitespace, emojis, special characters, or periods.

### onAdaEmbedLoaded

`onAdaEmbedLoaded?(): void;`

Called when the embed interface is ready to be used, before startup events fire. This is useful for [subscribing to events](#subscribeevent). Subscribing here ensures that subscriptions are in place before events are triggered.

**`HTML`**

```html HTML
<script type="text/javascript">
window.adaSettings = {
  onAdaEmbedLoaded: () => {
    window.adaEmbed.subscribeEvent("ada:ready", (data) => {
      console.log("Chat is ready in mode:", data.mode);
    });
  }
};
</script>
```

The equivalent event is [`ada:embed_loaded`](#subscribeevent).

### parentElement

`parentElement?: string | HTMLElement;`

Specifies where to mount the conversation frame if the default side drawer is not desired. Accepts the `HTMLElement` or element `id` of the desired parent element.

**`HTML`**

```html HTML
<head>
  <!-- ... -->
  <script type="text/javascript">
    window.adaSettings = {
      parentElement: "custom-iframe"
    }
  </script>
</head>
<body>
  <!-- ... -->
  <div id="custom-iframe"></div>
</body>
```

In `parentElement` mode, [`toggle`](#toggle) throws, and [`open`](#open) and [`close`](#close) do not show or hide a drawer. The [`WindowInfo`](#windowinfo) field `isDrawerOpen` is always `false`.

> **Warning**
>
> Chat renders immediately inside the `parentElement` provided. This means that the conversation also gets initialized. Avoid initializing the SDK with the `parentElement` setting automatically on every page, since it could lead to low engagement rates.

### privateMode

`privateMode?: boolean;`

Puts the SDK into private mode when set to `true`. In private mode, the SDK stores nothing in the browser. It forgets the conversation on every refresh or tab close, even during a live agent handoff. This is not the same as the [customer persistence](https://docs.ada.cx/docs/generative/set-up-your-ai-agent-s-knowledge-and-behavior/manage-your-ai-agent-s-behavior/manage-when-your-ai-agent-forgets-customers/) setting **Forget After Reload**.

That setting also forgets the conversation on refresh, except during a live agent handoff. A same-tab refresh during a handoff keeps the same agent conversation. It forgets the conversation when the tab closes.

### rolloutOverride

`rolloutOverride?: number;`

Overrides the rollout value set in the dashboard for this session. Useful if you need page-specific rollout values, or together with [`testMode`](#testmode) to always show the widget. For testing and QA purposes only.

Accepts a fraction between `0` and `1`. For backward compatibility, a `window.adaSettings` value above `1` is treated as a legacy percentage (for example, `50` becomes `0.5`).

### sensitiveMetaFields

`sensitiveMetaFields?: Record<string, MetaFieldValue>;`

Like [metaFields](#metafields), you can use `sensitiveMetaFields` to pass information about an end user to Ada. With `sensitiveMetaFields`, however, values are:

* encrypted immediately upon entering the Ada system
* temporarily stored in a cache on the Ada side, that is cleared after 24 hours
* not stored in the database
* redacted on the dashboard

Sensitive values are delivered over the SDK's secure storage transport and are never readable back through [`getMetaFields`](#getmetafields).

To change these values after setup, use the [setSensitiveMetaFields](#setsensitivemetafields) action.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  sensitiveMetaFields: {
    jwt_token: "xxxxx.yyyyy.zzzzz",
  },
});
```

> **Note**
>
> Meta field keys should not include whitespace, emojis, special characters, or periods.

### showFallbackOnTimeout

`showFallbackOnTimeout?: boolean;`

When `true` (the default), the SDK shows a self-contained fallback document in the chat frame if chat activation exceeds the timeout budget and no exact [`ada:chat_frame_timeout`](#subscribeevent) subscriber has taken over handling. A `subscribeAll()` listener does not count: it receives the event and still gets the fallback. Set to `false` to suppress the built-in fallback and handle timeouts yourself. The fallback is a `data:` document, so a `frame-src` that allows only the Ada asset host blocks it.

### styles

`styles?: Record<string, string>;`

> **Warning**
>
> `styles` is deprecated and has no effect. To change the accent color, set [`branding.tintColor`](#branding).

The SDK ignores `styles` in every form, and the legacy `embedStyles` key in `window.adaSettings`. It shows a console notice for each one that it reads.

### testMode

`testMode?: boolean;`

Marks the end user as a test user. The session keeps the real transport and backend; the SDK stamps the `test_user: 1` meta variable on the session so analytics can exclude it. Rollout percentage still applies; set [`rolloutOverride`](#rolloutoverride) to `1` to always show the widget.

### theme

`theme?: "light" | "dark" | "auto";`

Color scheme for the chat widget. When set to `"auto"` (the default), the widget follows the user's OS preference via `prefers-color-scheme`. This overrides the server-configured theme. Change it at runtime with [`setTheme`](#settheme).

> **Note**
>
> This setting is read only from `start(settings)` or the `loadAdaMessaging` settings object. It is not read from `window.adaSettings`.

### toggleCallback

`toggleCallback?(isDrawerOpen: boolean): void;`

Use this setting to trigger side effects when the chat drawer is opened or closed. The equivalent event is [`ada:toggle`](#subscribeevent).

### zdChatterAuthCallback

`zdChatterAuthCallback?(callback: (token: string) => void): void;`

Use `zdChatterAuthCallback` for Zendesk Chat or Zendesk Messaging end user authentication. Request a JWT from your server and pass it to the callback.

**`JavaScript`**

```javascript JavaScript
window.adaSettings = {
  handle: "<your-handle>",
  zdChatterAuthCallback: (callback) => {
    // Request a fresh JWT token, then pass it to Ada
    const token = "token goes here";
    callback(token);
  }
};
```

For Zendesk Messaging, return your Zendesk Messaging authentication JWT. Sign it with a Zendesk Messaging signing key using HS256.
Include a `kid` header and the `scope: "user"`, `external_id`, and `exp` claims.
The `external_id` must be a non-empty string of at most 255 characters.
It must not contain `/`, `?`, `#`, `%`, whitespace, or control characters.
Send the required `external_id` claim and the optional `name`, `email`, and `email_verified` claims.

Verified email linking requires the Messaging SDK.

> **Warning**
>
> Ada links to or creates a Zendesk user from the email.
> A wrong email links the conversation to another person's Zendesk user.
>
> [Read all risks](/docs/handoffs/zendesk/zendesk-messaging#authenticate-end-users)

Ada uses `name` and `email` for display and linking only when the JWT has an `email` with boolean `email_verified: true`.
Without a verified email, Ada uses only `external_id`.
With a verified email, Ada writes META `jwt_name` and `jwt_email`, as it does for Zendesk Chat.
Ada updates `name` only when it is empty or still equals the previous JWT's name.
Ada never writes META `email` from these claims.
This preserves the email that your page or a **Capture** block sets.

With a verified email, Ada first uses a Zendesk end user with the same `external_id` as the JWT.
With `email_verified: true`, Ada next matches exactly one Zendesk end user by primary email.
Ada trims whitespace and converts the email to lowercase before matching.
Ada uses the matched user's external ID or sets it to the JWT's `external_id` if missing.
When Ada sets a missing external ID on the Zendesk user, Zendesk keeps that link for later conversations.

If a verified email has no match, Ada creates a Zendesk end user with the JWT's name and email.
If the JWT has no name, Ada uses the email as the name.

If Ada cannot select or create a Zendesk end user, Ada uses the JWT's `external_id`.

Ada uses the JWT's email to match or create a Zendesk user only when `email_verified` is `true`.
Ada never links agents or admins, or a match that is not exact.
See [Authenticate end users](/docs/handoffs/zendesk/zendesk-messaging#authenticate-end-users) for the full matching rules.

The SDK requests the token at startup.

After successful verification, the server stores the identity and its validity window on the end user record.
Each successful startup check renews the window for 24 hours.
Browser metadata cannot supply or change these values.

If authentication fails and no other identity mapping applies, the Handoff proceeds with an anonymous Sunshine user.
When a user signs out, call [`reset()`](#reset) so the next user starts a new conversation and a new Zendesk user.

## Actions

You can call the following actions on the `adaEmbed` interface. Every action except [`setDelegate`](#setdelegate) returns a Promise.

These actions work before `start()`:

* [`createProactive`](#createproactive)
* [`destroy`](#destroy)
* [`evaluateCampaignConditions`](#evaluatecampaignconditions)
* [`setDelegate`](#setdelegate)
* [`start`](#start)
* [`subscribeAll`](#subscribeall)
* [`subscribeEvent`](#subscribeevent)
* [`triggerCampaign`](#triggercampaign)
* [`unsubscribeEvent`](#unsubscribeevent)

Other actions reject until `start()` runs, with this message:

```text
Actions cannot be called until Embed has been instantiated. Try running adaEmbed.start({...}).
```

> **Note**
>
> The Messaging SDK supports generative AI Agents only. The scripted-campaign actions [`createProactive`](#createproactive), [`evaluateCampaignConditions`](#evaluatecampaignconditions), and [`triggerCampaign`](#triggercampaign) are deprecated. Calls resolve without effect. The SDK logs a console warning when SDK diagnostics are turned on. See [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget). To start a proactive conversation, use [`triggerProactive`](#triggerproactive).

### close

`close(): Promise<void>;`

Closes the chat drawer and restores the launcher button. A no-op when the chat is already closed, in [`headless`](#headless) mode, and in [`parentElement`](#parentelement) mode.

This action emits neither [`ada:minimize_chat`](#subscribeevent) nor [`ada:close_chat`](#subscribeevent). It still emits `ada:toggle(false)` and calls `toggleCallback(false)`.

### closeCampaign

`closeCampaign(): Promise<void>;`

Closes the currently displayed proactive message bubble. Does nothing if no proactive bubble is currently displayed.

### createProactive

`createProactive(body: string, duration?: number, responseId?: string): Promise<void>;`

> **Note**
>
> This action is deprecated on the Messaging SDK. Calls resolve without effect. The SDK logs a console warning when SDK diagnostics are turned on. See [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget). Use [`triggerProactive`](#triggerproactive) instead.

### deleteHistory

`deleteHistory(): Promise<void>;`

Permanently deletes the end user's conversation history and persisted state, and resets the session. When the end user opens a new chat window, a new end user token is generated. The promise resolves once the SDK confirms the persisted clear is durable.

### destroy

`destroy(): Promise<void>;`

Tears down all SDK frames, channels, and listeners, and clears every event subscription and the delegate. Use [`stop`](#stop) instead when you plan to start again and want subscriptions to survive.

### evaluateCampaignConditions

`evaluateCampaignConditions(options?: Record<string, unknown>): Promise<void>;`

> **Note**
>
> This action is deprecated on the Messaging SDK. Calls resolve without effect. The SDK logs a console warning when SDK diagnostics are turned on. See [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget). Use [`triggerProactive`](#triggerproactive) instead.

### getConversation

`getConversation(): Promise<ConversationInfo>;`

Returns information about the active conversation: its id (or `null` if the end user has not sent or received any messages yet) and the live agent handoff state. See [ConversationInfo](#conversationinfo) for the full shape.

Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Otherwise rejects with `ProgrammaticControlNotEnabled`. Rejects with a timeout error when the chat frame does not respond in time.

**`JavaScript`**

```javascript JavaScript
const conversation = await window.adaEmbed.getConversation();
console.log("Conversation id:", conversation.id);
if (conversation.handoff.inLiveChat) {
  console.log("Talking to agent:", conversation.handoff.agent?.name);
}
```

### getInfo

`getInfo(): Promise<WindowInfo>;`

Returns a [`WindowInfo`](#windowinfo) object containing information about the current client state.

**`JavaScript`**

```javascript JavaScript
const windowInfo = await window.adaEmbed.getInfo();
console.log("Is the drawer open?", windowInfo.isDrawerOpen ? "Yes" : "No");
```

### getMessages

`getMessages(): Promise<PublicMessage[]>;`

Returns the messages currently held in the chat session, in the order they were sent. See [PublicMessage](#publicmessage) for the field shape.

Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Otherwise rejects with `ProgrammaticControlNotEnabled`.

Important behaviors:

* **In-memory only.** This is the transcript the SDK holds for the current end user. There is no server-side history endpoint or pagination. Refreshing the page resets the list to whatever the SDK rehydrates from the persisted session.
* **Filtered to conversational messages.** Presence notifications (typing, conversation-ended), system events, and other non-conversational entries are excluded. Every returned message has one of the `role` values documented in [PublicMessage](#publicmessage).

**`JavaScript`**

```javascript JavaScript
const messages = await window.adaEmbed.getMessages();
messages.forEach((m) => console.log(`[${m.role}] ${m.body ?? "<no body>"}`));
```

### getMetaFields

`getMetaFields(): Promise<Record<string, MetaFieldValue>>;`

Returns the non-sensitive meta fields currently set on the active session: the values passed in settings plus any subsequent [`setMetaFields`](#setmetafields) updates. Sensitive meta fields are never returned.

**`JavaScript`**

```javascript JavaScript
const meta = await window.adaEmbed.getMetaFields();
```

### handleNotification

`handleNotification(): Promise<void>;`

Opens the chat drawer in response to a notification the host page handled itself, for example a push notification tap. A no-op in [`headless`](#headless) mode.

> **Note**
>
> On the legacy Chat SDK, this action toggled the drawer only when the chat was closed. On the Messaging SDK it calls [`open`](#open) unconditionally, which is a no-op when the drawer is already open. The end state is the same.

### isOpen

`isOpen(): Promise<boolean>;`

Resolves to `true` when the chat is currently visible to the end user, and `false` otherwise. Always resolves `false` in [`headless`](#headless) mode.

**`JavaScript`**

```javascript JavaScript
if (!(await window.adaEmbed.isOpen())) {
  window.adaEmbed.toggle();
}
```

### open

`open(): Promise<void>;`

Opens the chat drawer. A no-op when the chat is already open and in [`headless`](#headless) mode.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.open();
```

### requestNotifications

`requestNotifications(): Promise<NotificationPermission | null>;`

Requests browser web-notification permission. Call it from a user gesture, such as a button click, per browser policy. Resolves the resulting permission (`"granted"`, `"denied"`, or `"default"`), or `null` when the browser does not support the Notification API.

Once permission is granted, the widget shows notifications while the page is hidden or the chat is closed. Notifications cover new live agent messages and live agent joins or departures. Ending chat yourself does not produce a leave notification. Clicking the notification focuses the window, opens the chat, and publishes the [`ada:web_notification:click`](#subscribeevent) event.

**`JavaScript`**

```javascript JavaScript
enableNotificationsButton.onclick = async () => {
  const permission = await window.adaEmbed.requestNotifications();
  console.log("Notification permission:", permission);
};
```

### reset

`reset(resetParams?: ResetParams): Promise<void>;`

Resets the session. By default this clears the history and creates a new end user; pass `resetChatHistory: false` to update configuration while keeping the history and the current end user. See [ResetParams](#resetparams) for all options.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.reset({
  language: "fr",
});
```

Behaviors to know:

* Caller-supplied `metaFields` apply on a full reset by default. Set [`allowMetaFieldsInReset`](#allowmetafieldsinreset) to `false` to block them and restore the `start()` baseline instead. History-preserving resets always apply them.
* Caller-supplied `sensitiveMetaFields` apply on a full reset by default, the same as `metaFields`. Set `allowMetaFieldsInReset` to `false` to drop them instead. A full reset does not restore dropped `sensitiveMetaFields` from a `start()` baseline. The device binding registered through [`setDeviceToken`](#setdevicetoken) survives either way.
* Passing `identityToken` starts a new identified session and always forces a full reset. See [identityToken](#identitytoken).
* The promise resolves on the SDK's confirmation. Full resets wait for the persisted conversation clear to be durable.

### selectQuickReply

`selectQuickReply(messageId: string, index: number): Promise<void>;`

Selects a quick reply through the same send path as a tap in the built-in UI.
Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol).

| Parameter   | Type     | Description                                                                      |
| ----------- | -------- | -------------------------------------------------------------------------------- |
| `messageId` | `string` | The non-empty message `id` from `getMessages()` or `ada:message:received`.       |
| `index`     | `number` | A non-negative integer index into `message.data.quickReplies`, starting at zero. |

The runtime reads the reply fields from its own transcript. The host supplies only the message ID and index.
For `suggestion` and `switch` replies, the runtime sends a trigger with the stored target.
For `accepted_value` replies, the runtime sends the stored label as text. If the label is empty, the runtime sends the stored value.

The method starts engagement and activates the chat, like `sendMessage`.
If the SDK is not initialized, it starts headless mode or opens the chat.
In visible mode, it opens a closed chat and reloads a chat that shows the timeout fallback.
The runtime waits for a pending history-preserving reset, then activates the chat before it validates the stored row.
Selection uses the normal activation greeting rules.
A restored conversation with messages or an already sent greeting prevents a duplicate greeting.
A rejected selection on a fresh session still allows the normal greeting.

The promise resolves when the runtime accepts the send, at the same point as `sendMessage`.
The selected user message carries `origin: "programmatic"` in `getMessages()` and `ada:message:sent`.
Built-in UI taps and custom-app bridge selections omit this field.
Selection does not use the `beforeSend` text hook.

The promise rejects when programmatic control is off, with `ProgrammaticControlNotEnabled`.
It also rejects empty or non-string message IDs and indices that are not non-negative integers.
The runtime rejection messages include these codes:

| Code                                     | Cause                                                                                                                                   |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `ALREADY_ANSWERED`                       | The message already has an answer.                                                                                                      |
| `CHAT_OUTAGE`                            | The chat has a mid-conversation outage.                                                                                                 |
| `HISTORICAL_MESSAGE`                     | The message belongs to an earlier conversation.                                                                                         |
| `INDEX_OUT_OF_RANGE`                     | The index is outside the stored reply list.                                                                                             |
| `INVALID_PAYLOAD`                        | The frame payload has an invalid message ID or index.                                                                                   |
| `INVALID_QUICK_REPLY`                    | A suggestion or switch has no target or label, or an accepted-value chip has neither a label nor value.                                 |
| `MESSAGE_NOT_FOUND_OR_NOT_QUICK_REPLIES` | The message is missing or is not an AI Agent quick-reply message.                                                                       |
| `SEND_DROPPED`                           | The session is not ready, the rate limit blocks the send, or an image group send is in progress.                                        |
| `SEND_FAILED`                            | The server confirms rejection with HTTP 4xx while the conversation and session remain current.                                          |
| `SEND_UNCONFIRMED`                       | HTTP 5xx, a transport error, or another unconfirmed outcome leaves the selection answered. The selection may already have taken effect. |
| `UPLOAD_IN_PROGRESS`                     | A file upload is in progress.                                                                                                           |

After `SEND_FAILED`, the host can call `selectQuickReply` again for the same message once the rate limit permits it.
The runtime withdraws that answer and releases the block in the app display state and host selection checks.
The message must still belong to the current conversation and pass the other selection checks.
After `SEND_UNCONFIRMED`, the block stays answered because the selection may already have taken effect.
Another selection of that message rejects with `ALREADY_ANSWERED`.

If the conversation or session changes during the request, the promise resolves as accepted, as `sendMessage` does.
The runtime does not apply the send result to the replacement state or publish `ada:message:sent` for that request.
Cancellation before the send, activation errors, frame closure, and timeouts also reject the promise.

This example adds a button for each received reply. Each button selects its reply by index.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.subscribeEvent("ada:message:received", ({ message }) => {
  if (message.type !== "quick_replies") return;
  const container = document.getElementById("quick-replies");
  if (!container) return;
  container.dataset.messageId = message.id;
  container.replaceChildren();
  message.data.quickReplies.forEach((reply, index) => {
    const button = document.createElement("button");
    button.textContent = reply.label;
    button.onclick = async () => {
      try {
        await window.adaEmbed.selectQuickReply(message.id, index);
        if (container.dataset.messageId === message.id) {
          container.replaceChildren();
        }
      } catch (error) {
        console.error(error.message);
      }
    };
    container.append(button);
  });
});
```

### sendMessage

`sendMessage(text: string): Promise<SendMessageResult>;`

Sends a message to the AI Agent on behalf of the end user. Resolves with the new message's id, which matches the `id` on the same message in [getMessages](#getmessages) and in the [ada:message:sent](#subscribeevent) event payload.

Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Otherwise rejects with `ProgrammaticControlNotEnabled`.

Sending opens the chat drawer if it is not already visible. In [`headless`](#headless) mode, sending activates the session in the background instead. If the conversation has not started, the auto-greeting is skipped and the message starts the conversation.

Possible rejections:

* `sendMessage requires non-empty text`: `text` is empty, whitespace-only, or not a string.
* `DelegateRejected`: a registered [`beforeSend`](#setdelegate) hook returned `false`.
* `DelegateTimeout`: the `beforeSend` hook did not resolve within 5 seconds.
* `sendMessage rejected by chat: <CODE>`: chat-side validation rejected the message. Codes include `TOO_LONG` (exceeds the 500-character message cap), `RATE_LIMITED` (a previous send was less than 200 ms ago), `EMPTY_TEXT`, and `DISPATCH_FAILED`. During a Zendesk Chat handoff, a message that exceeds the limit rejects with `MESSAGE_TOO_LONG`.
* `sendMessage cancelled: the session changed before it could be sent`: a `reset()` or identity change superseded the send.
* A timeout error when the chat frame does not respond in time.

To attach metadata to the conversation, use [setMetaFields](#setmetafields) before calling `sendMessage`.

**`JavaScript`**

```javascript JavaScript
const { id } = await window.adaEmbed.sendMessage("What is your refund policy?");
console.log("Sent message id:", id);
```

### setComposerText

`setComposerText(text: string): Promise<void>;`

Pre-fills the chat composer with `text` without sending it. The end user can still edit or clear the text before sending.

Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Otherwise rejects with `ProgrammaticControlNotEnabled`. Not available in [headless](#headless) mode (rejects with `HeadlessModeError`). Rejects with `setComposerText rejected by chat: TOO_LONG` if `text` exceeds the composer's 500-character cap.

**`JavaScript`**

```javascript JavaScript
window.adaEmbed.setComposerText("Hello, I'd like to ask about ");
```

### setDelegate

`setDelegate(delegate: Delegate | null | undefined): void;`

Registers a `beforeSend` hook that runs on outgoing messages sent via [`sendMessage`](#sendmessage). Use it to transform, cancel, or pass the message through.

Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol). Called after `start()`, it throws synchronously with `ProgrammaticControlNotEnabled` when the gate is off. Before `start()`, the SDK queues the delegate. If programmatic control is disabled at startup, the SDK ignores the delegate. It logs a console warning when SDK diagnostics are turned on. See [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget).

> **Warning**
>
> The hook fires **only** for programmatic `sendMessage()` calls. Messages typed into the chat composer are sent through a separate path and do **not** flow through `beforeSend`. Do not rely on this hook as a moderation or PII-redaction gate for composer input.

The hook receives `{ text: string }` (not a full [PublicMessage](#publicmessage)) and returns one of:

* A modified `{ text: string }` to change what is sent.
* `false` (or a Promise resolving to `false`) to cancel. `sendMessage` rejects with `DelegateRejected`.
* The input unchanged to pass through.

The hook can be synchronous or asynchronous. It is bounded by a **5-second timeout**; a slow or never-resolving delegate causes `sendMessage` to reject with `DelegateTimeout`. The transformed text is **re-validated**: returning `{ text: "" }` or `{ text: "   " }` causes `sendMessage` to reject, and the message is not sent.

Pass `null` or `undefined` to clear the delegate.

**`JavaScript`**

```javascript JavaScript
window.adaEmbed.setDelegate({
  beforeSend: ({ text }) => {
    if (text.toLowerCase().includes("secret")) return false;
    return { text: text.trim() };
  },
});
```

See [Delegate](#delegate) for the type shape.

### setDeviceToken

`setDeviceToken(token: string): Promise<void>;`

Stores the APNs or FCM device token with the end user's sensitive metadata. To send push notifications, use webhooks and your push provider. See [Push notifications](/docs/channels/messaging/push-notifications).

The SDK ignores an empty device token or one that contains only whitespace. The mobile SDK wrappers use this action.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.setDeviceToken("fcm-device-token-here");
```

### setLanguage

`setLanguage(language: string): Promise<void>;`

Changes the conversation language programmatically. Use this action, rather than the [`language`](#language) setting, to change the language without clearing the chat history. Language codes use [ISO 639-1 format](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), for example `en`, `fr`, `ar`.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.setLanguage("fr");
```

You must first turn languages on in your Ada dashboard, under **Customization** > **Languages**. See [Support multiple languages in the same AI Agent](/docs/setup/languages/about-multilingual-support).

> **Note**
>
> On the legacy Chat SDK, `setLanguage` threw when the chat frame was not ready yet. On the Messaging SDK, the call is queued and applied when the frame is ready; it does not throw. A host that relied on catching the "not ready" error should stop doing so.

### setMetaFields

`setMetaFields(fields: Record<string, MetaFieldValue>): Promise<void>;`

Updates [`metaFields`](#metafields) after setup. In most situations, the `metaFields` setting is enough for user attribution. However, in cases where the chat remains open while page changes occur (like in single-page applications), this method may be useful.

The same value rules and limits as the [`metaFields`](#metafields) setting apply.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.setMetaFields({
  phone_number: "(123) 456-7890",
  name: "Ada Lovelace",
});
```

> **Note**
>
> Meta field keys should not include whitespace, emojis, special characters, or periods.

### setSensitiveMetaFields

`setSensitiveMetaFields(fields: Record<string, MetaFieldValue>): Promise<void>;`

Updates [`sensitiveMetaFields`](#sensitivemetafields) after setup. The values are not stored in the database and are deleted after 24 hours.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.setSensitiveMetaFields({
  jwt_token: "xxxxx.yyyyy.zzzzz",
});
```

> **Note**
>
> Meta field keys should not include whitespace, emojis, special characters, or periods.

### setTestModeBanner

`setTestModeBanner(label: string | null): Promise<void>;`

Shows a test-mode banner with the given label, or removes it with `null`. This action exists for the Ada dashboard's test surface; host pages do not normally call it.

### setTheme

`setTheme(theme: "light" | "dark" | "auto"): Promise<void>;`

Changes the chat widget's color scheme at runtime. Takes effect immediately, without a reset or reload. `"auto"` follows the user's OS preference.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.setTheme("dark");
```

### start

`start(settings?: AdaMessagingStartOptions): Promise<void>;`

Initializes the SDK on your page with the merged settings: `window.adaSettings` first, then any settings passed to `createAdaEmbedInterface` or `loadAdaMessaging`, then this call's `settings` on top. `handle` is required unless it was already provided.

`AdaMessagingStartOptions` accepts everything listed in the [Settings](#settings) section.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.start({
  handle: "<your-handle>",
  language: "fr",
});
```

If the assigned AI Agent's configuration explicitly turns web chat off, `start()` logs a warning, mounts no widget, and resolves. This applies to the standard widget and [`parentElement`](#parentelement) mode. [`Headless`](#headless) mode skips this check.

After this `start()` resolves, calls behave as they do before the widget starts. For example, `getInfo()`, `toggle()`, and metadata setters reject with the not-instantiated error. Calls already waiting on this start reject with `start_interrupted`.

Turn on web chat in the Agent's settings. Then call `start()` again. You do not need to call `stop()` first.

For both primary and alternative Agents, failed configuration reads, timeouts, or missing chat availability data keep the existing startup behavior. The SDK starts the assigned Agent without checking chat availability.

Rejections:

* `AdaEmbedError` with code `start_already_active` when the SDK has already been started. Call [`stop`](#stop) first to restart.
* `AdaEmbedError` with code `start_interrupted` when `stop()` or `destroy()` interrupts the start.
* `AdaEmbedError` with code `start_owner_conflict` when a different `adaEmbed` interface already runs on the page. Only one interface can own a page. Stop the other interface first. `loadAdaMessaging` reuses its saved interface, including after a start with web chat off. Repeated loader calls do not start it again. Call `start()` on the returned interface to retry. See [One interface per page](/messaging/web/getting-started#one-interface-per-page).
* An error asking for a `handle` when none was provided anywhere.

If `start()` fails for another reason, queued calls reject with the same error that `start()` returns.

When a client starts in [`headless`](#headless) or [`parentElement`](#parentelement) mode, `start()` also activates the conversation session. These modes have no launcher gesture.

### stop

`stop(): Promise<void>;`

Removes the SDK from your page and tears down its frames. A later [`start`](#start) can run again. Event subscriptions made through [`subscribeEvent`](#subscribeevent) and [`subscribeAll`](#subscribeall) survive a stop and replay onto the next started client; a delegate set with [`setDelegate`](#setdelegate) does not.

If `start()` resolved with web chat off, no client exists to stop. In that state, `stop()` rejects with the not-instantiated error.

### subscribeAll

`subscribeAll(callback: (eventKey: string, data: unknown) => void): Promise<number>;`

Subscribes to every broadcast event with a single callback. The callback receives the event key and the event data. Resolves a subscription id for [`unsubscribeEvent`](#unsubscribeevent).

This action is designed for bridge adapters, such as native wrappers, that forward SDK events wholesale.

> **Note**
>
> The transcript-bearing events `ada:message:sent` and `ada:message:received` are never delivered to `subscribeAll` subscribers. They require exact [`subscribeEvent`](#subscribeevent) subscriptions, so transcript data is not exposed to generic host-page listeners.

**`JavaScript`**

```javascript JavaScript
const id = await window.adaEmbed.subscribeAll((eventKey, data) => {
  console.log("Ada event:", eventKey, data);
});
```

### subscribeEvent

`subscribeEvent(eventKey: string, callback: (data: unknown, context: { eventKey: string }) => void): Promise<number>;`

Certain things in Ada trigger events. Each event consists of an event key and a data payload. Using `subscribeEvent`, you can define callbacks that are called when a specific event is triggered.

**`JavaScript`**

```javascript JavaScript
window.adaEmbed.subscribeEvent("ada:end_conversation", (data) => {
  console.log("The conversation has ended.");
});
```

Callbacks are triggered whenever an event starts with the `eventKey` provided to `subscribeEvent`. This means you can subscribe to all events of a certain category. For example, the callback in a subscription to `ada:campaigns` is called on `ada:campaigns:shown`, `ada:campaigns:engaged`, and any other event beginning with `ada:campaigns`.

Transcript-bearing events are the exception: `ada:message:sent` and `ada:message:received` require exact subscriptions to those event keys. They are not delivered through prefix subscriptions such as `ada:message` or broad listeners such as [`subscribeAll`](#subscribeall), so transcript data is not exposed to generic host-page subscribers.

Two arguments are provided to each callback when it is called: `data` and `context`.

* `data` is specific to each event.
* `context` is an object with a single property, the `eventKey` of the event that triggered the callback.

`subscribeEvent` resolves a numeric `subscriptionId` that you can use to unsubscribe later (see [unsubscribeEvent](#unsubscribeevent)). Subscriptions made before `start()` are queued and replay onto each started client.

It is strongly recommended that you place initial subscriptions in the [`onAdaEmbedLoaded`](#onadaembedloaded) setting. This ensures that subscriptions are in place before any events are triggered, so that no events are missed.

The following are the events that you can currently subscribe to:

| Event key                    | Data                                                                                                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ada.activation.error`       | `{ "error": string, "attempt": number }`, plus `{ "code": string }` when a fail-closed custom app mount caused the failure                                | Triggered when a chat activation attempt fails. `error` is a human-readable message. `attempt` counts consecutive failures within its own class. The SDK counts a payload that carries `code` on the separate fail-closed custom-mount budget. The default-app class stops emitting after three consecutive failures. The fail-closed class has no limit of its own, until three consecutive default-app failures latch the activation and stop all attempts. With [`appUrlFallback`](#appurlfallback) set to `false`, a failed custom [`appUrl`](#appurl) mount surfaces here, and `code` carries the same stable values as `ada.customApp.fallback`. A fail-closed custom mount failure during a session resume also triggers this event. Branch on `code`, because both timeout causes share one `error` message.                                                                                                                                                                                                                                                                                                                                                                    |
| `ada.customApp.fallback`     | `{ "appUrl": string, "reason": string, "code": string, "retryable": boolean }`                                                                            | Triggered when the SDK mounts the default app in place of a custom [`appUrl`](#appurl) frame. `code` names the cause with a stable value: `origin_not_allowlisted`, `allowlist_unavailable`, `frame_load_failed`, or `handshake_timeout`. `retryable` is `true` only for `allowlist_unavailable`. `reason` is a human-readable sentence, which the SDK also logs as a console warning. See [Watch for fallbacks](/messaging/custom-apps/getting-started#step-6-watch-for-fallbacks).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `ada:agent:joined`           | `{ "conversation_id": string }`                                                                                                                           | Triggered whenever a live agent joins the conversation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `ada:agent:left`             | `{ "conversation_id": string }`                                                                                                                           | Triggered whenever a live agent leaves the conversation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `ada:analytics`              | `{ "event": string, "data": object }`                                                                                                                     | Triggered for conversation analytics events, including events recorded through [`trackEvent`](#trackevent).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `ada:campaigns:dismissed`    | `{ "campaignKey": string }`                                                                                                                               | Triggered when the end user dismisses a proactive message bubble.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `ada:campaigns:engaged`      | `{ "campaignKey": string }`                                                                                                                               | Triggered when the end user engages the conversation (that is, sends a message) after being shown a proactive message in the same session.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `ada:campaigns:opened`       | `{ "campaignKey": string }`                                                                                                                               | Triggered when chat is opened after a proactive message has been shown.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `ada:campaigns:shown`        | `{ "campaignKey": string }`                                                                                                                               | Triggered when a proactive message bubble is displayed to the end user.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `ada:chat_frame_timeout`     | `{ "message": string }` plus `{ "frame": string }` for a frame-load timeout or `{ "phase": "activation", "timeoutMs": number }` for an activation timeout | Triggered when a chat frame fails to load, or when chat activation exceeds its timeout budget. Use it to build a fallback when chat fails to load. See also [`showFallbackOnTimeout`](#showfallbackontimeout). On the legacy Chat SDK the payload was `null`; on the Messaging SDK it carries diagnostic fields.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `ada:chatter_token`          | The end user token as a string                                                                                                                            | Triggered when the end user token becomes available. Also drives [`chatterTokenCallback`](#chattertokencallback).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `ada:close_chat`             | `{ "conversation_id": string, "is_engaged": boolean }`                                                                                                    | Triggered when the end user explicitly ends or closes the chat with the drawer's close (X) control, or confirms ending the conversation. It is not a signal that the chat UI is gone: after an End Chat confirm the UI stays on screen for any survey, and the event fires even when the chat could not end. `is_engaged` reports whether the end user sent at least one message.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `ada:connection:change`      | `{ "state": "connected" \| "reconnecting" \| "disconnected" }`                                                                                            | Triggered when the chat connection state changes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `ada:conversation:change`    | `{ "id": string }`                                                                                                                                        | Triggered when the end user moves to a new conversation. **Does not fire for the end user's first conversation**; it only fires on transitions between two already-known conversations. To detect every new conversation including the first, listen for both `ada:ready` and the first `ada:message:sent` (where `role === "user"`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `ada:conversation:message`   | `{ "author": string, "conversation_id": string, "message_id": string }`                                                                                   | Triggered whenever the end user receives a new message. `author` reports whether the AI Agent or a live agent sent it, using the inbound `role` values from [PublicMessage](#publicmessage). Carries metadata only, never the message body. For full message payloads, use the gated `ada:message:received` event.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `ada:csat_submitted`         | `{ "conversation_id": string, "csat_score"?: number }`                                                                                                    | Core and integration surveys publish this event after every successful non-partial POST with `submit: true`, including autosaves and later submissions. Drafts, partial POSTs, and writes without `submit: true` emit no event. Responses received after router destruction emit no event. `csat_score` uses the five-point scale: NUMERIC and EMOJI keep their 1–5 value; NUMERIC_TEN divides the 1–10 value by two without rounding; THUMBS maps negative to 1 and positive to 5. The payload omits the `csat_score` key when the write has no score.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `ada:embed_loaded`           | `undefined`                                                                                                                                               | Triggered once, when the embed interface is ready to be used. A subscriber that registers after the event already fired receives an immediate replay. Also drives [`onAdaEmbedLoaded`](#onadaembedloaded).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `ada:end_conversation`       | `{ "chatter_id"?: string, "session_id"?: string, "event_data": object }`                                                                                  | Triggered when the end user ends the conversation. The payload projects `chatter_id` and `session_id` at the top level. `chatter_id` is the Ada end user record ID, matching the legacy payload. It differs from the `end_user_id` you supply when minting an [`identityToken`](#identitytoken). **Changed in this release:** earlier messaging builds put the chatter *token* in this field, so update any handler that treated `chatter_id` as the token. `session_id` is newly added at the top level. The two fields differ in availability. `chatter_id` appears whenever the id is known (from the enrichment lookup or the local session), so a routine 404 does not drop it. `session_id` has no local fallback, so it appears only when the enrichment lookup succeeds. If the conversation resets during this lookup, the payload omits `session_id` and `event_data.chatter_transcript`. It keeps `chatter_id` when known, plus `event_data.event_name`. Captured custom fields stay under `event_data.user_data`, not at the top level. `event_data` contains conversation information such as `chatter_transcript` (when available) plus `event_name: "END_CONVERSATION"`. |
| `ada:identity_token:error`   | `{ "reason": string }`                                                                                                                                    | Triggered when an [`identityToken`](#identitytoken) exchange fails. Reasons include `chatter_not_found`, `identity_token_already_used`, `identity_token_exchange_failed`, `identity_token_expired`, `identity_token_state_unavailable`, `invalid_identity_token`, `missing_identity_token`, and `not_configured`. See the [reason descriptions](#identitytoken). The session continues anonymously.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `ada:identity_token:expired` | `{}`                                                                                                                                                      | Triggered, in addition to `ada:identity_token:error`, when the supplied identity token had already expired.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `ada:message:received`       | `{ "message": PublicMessage }`                                                                                                                            | Triggered for each inbound AI Agent or live agent message, delivered **at most once per message id** — a message re-presented by gap-recovery or catch-up does not re-fire. Presence and system notifications are excluded, so the payload's `role` is never `"user"`. **Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol)** and an exact subscription to `ada:message:received`. **Privacy:** the payload includes the full message body and reaches every host-page subscriber to this exact event key, including third-party scripts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `ada:message:sent`           | `{ "message": PublicMessage }`                                                                                                                            | Triggered after successful dispatch of an outgoing end user message: composer text, `sendMessage`, quick replies, and uploads. The payload's `role` is always `"user"`; programmatic sends carry `origin: "programmatic"`, and button-driven sends carry `data.buttonType` (see [PublicMessage](#publicmessage)). **Requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol)** and an exact subscription to `ada:message:sent`. **Privacy:** same caveat as `ada:message:received`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `ada:minimize_chat`          | `{ "conversation_id": string, "is_engaged": boolean }`                                                                                                    | The SDK emits this event only when the end user selects the minimize button. Host close or toggle calls, launcher clicks, mask clicks, Escape, fallback close, and automatic minimization after End Chat emit no close or minimize event. These hides still emit `ada:toggle(false)` and call [`toggleCallback`](#togglecallback) with `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `ada:ready`                  | `{ "mode": "headless" \| "visible" }`                                                                                                                     | Triggered once, when chat has finished initializing and is ready to accept programmatic calls. Fires before any conversation exists. Register this subscription inside `onAdaEmbedLoaded` so it is in place before the event fires.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `ada:start`                  | `{ "isRolledOut": boolean }`                                                                                                                              | Triggered when the SDK finishes setting up. `isRolledOut` is `false` when the session falls outside the configured rollout. Also drives [`adaReadyCallback`](#adareadycallback).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `ada:toggle`                 | `true` when the drawer opened, `false` when it closed                                                                                                     | Triggered when the chat drawer opens or closes. Also drives [`toggleCallback`](#togglecallback).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `ada:typing:start`           | `{ "agentId": string }`                                                                                                                                   | Triggered during a live agent handoff when the agent starts typing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `ada:typing:stop`            | `{ "agentId": string }`                                                                                                                                   | Triggered during a live agent handoff when the agent stops typing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `ada:web_notification:click` | `{ "conversation_id"?: string, "message_id"?: string }`                                                                                                   | Triggered when the end user clicks a browser web notification created after [`requestNotifications`](#requestnotifications) was granted. `message_id` is `undefined` for join and leave notifications. Ending chat yourself does not produce a leave notification.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |

### toggle

`toggle(): Promise<void>;`

Opens the chat drawer if it is closed, and closes it if it is open. A no-op in [`headless`](#headless) mode. Rejects when the [`parentElement`](#parentelement) setting is in use.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.toggle();
```

### trackEvent

`trackEvent(eventKey: string, value?: number, meta?: Record<string, unknown>): Promise<void>;`

Tracks a business Event for conversation analytics. The arguments of this function are:

* `eventKey: string`: the key of the Event to track (required).
* `value?: number`: an optional value to assign to the Event. Defaults to `0`.
* `meta?: Record<string, unknown>`: an optional object containing metadata corresponding to the Event, such as currency, product group, or customer segment. The SDK always stamps the current page URL onto the metadata.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.trackEvent("Example_Event", 2, {
  productId: "a1b2c3",
  customerSegment: "premium",
});
```

Tracked events are also re-published to subscribers as [`ada:analytics`](#subscribeevent) events.

> **Note**
>
> On the legacy Chat SDK, `value` was required. On the Messaging SDK, both `value` and `meta` are optional.

### triggerAnswer

`triggerAnswer(answerId: string): Promise<void>;`

Triggers a specific Answer in the conversation. Include the Answer's response id, which you can find in the URL of the corresponding Answer in the dashboard.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.triggerAnswer("627d28a9bd9ca9e5337b9763");
```

### triggerCampaign

`triggerCampaign(campaignKey: string, options?: Record<string, unknown>): Promise<void>;`

> **Note**
>
> This action is deprecated on the Messaging SDK. Calls resolve without effect. The SDK logs a console warning when SDK diagnostics are turned on. See [Debug a blocked widget](/messaging/web/getting-started#debug-a-blocked-widget). Use [`triggerProactive`](#triggerproactive) instead.

### triggerGreeting

`triggerGreeting(handle?: string): Promise<void>;`

Triggers the AI Agent's configured greeting in the active conversation. `handle` optionally overrides the configured handle for the greeting.

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.triggerGreeting();
```

### triggerPlaybook

`triggerPlaybook(playbookId: string, options?: TriggerPlaybookOptions): Promise<void>;`

Starts a specific Playbook from your host page. Opens the chat drawer if it is closed.
Headless clients activate the session without a drawer.

> **Note**
>
> This method is in Early Access.
> It requires [`enableProgrammaticControl: true`](#enableprogrammaticcontrol) and an active v2 Playbook marked as startable from the SDK.
> Set `startable_from_sdk: true` through the dashboard API, or ask the Ada team to mark the Playbook.
> This Playbook setting defaults to `false`.

Pass the Playbook ID as a 24-character hexadecimal string.
The SDK sends `metaFields` and `sensitiveMetaFields` with the trigger.
For a valid Playbook ID within the rate limit, the API stores metadata before it checks Playbook eligibility.
It also applies a supported `language`.
It also binds the push device token when push notifications are enabled.
These updates persist even when a later check refuses the trigger. A rate-limited request stores nothing.
Both options use the same value rules as [`setMetaFields`](#setmetafields) and [`setSensitiveMetaFields`](#setsensitivemetafields).

**`JavaScript`**

```javascript JavaScript
await window.adaEmbed.triggerPlaybook("507f1f77bcf86cd799439011", {
  metaFields: { orderId: "order-123" },
  sensitiveMetaFields: { accountToken: "<account-token>" },
});
```

The Playbook must allow standalone use and pass its availability rules for the end user.
The API refuses the request during a live handoff or when another Playbook is running.
If another trigger for the same end user is in progress, the API returns HTTP 409 with `failure_reason: "trigger_in_progress"`.
If the AI Agent is still answering an earlier message in the conversation, the API returns HTTP 409 with `failure_reason: "turn_in_progress"`.

If `metaFields.language` requests an unsupported language, the API returns HTTP 409 with `failure_reason: "language_not_supported"`.
For this language refusal, the API keeps the current conversation language and skips push device binding and Playbook activation.
Other metadata and email fields remain stored under the same rules as the metadata setters.

If either metadata field has a non-null value that is not an object, the API returns HTTP 409 with `failure_reason: "invalid_metadata"`.
The API stores no metadata and does not activate the Playbook for this refusal.

Each end user can make three valid-ID attempts with valid metadata objects per minute per AI Agent.

The promise resolves when the API accepts the trigger. It rejects on refusal, invalid input, metadata write failure, or timeout.
The failure includes the reason, such as `triggerPlaybook rejected: playbook_running`.
HTTP 503 with `activation_indeterminate` means the API cannot confirm whether the Playbook started.
The rejection does not mean the Playbook did not start.
Timeouts and network errors can also leave the result unknown. Do not retry automatically.

### triggerProactive

`triggerProactive(messageKeyOrOptions: string | { messageKey: string; params?: Record<string, string> }, params?: Record<string, string>): Promise<void>;`

Triggers a proactive conversation using a specified key. This displays a predefined static or template message, where template messages can include dynamic parameters.

Parameters:

| Name         | Type                     | Required                   | Description                                                                                         |
| ------------ | ------------------------ | -------------------------- | --------------------------------------------------------------------------------------------------- |
| `messageKey` | `string`                 | Yes                        | The key identifying the proactive conversation. Must match a predefined static or template message. |
| `params`     | `Record<string, string>` | No (static) Yes (template) | Key-value pairs for replacing placeholders in template messages. Ignored for static messages.       |

The Messaging SDK accepts both the object form and a plain string key. The legacy Chat SDK accepted only the object form, so existing calls keep working.

The trigger is ignored while the chat drawer is open.

**Examples**

1. Triggering a static proactive conversation:

**`JavaScript`**

```javascript JavaScript
window.adaEmbed.triggerProactive({
  messageKey: "static_message_key"
});
```

2. Triggering a template proactive conversation, for example *"Hello \{\{name}}, how can I help you today?"*:

**`JavaScript`**

```javascript JavaScript
window.adaEmbed.triggerProactive({
  messageKey: "template_message_key",
  params: { name: "Ada" }
});

// -> "Hello Ada, how can I help you today?"
```

### unsubscribeEvent

`unsubscribeEvent(subscriptionId: number): Promise<void>;`

Removes a subscription created with [`subscribeEvent`](#subscribeevent) or [`subscribeAll`](#subscribeall). It takes a single parameter, `subscriptionId`, which is the id of the subscription to be removed.

**`JavaScript`**

```javascript JavaScript
const subscriptionId = await window.adaEmbed.subscribeEvent("ada:ready", (data) => {
  console.log("Chat is ready in mode:", data.mode);
});

await window.adaEmbed.unsubscribeEvent(subscriptionId);
```

## Type signatures

Messaging SDK actions commonly use the following type signatures. When you use the [`@ada-cx/messaging-sdk`](/messaging/web/getting-started#use-the-types) npm package, import them by these names.

### AdaMessagingBranding

**`JavaScript`**

```javascript JavaScript
{
  aiAgentDescription?: AdaMessagingLocalizedText;
  aiAgentName?: AdaMessagingLocalizedText;
  avatarUrl?: string;
  cornerStyle?: "round" | "rectangular";
  headerColor?: string;
  headerTextColor?: string;
  launcher?: AdaMessagingBrandingLauncher;
  textSize?: "small" | "default" | "large";
  tintColor?: string;
}
```

The object accepted by the [branding](#branding) setting.

### AdaMessagingBrandingLauncher

**`JavaScript`**

```javascript JavaScript
{
  backgroundColor?: string;
  iconUrl?: string;
  size?: number;
  text?: AdaMessagingLocalizedText;
}
```

The launcher overrides inside [AdaMessagingBranding](#adamessagingbranding). `backgroundColor` is a 3- or 6-digit hex color. `iconUrl` is an absolute `https:` URL, ignored when `text` is set. `size` is an integer from 32 to 120, in pixels. `text` is the launcher label, at most 40 characters per language.

### AdaMessagingLocalizedText

**`JavaScript`**

```javascript JavaScript
Record<string, string>
```

Text keyed by language code, for example `{ en: "Aurora Concierge", fr: "Concierge Aurora" }`. The map must include the AI Agent's default language, which the widget uses when the end user's language has no entry.

### AdaMessagingStartOptions

**`JavaScript`**

```javascript JavaScript
{
  handle?: string;
  // ...every setting from the Settings section
}
```

The object accepted by [start](#start) and `loadAdaMessaging`. It extends the settings object with `handle`, which is required unless it was already provided elsewhere.

### AdaReadyCallbackParams

```typescript
{ isRolledOut: boolean }
```

The payload for [adaReadyCallback](#adareadycallback).

### AdaSdkEventCallback

**`JavaScript`**

```javascript JavaScript
(data: unknown, context: { eventKey: string }) => void
```

The callback shape for [subscribeEvent](#subscribeevent) subscriptions and [eventCallbacks](#eventcallbacks) entries.

### AdaSdkEventKey

A string type for [subscribeEvent](#subscribeevent) keys. It suggests known event keys and also accepts custom strings.

### BeforeSend

```typescript
(message: { text: string }) =>
  { text: string } | false | Promise<{ text: string } | false>
```

The hook type for [Delegate](#delegate).

### ConversationInfo

**`JavaScript`**

```javascript JavaScript
{
  id: string | null;
  handoff: {
    inLiveChat: boolean;
    agent?: PublicAgentInfo;
  };
}
```

The shape returned by [getConversation](#getconversation). `id` is the id of the active conversation, or `null` if the end user has not sent or received any messages yet. `handoff.agent` is populated only during an active live agent handoff.

### Delegate

**`JavaScript`**

```javascript JavaScript
{
  beforeSend?: (message: { text: string }) =>
    { text: string } | false | Promise<{ text: string } | false>;
}
```

The object accepted by [setDelegate](#setdelegate). `beforeSend` is called for messages sent via [`sendMessage`](#sendmessage) only (not composer-typed messages). Return `{ text }` to change the message, `false` to cancel it (causing `sendMessage` to reject with `DelegateRejected`), or pass the input through unchanged. Bounded by a 5-second timeout.

### LoadMessagingSdkOptions

```typescript
{
  cdnBase?: string;
  pinBuildSha?: string;
}
```

The loader options for `loadAdaMessaging` and `loadMessagingSdk`. See [Pin the CDN build](/messaging/web/getting-started#pin-the-cdn-build).

### MessagingSdkLoadErrorCode

```typescript
"invalid_build_sha" | "invalid_cdn_base" | "sdk_import_failed" |
"sdk_module_invalid" | "unstamped_build_sha"
```

The failure codes on `MessagingSdkLoadError`.

### MetaFieldValue

**`JavaScript`**

```javascript JavaScript
string | number | boolean | null
```

The value type accepted by [metaFields](#metafields), [sensitiveMetaFields](#sensitivemetafields), [setMetaFields](#setmetafields), and [setSensitiveMetaFields](#setsensitivemetafields).

### PublicAgentInfo

**`JavaScript`**

```javascript JavaScript
{
  id: string;
  name: string;
  avatarUrl?: string;
}
```

Basic live agent identity, carried on [ConversationInfo](#conversationinfo) and on agent messages in [PublicMessage](#publicmessage).

### PublicMessage

**`JavaScript`**

```javascript JavaScript
{
  id: string;
  role: "user" | "bot" | "agent";
  type: string;              // "text", "picture", "quick_replies", etc.
  body?: string;             // omitted for non-text messages
  createdAt: number;         // Unix epoch seconds
  agent?: PublicAgentInfo;   // present on agent messages during a live agent handoff
  data?: Record<string, unknown>;  // type-specific extras. Button-driven sends carry { buttonType }: a quick-reply chip names its kind ("suggestion" or "switch"), and the capture-skip and list-selection cancel actions send "suggestion". Accepted-value chips, option-block row clicks, and typed composer sends carry none. buttonType is client-stamped on a live send and is not persisted server-side, so it is absent on a message restored from history (for example after a page reload).
  origin?: "programmatic";   // present on messages sent through sendMessage()
}
```

The message shape returned by [getMessages](#getmessages) and carried in the `ada:message:sent` and `ada:message:received` event payloads.

### ResetParams

**`JavaScript`**

```javascript JavaScript
{
  greeting?: string | null;
  greetingResponseId?: string | null;
  language?: string;
  metaFields?: Record<string, MetaFieldValue>;
  sensitiveMetaFields?: Record<string, MetaFieldValue>;
  identityToken?: string;
  resetChatHistory?: boolean;
}
```

The options accepted by [reset](#reset). `greeting` and `greetingResponseId` both name an Answer response id to trigger after the reset; `greeting` matches the legacy Chat SDK field. `identityToken` starts a new identified session and forces a full reset. `resetChatHistory: false` keeps the conversation history and the current end user.

### SendMessageResult

**`JavaScript`**

```javascript JavaScript
{
  id: string;
}
```

The result resolved by [sendMessage](#sendmessage) once Messaging accepts the end user message.

### TriggerPlaybookOptions

Context to apply before a Playbook starts.

**`TypeScript`**

```typescript TypeScript
interface TriggerPlaybookOptions {
  metaFields?: Record<string, unknown>;
  sensitiveMetaFields?: Record<string, unknown>;
}
```

### WindowInfo

**`JavaScript`**

```javascript JavaScript
{
  hasActiveChatter: boolean;
  hasClosedChat: boolean;
  isChatOpen: boolean;
  isDrawerOpen: boolean;
}
```

The shape returned by [getInfo](#getinfo). `isChatOpen` reports whether the chat is visible to the end user. `isDrawerOpen` reports the floating drawer specifically and is always `false` in [`parentElement`](#parentelement) mode. `hasActiveChatter` reports whether an end user session exists. `hasClosedChat` reports whether the end user has previously closed a chat.

Before activation, `hasActiveChatter` reads persisted session presence from local storage in normal mode or session storage in session mode. It applies the configured "Forget after X time" retention window to the confirmed storage snapshot. Confirmed active handoffs and Zendesk Messaging continuity remain present after that window expires. This read does not change storage. It reports `false` in private mode, with a supplied `identityToken`, or while a persistence clear is pending. If storage cannot confirm the read, core retries once and reports `false` if the retry remains unconfirmed.

For this read, private mode includes the dashboard's **Forget After Reload** setting, even during a live agent handoff, as in legacy chat.

A stored end user session reports `true` whether or not the earlier visit was identified, subject to retention. Malformed session records and conflicting stored tokens report `false`.

During a background resume, persisted session presence remains available while session announcements wait for validation and the App handshake. The provisional conversation, handoff, and messages remain hidden until the resume completes. In `parentElement` mode, `hasActiveChatter` uses only the current session and does not read persistence.

Calls from `onAdaEmbedLoaded` or `adaReadyCallback` read persisted session presence without opening the drawer or creating a session. Readiness and `adaReadyCallback` do not wait for storage.

`isOpen()` resolves immediately from local visibility state without a core request.

With floating widget chrome, `getInfo()` queues for up to 35 seconds from the host call. In mobile webview, `parentElement`, and headless modes, it queues for up to five seconds. Requests still queued at the applicable deadline resolve with local state.

Before activation, floating and headless reads wait up to 15 seconds after delivery for core's storage read. In these modes, delivery sets the response deadline to the later of the current deadline and delivery plus 15 seconds. Total wait is at most 50 seconds for floating requests and 20 seconds for headless requests.

In `parentElement` and mobile webview modes, delivery starts a fresh five-second response timeout. These calls settle within 10 seconds, below the 30-second bridge timeout and the 35-second native timeouts. If core does not answer by the response deadline, the call resolves with local state.

The 10-second core frame load timeout leaves info requests pending. A later `core.ready` can deliver requests before the queue deadline. Terminal core errors, current public config failures, and the SDK timeout fallback resolve pending and later calls immediately with local fallback values. A failed activation attempt or the configured outage UI keeps info reads available through core.

A superseded startup failure sends no failure signal and leaves replacement requests pending. Successful activation lets later info reads query core again. `destroy()` and `stop()` resolve pending calls immediately with local defaults.