The chat URL
Every query parameter the chat runtime accepts — required container flags, session continuity, and flow entry points you can open straight from the URL.
The runtime is addressed entirely by URL. Your agentId is a path segment;
everything else is a query parameter.
https://chat.loopsai.com/<agentId>?embedded=true&mode=sdk&platform=ios&isMobile=trueEncode your values
Build the URL with your platform's URL builder (URLComponents,
Uri.Builder, URL/URLSearchParams), not string concatenation. A raw &,
space, or non-ASCII character in a product code or search query silently
truncates the rest of the query string.
Required for a mobile container
| Parameter | Value | Why |
|---|---|---|
embedded | true | Loads the embedded chat surface (the panel), not the standalone marketing page. |
mode | sdk | Switches the runtime to host-driven mode: it attaches its message listener, emits ready, and routes product taps to you instead of navigating itself. |
platform | ios | android | Tags the session and its analytics with the real platform. Match the device. |
isMobile | true | Mobile layout (sizing, sheets, keyboard handling). Send it on every phone-sized container. |
`mode=sdk` is the switch that makes a WebView integration possible
Leave it out and the chat still renders — but the runtime treats a top-level
page as standalone: it never attaches its inbound message listener, never
emits ready, and opens product links inside its own WebView. You would have a
chat you cannot control, close, or route out of. Always send it.
Optional
| Parameter | Value | Effect |
|---|---|---|
_lsuid | Anonymous user id | Restores a known anonymous user — see Session continuity. |
_lscid | Conversation id | Resumes that conversation. Ignored when fresh=true. |
fresh | true | Starts a brand-new conversation and ignores any stored one. Use for a "new chat" entry point. |
parentOrigin | Your page origin, URL-encoded | Iframe transport only. Adds your page to the runtime's trusted-sender list. Without it your inbound messages are dropped. |
locale | en | tr | Pins the chat language, outranking the device language and any previously stored preference. Regional tags are accepted (tr-TR → tr). Omit it to follow the device. |
Do not copy web-embed parameters
widgetMode, loopsai_open, and rt belong to the website embed script and
its bubble / bottom-bar / sidebar layouts. They have no meaning in a mobile
container — the WebView is the panel.
Language
The chat resolves its language in this order:
?locale=— your explicit override. Use it when your app has its own language setting that may differ from the device.- The visitor's stored preference from an earlier session.
- The WebView's own language (
Accept-Language/navigator.language). - English.
English and Turkish are supported today; an unsupported value falls through to
the next step rather than failing. ?locale= applies to the whole session —
interface strings, agent copy, and the language the assistant answers in — so
pass it whenever your app knows better than the device.
It only applies on load
Language is resolved when the runtime boots. To switch language in a warm
WebView, reload it with the new ?locale= value.
Flow entry points on the URL
These open a flow at load time, with no bridge message. Ideal when a native
button should land the user straight in a flow, because there is nothing to wait
for — no ready, no timing.
| Parameter | Opens |
|---|---|
suggestSize=true | The size-suggestion form (height + weight) as soon as the chat is usable. |
sizeGuide=true | The body-type size guide inside the conversation. Combine with productCode and gender. |
productCode=<code> | The product the size guide should size. Must match your Loops AI catalog code. |
gender=women | men | Skips the gender question in the size guide. |
searchQuery=<text> | Starts a fresh conversation and runs that query as an AI search escalation. |
# "Find my size" button
https://chat.loopsai.com/AGENT?embedded=true&mode=sdk&platform=ios&isMobile=true&suggestSize=true
# "Size guide" button on a product screen
https://chat.loopsai.com/AGENT?embedded=true&mode=sdk&platform=android&isMobile=true
&sizeGuide=true&productCode=1010600-88244&gender=women
# Your own search bar escalating into the assistant
https://chat.loopsai.com/AGENT?embedded=true&mode=sdk&platform=ios&isMobile=true
&searchQuery=linen%20shirts%20under%2050URL for a cold open, bridge for a warm one
If you keep one WebView alive and reuse it (recommended — it reopens instantly), a URL parameter only applies on the load that carried it. Use the bridge actions in Flow modes to open a flow in a WebView that is already loaded.
The standalone size guide
The body-type size guide also has its own route. Open it in its own WebView/sheet when you want the guide without the conversation around it:
https://chat.loopsai.com/<agentId>/size?productCode=1010600-88244&gender=women&mode=sdk&platform=ios| Parameter | Notes |
|---|---|
productCode | Optional. Resolved from the catalog when omitted. |
gender | women or men. Skips the gender step. |
mode, platform | Same as the chat — required for the page to talk to your bridge. |
It speaks the same bridge and sends three actions: closeChat when the user
dismisses it, openExternalUrl for a product link, and startSizeGuideInline
when the user chooses to continue in chat. See
Links & navigation
for that hand-off.
Session continuity
The runtime keeps its anonymous user id and conversation id in the WebView's
localStorage. As long as your WebView uses a persistent data store and you
don't clear it, the conversation resumes on its own — nothing to implement.
Two ids matter:
| Id | Meaning |
|---|---|
anonUserId | The pseudonymous shopper. Stable across conversations. |
conversationId | The conversation to resume, per agent. |
For a fully robust setup, mirror them natively. The runtime hands them to you whenever they change:
// web → host
{ "name": "persistSession",
"payload": { "agentId": "AGENT", "anonUserId": "…", "conversationId": "…" } }Store both, then pass them back as _lsuid and _lscid on the next load. They
are only used to seed storage when it is empty, so replaying them is safe.
https://chat.loopsai.com/AGENT?embedded=true&mode=sdk&platform=ios&isMobile=true
&_lsuid=<anonUserId>&_lscid=<conversationId>Why the SDKs bother
iOS WebKit can clear WebView storage under storage pressure or on app
termination, which would drop the conversation. Mirroring persistSession
natively (Keychain, UserDefaults, SharedPreferences, secure storage) and
replaying the two ids is what makes a session survive that. Skip it and you get
a good-enough session that occasionally restarts.
To start a new conversation deliberately, drop _lscid and add fresh=true —
keep _lsuid so the new conversation still belongs to the same shopper.
Environments
| Environment | Base URL |
|---|---|
| Production | https://chat.loopsai.com |
| Staging / self-hosted | Provided by your Loops AI contact |
There is no API key or client secret in the URL. Access is resolved server-side
from the agentId, so treat the URL as public — and keep any personal data out
of it (use initContext over the bridge instead).
Availability check
Your Loops AI channel has an Active / Inactive status in the dashboard. Ask for it before you show your chat entry point, and you can turn the assistant on or off — for a launch, or for maintenance — without shipping an app update:
GET https://chat.loopsai.com/api/embed/<agentId>/style{
"embedEnabled": true, // web channel status
"iosEnabled": true, // present when an iOS channel exists
"androidEnabled": true // present when an Android channel exists
}Read the platform field first and fall back to embedEnabled. Fail open —
treat a network error or unparseable response as available, so a transient blip
never hides a working assistant.
val available = json.optBoolean("androidEnabled", json.optBoolean("embedEnabled", true))
chatButton.isVisible = availableThe response is the agent's published configuration, so the same call tells you
which native entry points are worth showing — virtualTryOnEnabled,
sizeSuggestionEnabled, voiceModeEnabled, and friends. Gate your buttons on
them: a flow that is disabled for the agent is ignored by the runtime, so a
button for it would do nothing.
Next: The message bridge.