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

Source: https://shopassist.coec.dk/docs/install/generic/checkout-and-cart · Updated 2026-10-01

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]

| `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 [#checkout-template]

With a template, the assistant can send a shopper straight to checkout with the products it recommended:

```text
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](/docs/analytics/assisted-revenue-and-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 [#cart-handler]

In `hook` mode, register a handler after the widget script:

```js
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:

```ts
/** 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 [#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](/docs/guardrails/stock-disclosure).
