> ## Documentation Index
> Fetch the complete documentation index at: https://docs.molin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracking API

> Send the cart, purchases and the logged-in customer to Molin AI, so search ranks the products your customers actually buy.

Call these JavaScript functions from your shop's own pages, programmatically, whenever the cart changes, an order is placed, or a customer logs in.

If your shop runs on Shopify, Shoprenter, UNAS or Shoper, the widget reads this data for you and you do not need this API.

<Warning>
  Load the widget script on **every page** of your shop, including the cart and order confirmation pages. Tracking only works on pages where the script runs. See [how to embed the widget
  script](/apis/experimental/personalization-api#how-to-pass-data).
</Warning>

<Note>Data collection is switched off until we enable it for your shop. Contact us before you start testing.</Note>

## Helper function to avoid a loading race

Copy this helper into your page before you call any tracking function, and wrap every call in it:

```javascript theme={null}
function withMolin(fn) {
  if (window.Molin) {
    fn(window.Molin);
  } else {
    window.addEventListener('molin:ready', () => fn(window.Molin), { once: true });
  }
}
```

## Logged-in customer

Call `linkCustomer` on page load whenever a customer is logged in:

```javascript theme={null}
withMolin((Molin) => Molin.linkCustomer({ id: String(customer.id) }));
```

* send only your internal customer id, never an email, phone number, name, or anything else that identifies a person
* calling it on every page is fine, it only forwards when the id changes

## Line items

A line item is one product in the cart or in the order, with how many the shopper took and at what price. `setCart` and `trackPurchase` both take a `lines` array of them.

Each line item must say which product it is, with at least one of `url`, `sku`, `gtin` or `variantId`. The value must match your product feed character for character, because we use it to find the product in our catalog.

| Field | What | Example |
| - | - | - |
| `url` | The product page URL, without tracking parameters such as `?utm_source=…` | `https://store.example/products/alpine-winter-jacket` |
| `sku` | Your own article number | `AWJ-001` |
| `gtin` | The barcode (EAN, UPC) | `5901234123457` |
| `variantId` | The most specific id your shop has: the variant id when the product has variants, otherwise the product id | `8123456789` |
| `quantity` | A number, more than 0 | `2` |
| `price` | Price of one unit as shown to the shopper, in the shop's currency | `199.5` |

```javascript theme={null}
{
  // at least one of url, sku, gtin, variantId is required
  url: 'https://store.example/products/alpine-winter-jacket',
  sku: 'AWJ-001',
  gtin: '5901234123457',
  variantId: '8123456789',
  quantity: 2,
  price: 199.5,
}
```

## Cart

Call `setCart` on every page load and after every cart change: add, remove, or quantity change. Always send the whole cart, not only the item that changed. An emptied cart is `lines: []`, and please send it too.

```javascript theme={null}
withMolin((Molin) =>
  Molin.setCart({
    lines: [/* line items */],
    total: 448,
    currency: 'EUR',
  }),
);
```

* calling it on every page is fine, it only forwards when the cart changes

## Purchase

Call `trackPurchase` on the order confirmation page, once the order is really confirmed.

```javascript theme={null}
withMolin((Molin) =>
  Molin.trackPurchase({
    orderId: 'ORD-2026-000123',
    lines: [/* line items */],
    total: 457, // what the shopper paid, shipping included
    currency: 'EUR',
  }),
);
```

* run it on every load of the confirmation page, an identical repeat is not sent again and we also deduplicate by `orderId`
* `total` does not have to equal the sum of the lines, discounts, shipping and tax can make them differ

## Field rules

A call that breaks these rules is rejected.

| Field | Rule |
| - | - |
| `id` in `linkCustomer`, `orderId` | Text, not a number: use `String(id)`. Not empty, at most 200 characters |
| `currency` | Three-letter currency code, for example `'EUR'` |
| `total` | Number, 0 or more, in that currency |
| `lines` | At most 200 line items |

## Checking that it works

Open the browser's DevTools console and type `Molin: tracking error` into its filter box. If you see no errors, the calls work.

If a call is wrong, the message names the call and the field to fix, for example `Molin: tracking error - setCart was not sent` followed by `lines.0.price` for the first line's price.

If the message ends with `because storefront events are disabled for this widget`, we have not switched data collection on for your shop yet. Contact us to enable it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.