Guides
Native identity bridge compatibility
Understand the frozen identity v2 contract, existing app compatibility and when native capabilities require an app release.
On this page
AI Assistant runs inside a mobile app through one small file: the app bridge. Kit v2 freezes that identity bridge. Hosted chat improvements arrive from our side; adding a native OS capability, such as microphone permission, needs your app's own release.
For a new iOS or Android app, install the official iOS SDK or official Android SDK. This page documents the underlying frozen identity contract and compatibility integrations. SDK distribution versions and identity protocol versions are separate.
Hosted updates do not replace app releases for new SDK code or OS permissions. Existing versioned identity files remain available for compatibility.
The full message contract is published beside the files, at https://busymate.ai/sdk/v2/2.0.0/CONTRACT.md.
1. Know what the bridge can do
helloreports the bridge version, the platform, the WebView version, your app build and the assistant it was installed for.statesays whether someone is signed in, with a scrambled account key. Your user id never leaves the device.mintasks your backend for a short-lived sign-in token. Your code gets what the chat sent (anonceto sign) plusassistantandorigin, which the bridge sets itself so a page can never choose them.openopens anhttpslink in the system browser. Any other link is refused.actionhands a named action, such asclose, to your app. Anything you do not handle is ignored.
The bridge also tells the chat when your app returns to the foreground, and when it reloaded the chat after the system stopped the web view. Only your own chat can reach it: exactly https://<your assistant>.busymate.ai and the exact https origins you list. There are no wildcards, so another company's chat, a shared artifact or any other busymate.ai page opened inside your app is refused, and your backend is never asked. List only hosts that serve the chat itself.
2. Install it on Android
Copy BusymateBridge.kt from the address above into your project and check its SHA-256 against SHA256SUMS in the same folder. Remove the kit v1 bridge (BusymateAIWebViewBridge, the BusymateAINative and SupportChatNative JavaScript interfaces) and any copied native-identity.js or wire.js. Then install the bridge before the first loadUrl, and forward renderer crashes so the chat recovers instead of the app closing:
// Copy https://busymate.ai/sdk/v2/2.0.0/android/BusymateBridge.kt (check SHA256SUMS).
// Needs androidx.webkit:webkit >= 1.8 and androidx.lifecycle:lifecycle-process.
BusymateBridge.install(webView, BusymateBridge.Config(
assistant = "your-assistant",
origins = emptyList(),
account = { session.userIdOrNull }, // null when signed out
mint = { request, done -> // YOUR backend signs; never a key in the app
backend.mintBusymate(request["nonce"] as String) { token, nonce ->
done(if (token != null) BusymateBridge.MintResult.Token(token, nonce) else BusymateBridge.MintResult.Failed)
}
},
))
// In your WebViewClient: the chat recovers instead of the app closing.
override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail) =
BusymateBridge.onRenderProcessGone(view, detail)
// Optional, only faster: after sign-in, sign-out or an account switch.
BusymateBridge.accountChanged()3. Install it on iOS
Copy BusymateBridge.swift (iOS 14 or later) and check its SHA-256. Remove the old BusymateAI and SupportChat message handlers. Install the bridge before the first load, and forward web content crashes from your navigation delegate:
// Copy https://busymate.ai/sdk/v2/2.0.0/ios/BusymateBridge.swift (iOS 14+, check SHA256SUMS).
BusymateBridge.install(on: webView, config: .init(
assistant: "your-assistant",
origins: [],
account: { Auth.shared.userId }, // nil when signed out
mint: { request in // YOUR backend signs; never a key in the app
guard let pair = try await Backend.mintBusymate(nonce: request.nonce) else { return nil }
return .init(token: pair.token, nonce: pair.nonce)
}))
// In your WKNavigationDelegate: the chat reloads and restores itself.
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
BusymateBridge.contentProcessDidTerminate(webView)
}
// Optional, only faster: after sign-in, sign-out or an account switch.
BusymateBridge.accountChanged()If your app sets WKAppBoundDomains, add the assistant's domains to that list, or the bridge cannot be reached.
4. Configure your website
Keep the embed script. Tell it who is signed in on your website and how to get a fresh token from your backend, and delete any page script that talked to the app directly. account() is your website's own session: inside your app's WebView it returns null, and the chat then asks the app itself, so one web bundle serves both:
<script id="busymate" src="https://your-assistant.busymate.ai/embed/v1.js" data-assistant="your-assistant" async></script>
<script>
// Once your own sign-in state is known. account() is your WEBSITE session (null = signed out;
// inside your app the app answers for itself). getIdentity({ nonce }) asks YOUR backend for a
// fresh { token, nonce } pair on every call (sign that nonce, or your own).
function configureBusymate() {
window.BusymateAI.configure({
account: () => currentUser?.id ?? null,
getIdentity: () => fetch("/api/busymate-token", { method: "POST" }).then((r) => r.json()),
});
}
if (window.BusymateAI?.configure) configureBusymate();
else document.getElementById("busymate").addEventListener("load", configureBusymate);
</script>React Native, Flutter and Electron apps use the matching file from the same folder: react-native/busymateBridge.js with core/busymateBridgeCore.js, flutter/busymate_bridge.dart, or electron/preload.js with electron/main.js. Each takes the same account and mint callbacks. For these, load the hosted chat page directly rather than a page that embeds it.
5. Keep your backend as it is
Your signer keeps producing ES256 tokens with nonce and jti that expire within 120 seconds. In the app it signs the nonce the bridge passes; on your website getIdentity receives { nonce } as well, and you may sign that one or your own. See Sign identity tokens from your backend.
The identity bridge itself requests no OS permission. Enabling the microphone adapter requires your microphone declaration and OS authorization. The chat can reach only your own sign-in token, an https link and close. Mention chat data shared with AI Assistant in your privacy label; the AI disclosure and the report control are shown inside the chat.
Voice: use the complete iOS/Android microphone integration guide: setup, tap-triggered OS permission, media callbacks, cleanup and device checks.
Verify
- Signed out, open the chat: a guest chat that answers.
- Sign in inside your app: the same conversation shows your customer's name, with no reload.
- Put the app in the background for a minute, then bring it back: the same conversation, and no "That chat had ended".
- Switch accounts: the first person's conversation disappears and the second person is recognized.
- Sign out: the chat goes back to a guest.
Every session records which bridge version it came through and whether the customer was recognized, so we can tell you how each build of your app is doing without access to your code.
Test your integration
The official SDK repositories include buildable example apps, configuration tests and build instructions. Build checks do not substitute for OS permission tests on physical devices. Run the verification steps above and each platform's microphone checklist with your actual backend, workspace and supported OS versions.
Questions
Do I have to update again later?
The identity file stays frozen. Optional native capabilities need your app's release; existing builds keep working.
What happens to app builds that still ship the old bridge?
Existing versioned bridge files remain compatibility paths. Test your supported builds against your workspace; guest access depends on your workspace settings.
Is my user id sent to AI Assistant?
No. The bridge sends a scrambled key made from your app id and the user id, only so the chat can tell that the person changed. The token your backend signs carries the id you choose to put in it.