Flow modes
Open size suggestion, the body-type size guide, virtual try-on, AI search, or a product question from your own native buttons — no SDK required.
Your product screen has its own buttons — Size guide, Find my size, Try on, Ask about this product. Each one opens the assistant directly in that flow. You have two ways to do it, and the right one depends on whether the chat is already loaded.
| Situation | Approach | Why |
|---|---|---|
| The chat is not open yet | Add a URL parameter and present the container | Nothing to wait for, no timing to get right |
| The chat is already loaded (warm WebView) | Send a bridge action | The URL only applies on the load that carried it |
All examples below use the send(name, payload) helper from the
Quickstart — one function that
wraps a payload in the envelope and posts it into the page.
Send `initContext` first — always
Send initContext from your ready handler even when you have no context to
pass ({} is fine). The runtime treats it as "the host has finished
initializing", and some flows — the body-type size guide in particular — wait for
it before they open.
Size suggestion
The short form: height + weight, then a size recommendation for the product in context.
// Cold open
`${CHAT_URL}&suggestSize=true`
// Warm open
send("initContext", { productContext: { productCode: "SKU123" } });
send("suggestSize");What the user sees depends on what the runtime already knows:
| State | Result |
|---|---|
| No height / weight on record | The measurement form opens right away. |
Measurements known and a productCode in context | The assistant answers with a size recommendation for that product. |
Measurements known, no productCode | Nothing opens — there is no product to size. |
So always set the product context before (or together with) suggestSize.
Body-type size guide
The longer guided flow: gender → body type → height & weight → recommendation.
// Cold open
`${CHAT_URL}&sizeGuide=true&productCode=1010600-88244&gender=women`
// Warm open
send("startSizeGuide", {
productContext: { productCode: "1010600-88244", gender: "women" },
});| Field | Notes |
|---|---|
productCode | Optional if the current context already has one. Must match your Loops AI catalog code. |
gender | women or men. Pass it to skip the gender step; omit it and the guide asks. |
Not the same flow as `suggestSize`
suggestSize is the quick height + weight form. startSizeGuide is the full
body-type guide. Pick one per button — they are different experiences with the
same goal.
As its own screen
The guide also has a standalone route you can present as its own sheet, with no conversation around it:
https://chat.loopsai.com/<agentId>/size?productCode=1010600-88244&gender=women&mode=sdk&platform=iosHandle closeChat to dismiss it, and startSizeGuideInline if the user asks to
continue in chat — see
Returning from the standalone size guide.
Virtual try-on
send("startVirtualTryOn", { product: { productCode: "SKU123" } });Requires try-on to be enabled for the agent, and the WebView permissions from the requirements table:
- Camera capture — grant the WebView's media request and hold the OS camera permission before the flow starts.
- Photo library — on Android this needs
WebChromeClient.onShowFileChooser. Without it the picker never opens and the screen appears frozen. - In an iframe:
allow="camera; microphone".
Ask for the OS permission before you open the flow
Granting the WebView's request is not enough — if the app itself lacks camera permission the system denies it and try-on stalls with no error message. Request it in your own UI, where you can explain why, then open the flow.
Try on the product being discussed
When the conversation surfaces a product, the runtime tells you:
{ "name": "productQuoteChanged",
"payload": { "product": { "code": "SKU123", "name": "Linen Shirt",
"image": "https://…", "vtoEnabled": true } } }Use it to show a native "Try this on" button — vtoEnabled reflects whether
try-on is enabled for the agent — then:
send("startTryOnFromQuote");An empty or absent product means the quote was cleared; hide the button.
Ask about this product
Pins the product as a card above the input without sending a message, so the user types their own question. This is the "Ask about this product" button on a product screen.
send("quoteProduct", { product: { productCode: "SKU123" } });
send("clearProductQuote"); // when they move onThe code is resolved against your catalog, so it must be a real product code.
AI search
For your own search bar: let the user type in your native UI, then hand the query over.
// Cold open
`${CHAT_URL}&searchQuery=${encodeURIComponent("linen shirts under 50")}`
// Warm open
send("searchEscalation", {
query: "linen shirts under 50",
isOnlySearchProducts: false,
});isOnlySearchProducts: true returns product results only; false lets the
assistant answer conversationally. Either way a new conversation starts, so the
search is not mixed into an unrelated thread.
Send a message as the user
For suggestion chips, canned questions, or a "Help me choose" entry point:
send("sendUserMessage", { message: "Which size fits a 42 chest?" });Voice mode
send("startVoiceMode");Requires voice mode to be enabled for the agent and microphone permission on both the WebView and the app. Ignored otherwise.
Keeping context in sync
Context is what makes answers specific. Update it whenever the user's situation changes — it is cheap and can be sent as often as you like.
// User swiped to another product
send("updateContext", {
productContext: {
productCode: "SKU999",
productName: "Wide-Fit Runner",
pageType: "PDP",
},
});
// User logged in
send("updateContext", { userContext: { id: "user-456", membership: "gold" } });
send("syncCustomerDetails", { customerId: "user-456" });| Context | Common keys |
|---|---|
productContext | productCode (must match your catalog code), productName, price, currency, category, pageType, url |
userContext | id, email, name, membership, and optional height / weight / gender used by the size flows |
Use string values, and keep them safe to log — context reaches the assistant and the analytics stream.
Only `productCode` replaces the product
A productContext without a productCode does not change the active product.
To clear it, send a productCode of "", or start a new conversation.
Rotation and layout
When the device rotates or your window's size class changes, tell the runtime so it re-evaluates its layout:
send("mobileStateChange", { isMobile: true });Closing cleanly
Before you dismiss your container from a native control (a back gesture, your own ✕), close any in-chat overlay first so the runtime is not left mid-flow:
send("closeOverlays");
// then dismiss your sheet / pop your routeA complete product screen
Everything a typical product screen wires up:
onReady(() => {
send("initContext", {
productContext: {
productCode: product.code,
productName: product.name,
price: String(product.price),
currency: product.currency,
pageType: "PDP",
},
userContext: user ? { id: user.id } : undefined,
});
});
sizeGuideButton.onTap = () =>
send("startSizeGuide", {
productContext: { productCode: product.code, gender: product.gender },
});
findMySizeButton.onTap = () => send("suggestSize");
tryOnButton.onTap = () =>
send("startVirtualTryOn", { product: { productCode: product.code } });
askButton.onTap = () =>
send("quoteProduct", { product: { productCode: product.code } });
onProductChanged((next) =>
send("updateContext", {
productContext: { productCode: next.code, pageType: "PDP" },
}),
);Gate each button on the agent's published flags so you never show a control that does nothing — see Availability check.
Next: Links & navigation.