Checkout and cart for Generic stores
Configure cart mode, the checkout URL template with items, code, conversation id and locale, and the JavaScript cart handler.
Generic stores choose how the assistant adds products to the cart and sends shoppers to checkout in Settings → Checkout and cart.
Cart modes
cart_mode | How "add to cart" works | Discount codes |
|---|---|---|
none | The assistant links product pages | — |
url | The widget opens your add-to-cart URL (from the product's add_to_cart_url, or the template below) | In the checkout template |
hook | Your page registers a JavaScript cart handler; the widget calls it | Optional (applyDiscount) |
Checkout URL template
With a template, the assistant can send a shopper straight to checkout with the products it recommended:
https://shop.example.com/cart/add-multiple?items={items}&coupon={code}&sa_cid={conversation_id}&lang={locale}| Placeholder | Value |
|---|---|
{items} | Comma-separated sku:quantity pairs (SKU or variant id), each part URL-encoded |
{code} | The discount code, or empty |
{conversation_id} | The ShopAssist conversation id (conv_…), or empty; store it on the order for attribution |
{locale} | The shopper's language, for example da-DK |
Without a template, the assistant links your cart page (cart_url) or checkout page (checkout_url) if set, otherwise product pages. Every URL must be HTTPS on one of your allowed domains. ShopAssist appends utm_source=shopassist&utm_medium=chat.
JavaScript cart handler
In hook mode, register a handler after the widget script:
ShopAssist.setCartHandler({
async add(lines) {
// lines: [{ sku, variant_id, quantity, title }]
const response = await fetch("/api/cart/lines", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ lines: lines.map(({ sku, quantity }) => ({ sku, quantity })) }),
})
if (!response.ok) throw new Error("cart failed")
return await response.json() // optional CartSnapshot: { items_count, subtotal, lines, discount_codes, cart_token }
},
async applyDiscount(code) {
const response = await fetch("/api/cart/discount", { method: "POST", body: JSON.stringify({ code }) })
return { applied: response.ok }
},
async linkConversation(conversationId) {
await fetch("/api/cart/attributes", { method: "POST", body: JSON.stringify({ sa_cid: conversationId }) })
},
cartUrl: "/cart",
checkoutUrl: "/checkout",
})A rejected promise is reported to the shopper as "couldn't add to cart". The full interface:
/** Cart handler of Generic stores in hook mode (08 §11.6). A rejected promise reports `platform_error`. */
interface GenericCartHandler {
add(
lines: { sku: string | null; variant_id: string; quantity: number; title: string }[],
): Promise<CartSnapshot | void>
applyDiscount?(code: string): Promise<{ applied: boolean; message?: string | null }>
removeDiscount?(code: string): Promise<void>
getCart?(): Promise<CartSnapshot | null>
/** Store the conversation id on the merchant's cart/order. */
linkConversation?(conversationId: string): Promise<void>
cartUrl?: string
checkoutUrl?: string
}Stock disclosure
Settings → Checkout and cart → Stock disclosure sets what your platform would show: exact quantities, levels only ("few left") or nothing. The assistant applies the stricter of this and your stock disclosure guardrail.
Last updated on
Generic RPC for orders
Let the assistant read order status and run cancellations and returns on any store by answering signed RPC calls from ShopAssist.
Headless and single-page storefronts
Use ShopAssist on Next.js, Hydrogen, Nuxt and other SPAs: route changes, the JavaScript API, strict CSP, the SRI-pinned loader and identity.