Loops AI Docs

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.

SituationApproachWhy
The chat is not open yetAdd a URL parameter and present the containerNothing to wait for, no timing to get right
The chat is already loaded (warm WebView)Send a bridge actionThe 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:

StateResult
No height / weight on recordThe measurement form opens right away.
Measurements known and a productCode in contextThe assistant answers with a size recommendation for that product.
Measurements known, no productCodeNothing 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" },
});
FieldNotes
productCodeOptional if the current context already has one. Must match your Loops AI catalog code.
genderwomen 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=ios

Handle 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 on

The code is resolved against your catalog, so it must be a real product code.

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" });
ContextCommon keys
productContextproductCode (must match your catalog code), productName, price, currency, category, pageType, url
userContextid, 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 route

A 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.

On this page