Getting started

This guide mints an identity token from your backend and wires it into each Messaging SDK. Read the identity tokens overview first for the flow and the security model.

Before you start

  • You need an Ada API key for your backend. The key must never reach a browser or mobile app.
  • You need the Ada end_user_id of the person to identify. Create end users, or look them up by your own external_id, with the End Users API.

Step 1: Mint a token from your backend

Call POST /v2/auth/tokens/ with the end user’s id. Authenticate with your API key as a Bearer token:

$curl -X POST https://EXAMPLE.ada.support/api/v2/auth/tokens/ \
> --header 'Authorization: Bearer {YOUR_API_TOKEN}' \
> --header 'Content-Type: application/json' \
> --data '{"end_user_id": "66b1a2c3d4e5f6a7b8c9d0e1"}'

A successful response returns 201 with the token:

1{
2 "token": "eyJhbGciOi...",
3 "type": "identity",
4 "expires_in": 900
5}

expires_in is in seconds. The token expires 15 minutes after minting and can be exchanged exactly once.

StatusMeaning
201Token minted.
400end_user_id is missing, empty, or not a valid id.
404No end user exists with that id.
503Token minting is not available for this AI Agent. Contact your Ada team.

Mint on demand, when your client is about to start or reset a chat session. Good mint points are sign-in and page load, always on your backend. Do not mint tokens in advance or cache them. Requests are rate limited per AI Agent; the limits are sized for one mint per end user opening chat. The Auth Tokens API reference documents the endpoint schema, error codes, and rate limits.

Step 2: Pass the token to the Web SDK

Deliver the token to your page over HTTPS, then pass it as identityToken:

1import { loadAdaMessaging } from "@ada-cx/messaging-sdk";
2
3const token = await fetchAdaIdentityToken(); // your backend call
4
5const ada = await loadAdaMessaging({
6 handle: "<your-handle>",
7 identityToken: token,
8});

The same setting works with adaEmbed.start() on the module-script path, and on window.adaSettings for pages that still configure through the global.

The SDK exchanges the token when the end user engages with the chat, before it restores any saved conversation state. An end user who never opens the chat never triggers the exchange.

Step 3: Handle exchange failures

Subscribe to the identity events on your host page:

1await ada.subscribeEvent("ada:identity_token:error", (data) => {
2 // The exchange failed. The session continues anonymously.
3});
4
5await ada.subscribeEvent("ada:identity_token:expired", (data) => {
6 // The server rejected the token and its expiry had elapsed.
7 // Mint a fresh token and pass it through reset() to retry.
8});

A failed exchange does not block the conversation. The session falls back to the normal anonymous path, so the end user can still chat.

Every exchange attempt consumes the token, even when the attempt fails afterward. To retry identification, mint a fresh token. Never reuse one.

Step 4: Re-identify or switch users

Pass a new token to reset() when the signed-in user changes, or to retry after an expired token:

1const token = await fetchAdaIdentityToken();
2await ada.reset({ identityToken: token });

Rules:

  • A full reset() without a replacement token discards any token that has not finished exchanging. The old identified session cannot be re-adopted.
  • reset({ resetChatHistory: false }) preserves the current session, and with it any pending token.
  • When the end user signs out of your product, call reset() so the next session starts anonymously.

Mobile SDKs

Each mobile SDK accepts the token at initialization and requires the Messaging runtime. The SDK injects the token into the WebView before any page script runs. The token never appears in a URL, and the runtime deletes it from the page after one read. A remount needs a freshly minted token.

iOS

Swift
1let adaWebHost = AdaWebHost(
2 handle: "<your-handle>",
3 eventCallbacks: [
4 "ada:identity_token:error": { event in /* continue anonymously */ },
5 "ada:identity_token:expired": { event in /* mint a fresh token, then reset */ },
6 ],
7 webSdk: .messaging,
8 identityToken: token
9)

Set identityToken at initialization. See the iOS SDK reference for details.

Android

Kotlin
1val settings = AdaMessagingView.Settings.Builder("<your-handle>")
2 .webSdk(AdaWebSdk.Messaging)
3 .identityToken(token)
4 .build()
5adaView.initialize(settings)
6
7adaView.addEventCallback("ada:identity_token:error") { event -> /* continue anonymously */ }

See the Android SDK reference for details.

React Native

TSX
1const token = await fetchAdaIdentityToken(); // your backend call
2
3<AdaMessagingView
4 handle="<your-handle>"
5 webSdk="messaging"
6 identityToken={token}
7 onEvent={(key) => {
8 if (key === "ada:identity_token:error") {
9 // continue anonymously, or remount with a fresh token
10 }
11 }}
12/>

Fetch the token before you mount the view. A token set after mount only applies on the next document load. See the React Native guide for details.

Identify one user across two devices

One identity gives one conversation on every device and browser. This walkthrough signs the same end user in on a laptop browser and on a phone app.

Device 1: the laptop browser

  1. The end user signs in to your site. Your backend verifies them with your normal logic.
  2. Your backend mints a token for their end_user_id, as in Step 1, and delivers it to the page.
  3. The page passes the token as identityToken:
1// Minted by your backend during page load
2const tokenA = await fetchAdaIdentityToken();
3
4const ada = await loadAdaMessaging({
5 handle: "<your-handle>",
6 identityToken: tokenA,
7});

When the end user opens the chat, the SDK exchanges the token and the identified conversation starts. Suppose they send “Where is my order?” and then close the laptop.

Device 2: the phone app

  1. The end user opens your mobile app and signs in. Your backend verifies them again.
  2. Your backend mints a second token for the same end_user_id. The first token is spent, so this device needs its own mint.
  3. The app passes the new token at initialization. React Native is shown here. iOS uses the identityToken initializer parameter, and Android uses Settings.Builder.identityToken.
TSX
1// A separate token, minted by your backend at app sign-in
2const tokenB = await fetchAdaIdentityToken();
3
4<AdaMessagingView
5 handle="<your-handle>"
6 webSdk="messaging"
7 identityToken={tokenB}
8/>

The exchange resolves to the same end user, and the phone restores the same conversation before the chat opens. “Where is my order?” and the AI Agent’s reply are already in the transcript. While both devices stay open, live updates flow to both over the same realtime channel. The phone’s exchange does not sign the laptop out; both hold live identified sessions.

Never send the laptop’s token to the phone. Tokens are single use, so the server rejects a replayed token and that session falls back to anonymous. Mint a fresh token on your backend for every device, at sign-in or page-load time.

This flow is verified end-to-end against a live environment on every change: two devices, two freshly minted tokens, one shared conversation.

Security checklist

  • Mint only from your backend, with your API key kept server-side.
  • Deliver tokens to clients over HTTPS only.
  • Never place a token in a URL, query parameter, or deep link.
  • Never log tokens on the server or the client.
  • Mint a fresh token per attempt. Tokens are single use and expire in 15 minutes.