Getting started
This guide embeds an AI Agent on a web page with the Messaging Web SDK. Pick one of three paths: a module script, the npm package, or an existing Chat SDK script tag that keeps working unchanged.
You need your AI Agent’s handle. If you are not sure of your handle, ask your Ada team.
For new installations, the module script and the npm package are the recommended paths. The window.adaEmbed and window.adaSettings globals remain fully supported for existing installs, but are deprecated for new installations. See Security best practices for the reasoning and for guidance on scoping your integration code.
Embed with a module script
Import the hosted SDK entry, create the adaEmbed interface, and start the AI Agent.
You should now see a chat button in the bottom right corner of the page. Click the button to open and close the chat drawer.
The adaEmbed reference stays private to the module. Do not assign it to window; see Keep the client in a private scope.
Notes:
handleis required. Most websites need onlyhandle.- Set
clusterordomainonly if Ada gives you a non-default regional configuration. - Pass
parentElementto render the conversation inline inside your own container. Omit it for the floating launcher and drawer. InparentElementmode,toggle()is not supported.
The SDK supports a rich set of settings, actions, and events. For example, you can set the AI Agent language, pass end-user context through metaFields, or start an identified session with identityToken. The SDK API reference covers all of them.
Install from npm
Use @ada-cx/messaging-sdk when your site is built with a bundler.
@ada-cx/messaging-sdk is live on npm. Versions before 1.0.0 are pre-GA setup releases; the GA rollout publishes 1.0.0.
The package carries TypeScript types, test mocks, and a thin loader. It never contains the SDK runtime. The loader imports the runtime from Ada’s CDN at page load, so every page receives the current rollout version. The npm version never pins the runtime. This is the same model as @stripe/stripe-js.
Quick start
Call loadAdaMessaging with your handle. It loads the runtime, creates the interface, starts the AI Agent, and returns the interface.
loadAdaMessaging never assigns the interface to window or any other global. Keep the returned reference in module scope, where only your own code can reach it. See Security best practices.
Use loadMessagingSdk when you want the runtime module without starting the AI Agent, for example to preload it before the page needs the widget:
loadMessagingSdk memoizes the in-flight import, so concurrent calls share one load. Both loaders reject a failed load with a MessagingSdkLoadError and permit a retry:
One interface per page
Only one messaging interface can run on a page, and loadAdaMessaging enforces this for you. Repeated calls return the same memoized promise and resolve to the same started interface. The first call’s settings win, and later calls never start a second widget. The call is therefore safe in code that runs more than once, such as React Strict Mode development effects. You do not need to cache the promise yourself. A failed load or start clears the memo, so a later call can retry.
Control the widget through the returned interface. stop() tears the widget down, and a later start(settings) on the same interface starts it again. A loadAdaMessaging() call after stop() returns the same stopped interface. It does not restart the widget.
If you create interfaces yourself with createAdaEmbedInterface, only one can start. A second interface’s start() rejects with the AdaEmbedError code start_owner_conflict.
Use the types
Import any public type directly. Type imports add zero runtime code.
AdaMessagingInterface is the interface loadAdaMessaging resolves. The AdaEmbedInterface name remains available as its legacy alias.
Runtime symbols such as AdaMessagingClient and createAdaEmbedInterface are exported as types only. Get their implementations from loadMessagingSdk(). createAdaEmbedInterface is the legacy window.adaEmbed compat facade; new integrations use loadAdaMessaging.
Test without the CDN
The ./testing entry provides an in-memory mock of the messaging interface, so your unit tests never touch the network.
The mock records every call in calls, and $emit(key, data) simulates SDK events for your subscribers. Pass overrides to replace any method with your own spy.
Bundler note
The loader imports the runtime with a fully dynamic URL. The import is pre-annotated with /* webpackIgnore: true */ and /* @vite-ignore */, so webpack and Vite leave it for the browser to resolve.
Do not copy the CDN import into your own code without those annotations. Without them, the bundler tries to resolve the https: URL at build time and fails, or rewrites the import so it never reaches Ada’s CDN. If your bundler still rewrites dynamic imports, configure it to ignore imports of https: URLs.
Migrate from the Chat SDK web script
If your site already loads Ada with the Chat SDK script tag, keep it. No code change is required.
The loader keeps the window.adaEmbed global, and Ada switches the runtime behind it per handle. window.adaSettings remains fully supported, and legacy callbacks such as adaReadyCallback, chatterTokenCallback, and conversationEndCallback keep working.
The globals stay supported for this path indefinitely. They are deprecated only for new installations, which should use the module script or the npm package. See Security best practices.
For new code, prefer event subscriptions over legacy callbacks:
The transcript-bearing events ada:message:sent and ada:message:received are exact-subscription only, and they require the enableProgrammaticControl setting. They are not delivered through subscribeAll().
Validate a specific runtime version
The runtime normally resolves from Ada’s rollout at page load. To validate a specific build on a staging page, append the ada-messaging-version query parameter to the page URL:
The value is the full git SHA of a main build. Ada gives you the value when you arrange a joint validation. Preview builds are Ada-internal, and your Ada team drives them. The pin bypasses the rollout while the parameter is present on the page URL. An invalid or unavailable value falls back to the stable rollout.
To point the npm loader at a different asset host, pass cdnBase. Ada gives you the host value when you arrange a preproduction validation:
Use cdnBase for staging validation only. Production pages must keep the default.
Pin the CDN build
Pinning is not recommended for production. A pinned runtime misses Ada’s fixes and rollouts. A pinned runtime can also predate later server contract changes and stop working.
The npm package exports CDN_BUILD_SHA. The value is the git commit SHA of the Ada commit your installed npm version was published from. Ada deploys each commit’s runtime under an immutable SHA root on the CDN, so the value names the CDN build associated with your npm version.
Pass the SHA as the pinBuildSha loader option. The loader then skips the rollout and imports that exact build:
Use a pin only to debug an issue, to validate a staged build, or to reproduce a report against a known runtime. The option accepts any full 40-character hex git SHA of a deployed Ada build. Only installed npm packages carry a real CDN_BUILD_SHA. In a local checkout the value is a 40-zero placeholder, isCdnBuildShaStamped() returns false, and the loader rejects the placeholder with the unstamped_build_sha error code. In the first minutes after an npm release, a pin can fail with sdk_import_failed until the matching CDN deploy completes. pinBuildSha composes with cdnBase for staged validation.
Content Security Policy
If your site enforces a CSP, allow the Ada asset host in three directives:
The asset host depends on how the SDK loads:
- Module script or npm loader:
https://messaging-assets.ada.support - Chat SDK script tag (
embed2.js):https://static.ada.support - Preproduction validation: the Ada-provided asset host, for the validation window only
During a migration, allow both production hosts.
You do not need to allow Ada’s API or WebSocket endpoints. Those requests run inside a cross-origin iframe with its own CSP, so your page’s policy does not govern them.
If your policy has no frame-src, framing falls back to child-src and then default-src. A restrictive default-src with neither directive blocks the widget without a frame-src violation to point at.
Roll out CSP changes with Content-Security-Policy-Report-Only first. It surfaces every violation without breaking the page.
Debug a blocked widget
The browser console names the violated directive and the blocked URL. The SDK emits no advisory warnings on the host console by default, so CSP violations stand out. To enable the SDK’s own advisory diagnostics, set window.adaSettings.debug = true or run localStorage.setItem("ada.debug", "1").