Loops AI Docs

Checklist & troubleshooting

What to verify before you ship a WebView integration, and the failure modes worth recognising on sight.

Before you ship

#CheckDone when
1URL carries all four container flagsembedded=true&mode=sdk&platform=<ios|android>&isMobile=true
2platform matches the real deviceAnalytics land on the right mobile channel
3Bridge object exists and is named exactly loopsAIBridgeYou receive ready
4Bridge is installed before page content loadsready arrives on the very first load, not only after a reload
5initContext is sent from your ready handlerEven {} — flows that wait for it can open
6The user can close the chatYour own control, or closeButtonEnabled: true — and closeChat dismisses your container
7openExternalUrl is routedProduct taps open the in-app browser (or a native screen you opted in)
8Attribution survivesCommercially important links open in a web context, not a native screen
9The WebView cannot navigate off chat.loopsai.comForeign navigations are cancelled and routed out
10Sender is verifiedMain frame + chat host (native) or event.origin (iframe)
11Storage is persistentReopening resumes the same conversation
12persistSession ids are stored and replayedThe conversation survives an app kill
13Camera / microphone are wired if you offer try-on or voiceBoth flows complete on a real device
14Android file chooser is implemented if you offer try-onPhoto-library upload opens a picker
15Rotation sends mobileStateChangeLayout re-flows on rotate
16Buttons are gated on the agent's flagsNo button that silently does nothing
17Availability is checked before showing your entry pointChat can be turned off from the dashboard
18Language matches your app?locale= sent when your app pins a language
19Nothing sensitive in the URL or in contextNo tokens, no personal records

Troubleshooting

There is no ✕ in the chat header

That is the mode=sdk default — the runtime assumes your container draws its own close control. Send initConfig with closeButtonEnabled: true to turn the web one on, or present your own and dismiss the container yourself.

The ✕ is there but nothing happens when I tap it

Your bridge is missing, misnamed, or installed too late. The runtime found no loopsAIBridge, fell back to window.parent — which in a top-level WebView is the page itself — and the message went nowhere.

  • Check the exact name and casing: loopsAIBridge.
  • On iOS it must be a WKUserContentController handler: window.webkit.messageHandlers.loopsAIBridge.
  • Everywhere else it must be window.loopsAIBridge with a postMessage(string) method.
  • In a WebView you can inspect, run typeof window.loopsAIBridge in the console — "undefined" is your answer.

I never receive ready

  • mode=sdk is missing from the URL. Without it the runtime treats a top-level page as standalone and never attaches its listener.
  • Or the bridge was injected after page scripts ran. Inject at document start (injectedJavaScriptBeforeContentLoaded, WKUserScript at .atDocumentStart, an early JavaScript channel registration).

The commands I send are ignored

  • Iframe transport: you did not pass parentOrigin (URL-encoded) on the iframe src. The runtime only trusts its own origin, the referrer, and parentOrigin.
  • You sent before ready. There is no queue on the web side — early messages are simply lost.
  • The envelope is malformed. It needs type: "nativeAction" and a name, with arguments inside payload.
  • Android only: you passed an object instead of a JSON string, or forgot JSON.stringify.

The size guide never opens

You have not sent initConfig or initContext. The body-type guide waits for the host to finish initializing. Send initContext (even {}) from your ready handler.

suggestSize does nothing

The shopper's height and weight are already on record and there is no productCode in context, so there is no product to size. Send updateContext with a productCode first.

Try-on or voice mode opens and then stalls

A permission is missing. Three layers all have to agree:

  1. The OS permission for your app (camera / microphone).
  2. The WebView's own request — requestMediaCapturePermissionFor on iOS, onPermissionRequest on Android.
  3. In an iframe: allow="camera; microphone" on the element.

If try-on stalls specifically on photo upload on Android, you are missing WebChromeClient.onShowFileChooser — the file input silently does nothing without it.

Product taps open inside the chat WebView

mode=sdk is missing, so the runtime is navigating for itself instead of emitting openExternalUrl. Add it, and add a navigation guard so a foreign URL can never load in the chat WebView.

The conversation restarts every time

  • You recreate the WebView with a non-persistent data store. Use WKWebsiteDataStore.default() and enable DOM storage on Android.
  • Or the WebView's storage was cleared between launches. Store the ids from persistSession natively and replay them as _lsuid / _lscid.

The chat renders but nothing is configured — default colours and copy

The agent's published configuration could not be fetched. Check the agentId in the path, and check that the device can reach chat.loopsai.com (a corporate proxy or certificate-pinning policy is the usual culprit).

The chat is in the wrong language

Without ?locale= the chat follows the device language, which is not always your app's language. Pass ?locale=en / ?locale=tr (regional tags like tr-TR are accepted) — it outranks the device and any stored preference, and it also sets the language the assistant answers in. Language is resolved on load, so a warm WebView has to be reloaded with the new value.

The chat does not render at all

Ask for the availability endpoint — GET /api/embed/<agentId>/style. If the channel is Inactive, activate it in the dashboard.

Analytics never arrive

trackEvent is only emitted when your agent's mobile channel is configured for native-bridge delivery. Ask your Loops AI contact to enable it — see Analytics.

Everything works, but reopening the chat is slow

You are destroying the WebView on close. Hide your container instead of tearing it down: the conversation, scroll position and connection stay alive and reopening is instant. Handle ready, persistSession and trackEvent at container level so they still work while the chat is off screen.

Still stuck?

Send your Loops AI contact the exact URL you load (minus session ids), the first five messages you receive from the bridge, and the platform + WebView version. That is almost always enough to spot the problem.

Where to go from here

  • Mobile SDKs — if you can adopt them later, they replace this whole page with a few lines.
  • Canonical events — the analytics taxonomy your forwarded events belong to.
  • Widget Embed — the same runtime on the web, if you also integrate a website.

On this page