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

# Google Tag Manager

> This guide covers the process for integrating Dema tracking into your website using Google Tag Manager (GTM) with the V2 event API.

## Supported event types

The V2 tracking uses a single generic `dema.event()` method. At minimum, send the following events:

<CardGroup cols={3}>
  <Card title="page_view" icon="eye" href="#page_view">
    Page visits across your site
  </Card>

  <Card title="view_item" icon="tag" href="#view_item">
    Product details page
  </Card>

  <Card title="purchase" icon="file-invoice-dollar" href="#purchase">
    Purchase transactions
  </Card>
</CardGroup>

The following event types are optional but recommended:

<CardGroup cols={3}>
  <Card title="view_item_list" icon="list" href="#view_item_list">
    Product list viewed
  </Card>

  <Card title="select_item" icon="hand-pointer" href="#select_item">
    Item selected from a list
  </Card>

  <Card title="add_to_cart" icon="cart-arrow-down" href="#add_to_cart">
    Item added to cart
  </Card>

  <Card title="remove_from_cart" icon="cart-xmark" href="#remove_from_cart">
    Item removed from cart
  </Card>

  <Card title="view_cart" icon="cart-shopping" href="#view_cart">
    Cart page viewed
  </Card>

  <Card title="add_to_wishlist" icon="heart" href="#add_to_wishlist">
    Item added to wishlist
  </Card>

  <Card title="remove_from_wishlist" icon="heart-crack" href="#remove_from_wishlist">
    Item removed from wishlist
  </Card>

  <Card title="begin_checkout" icon="credit-card" href="#begin_checkout">
    Checkout started
  </Card>
</CardGroup>

The API is extensible — you can send any `event_name` value beyond the ones listed above, and Dema can build custom metrics on top of it. The available fields per event are defined by the [event data schema](#event-data-schema-reference).

***

## Prerequisites

* Google Tag Manager must be installed and running on your website
* A data layer populated with product, cart, and transaction information (Google Analytics 4 ecommerce data layer structure works directly)
* Your unique Dema ID, formatted as `DV-XXXXXXXXXXXXXXXXXXXXXXXX`

<Note>
  On a Product Detail Page (PDP), both `page_view` and `view_item` should fire. This is a change from the legacy V1 tag, where a single pageview event covered both.
</Note>

***

## GTM variables

Set up the following variables once in GTM. All tags below reference these by name.

### `{{Dema > ID}}` — Constant

* **Variable Type:** Constant
* **Value:** Your unique Dema frontend ID, e.g. `DV-12345`

### `{{JS > Referrer}}` — Custom JavaScript

Tracking accuracy depends on a consistent referrer value across all events fired on the same page view. If your site is a single-page application (SPA) and you don't already have a referrer variable, create a **Custom JavaScript** variable named `JS > Referrer`. Two templates are available — pick based on whether GTM's History Change listener reliably populates `gtm.oldUrl` / `gtm.newUrl` on your site.

**Verify in GTM Preview before choosing:** open your site with the container in Preview mode, navigate between two SPA routes, and check the Data Layer panel for a `gtm.historyChange` event with both `gtm.oldUrl` and `gtm.newUrl` keys. Don't guess from the framework name — Next.js, Vue, etc. don't all behave the same.

* **`gtm.oldUrl`-based** (preferred when the keys are present) — reads the previous URL from GTM's History Change event. Requires two Data Layer variables: `{{DLV > gtm.oldUrl}}` and `{{DLV > gtm.newUrl}}`.
* **`sessionStorage`-only fallback** — use when `gtm.historyChange` doesn't fire (or you can't enable the History Change listener). No companion DLVs needed. Caveat: this version reads `location.href` at evaluation time, so it assumes the SPA updates the URL **before** the dataLayer event that triggers the tag fires. The normal `pushState → dataLayer.push({event: 'page_view'})` order satisfies this; if your framework pushes the dataLayer event before updating the URL, reorder the pushes — no JS workaround helps.

<CodeGroup>
  ```javascript gtm.oldUrl-based theme={null}
  function(){
    var oldUrl = {{DLV > gtm.oldUrl}};
    var newUrl = {{DLV > gtm.newUrl}};

    // SPA navigation — previous page URL is the referrer
    if (oldUrl && newUrl && oldUrl !== newUrl) {
      sessionStorage.setItem('cjs_page_referrer', oldUrl);
      return oldUrl;
    }

    // Same page (cookie consent re-fire, other events, page reload) — return cached value
    var stored = sessionStorage.getItem('cjs_page_referrer');
    if (stored) {
      return stored;
    }

    // First page load — cache and return document.referrer
    sessionStorage.setItem('cjs_page_referrer', document.referrer);
    return document.referrer;
  }
  ```

  ```javascript sessionStorage fallback theme={null}
  function(){
    var current = location.href;
    var lastSeen = sessionStorage.getItem('cjs_page_referrer_last_url');
    var referrer = sessionStorage.getItem('cjs_page_referrer');

    if (lastSeen !== current) {
      // URL changed — SPA nav (or first load)
      referrer = lastSeen || document.referrer;
      sessionStorage.setItem('cjs_page_referrer_last_url', current);
      sessionStorage.setItem('cjs_page_referrer', referrer);
    }
    // Same URL (re-fire on same page): return cached referrer

    return referrer;
  }
  ```
</CodeGroup>

Both templates handle the same four cases consistently:

1. **SPA navigation** — the previous URL is captured and returned as referrer (`gtm.oldUrl` reads it from the History Change event; the fallback compares the cached `last_url` against `location.href`).
2. **Cookie consent re-fire / other events on the same page** — returns the cached value from `sessionStorage`, keeping the referrer consistent across all events on the same page view.
3. **Page reload after SPA navigation** — `sessionStorage` survives the reload, so the SPA referrer is preserved.
4. **First page load** — caches and returns `document.referrer` (external referrer).

The `sessionStorage` keys (`cjs_page_referrer`, `cjs_page_referrer_last_url`) are intentionally vendor-neutral so the same variable can back any analytics tag.

***

## Cookie consent

V2 events must be gated by cookie consent. There are two strategies — **Stage + flush** is the default and recommended choice; **GTM native per-tag consent** is a simpler alternative for setups where consent is guaranteed to be resolved before any tracked event ever fires.

### Why Stage + flush is the default

Tracking events routinely fire **before** consent is settled, in two common ways:

1. **Events fire before the user has decided.** A first-time visitor lands on a category page and the `view_item_list` trigger fires while the consent banner is still showing. The user may accept consent seconds later — but with GTM-native gating the initial event was already blocked and is gone. With Stage + flush, the event is queued in `sessionStorage` and replayed the moment consent is granted.
2. **Events fire before the CMP has written consent into a GTM variable.** Many CMPs resolve consent asynchronously after the page has already started pushing dataLayer events, or write the consent cookie before GTM's consent variable has read it back in. Triggers that fire in that window see "consent denied" even though the user already accepted. GTM-native gating will block those events; Stage + flush queues them and flushes when the consent state is finally read into the GTM variable.

Stage + flush also survives navigation — `sessionStorage` persists across pages within the tab, so an event triggered on a page the user left before accepting is still flushed once they accept later.

### Strategy 1 — Stage + flush (default, recommended)

Stage + flush gates `dema.event()` on `{{Consent - Statistics}}` — a **Custom JavaScript** GTM variable that returns `'granted'` or `'denied'` based on the CMP's current statistics/analytics consent state.

<Accordion title="Define the `{{Consent - Statistics}}` GTM variable" icon="cookie-bite">
  Create a new **Custom JavaScript** variable in GTM named `Consent - Statistics`. Pick the body matching your CMP.

  <CodeGroup>
    ```js Cookiebot theme={null}
    function() {
      try {
        return (window.Cookiebot && window.Cookiebot.consent && window.Cookiebot.consent.statistics) ? 'granted' : 'denied';
      } catch (e) {
        return 'denied';
      }
    }
    ```

    ```js OneTrust theme={null}
    function() {
      try {
        var groups = window.OptanonActiveGroups || '';
        // C0002 = Performance/Analytics in OneTrust's default category mapping.
        // Confirm C0002 is your analytics category — some installations
        // remap the category IDs.
        return groups.indexOf(',C0002,') !== -1 ? 'granted' : 'denied';
      } catch (e) {
        return 'denied';
      }
    }
    ```

    ```js Usercentrics theme={null}
    function() {
      try {
        var info = window.UC_UI && window.UC_UI.getServicesBaseInfo && window.UC_UI.getServicesBaseInfo();
        if (!info) return 'denied';
        // Replace with your actual analytics service name
        // (commonly 'Google Analytics' or 'Dema').
        var s = info.find(function(x){ return x.name === '<analytics service name>'; });
        return (s && s.consent && s.consent.status) ? 'granted' : 'denied';
      } catch (e) {
        return 'denied';
      }
    }
    ```

    ```js Iubenda theme={null}
    function() {
      try {
        // Purpose 4 is Iubenda's default for Measurement/Analytics.
        // Confirm — some setups remap purpose IDs.
        return (window._iub && window._iub.cs && window._iub.cs.consent && window._iub.cs.consent.purposes && window._iub.cs.consent.purposes[4]) ? 'granted' : 'denied';
      } catch (e) {
        return 'denied';
      }
    }
    ```

    ```js Consent Mode v2 theme={null}
    function() {
      try {
        return google_tag_data.ics.entries.analytics_storage.update === 'granted' ? 'granted' : 'denied';
      } catch (e) {
        return 'denied';
      }
    }
    ```
  </CodeGroup>

  **Consent dataLayer event** — use this as the Custom Event trigger for the flush tag:

  | CMP                    | Event name              |
  | ---------------------- | ----------------------- |
  | Cookiebot              | `cookie_consent_update` |
  | OneTrust               | `OneTrustGroupsUpdated` |
  | Usercentrics           | `consent_status`        |
  | Iubenda                | `iubenda_consent_given` |
  | Google Consent Mode v2 | `consent_update`        |

  Verify the event name in GTM Preview before publishing — some setups customise it. The variable must resolve to `'granted'` at the moment that event fires.
</Accordion>

Per-event tag — wrap each `dema.event(...)` call with a consent check, queueing the payload when consent isn't granted yet:

```js theme={null}
if ({{Consent - Statistics}} === 'granted') {
  dema.event(payload, options);
} else {
  dema.addToQueue(payload, options);
}
```

`dema.addToQueue` persists the payload to `sessionStorage` (key `dema_pending_events`).

Flush tag — one shared Custom HTML tag that calls `dema.emitPendingEvents()` when consent is granted:

```html theme={null}
<script type="text/javascript">
  !function(){
    var t=window.dema=window.dema||[];
    if(t.bInitialized)return;
    t.init=function(e,v){
      if(!t.bInitialized){
        var i=document.createElement("script");
        i.setAttribute("async",true);
        i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
        document.head.appendChild(i);
      }
    };
    t.functions=["event","addToQueue","emitPendingEvents"];
    for(var e=0;e<t.functions.length;e++){
      t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
    }
    t.init("{{Dema > ID}}","2.0.0");
  }();

  if ({{Consent - Statistics}} === 'granted') {
    dema.emitPendingEvents();
  }
</script>
```

Trigger the flush tag on the CMP's consent dataLayer event (e.g. `cookie_consent_update` for Cookiebot, `OneTrustGroupsUpdated` for OneTrust) — Custom Event trigger.

The flush must fire **whenever the consent state is read into the GTM consent variable**, not only at the moment the user clicks accept. That covers both:

1. **First-time grant** — the user accepts the banner, the CMP pushes the consent event, and `{{Consent - Statistics}}` flips to `'granted'`.
2. **Returning visits** — on page load the CMP reads its existing cookie and pushes the same consent event so the GTM variable is populated. Any events queued earlier in that page load (because their triggers fired before the CMP had populated the consent variable) are flushed at that moment.

Without the second case, events that fired on the initial page load before the CMP had read its cookie would sit in the queue indefinitely.

Do not attach per-event triggers (`view_item_list`, `purchase`, …) to the flush tag — its only job is to drain the queue.

<Warning>
  With Stage + flush, leave **Advanced Settings → Consent Settings → Require additional consent for tag to fire** set to **Not set** on every event tag *and* on the flush tag. The consent decision lives inside the tag body — `if ({{Consent - Statistics}} === 'granted') { … } else { dema.addToQueue(…); }`. If GTM blocks the tag at the harness level, the JavaScript never runs, the queue stays empty, and pre-consent events are dropped silently.
</Warning>

**Queue behaviour:**

* Stored in `sessionStorage` under key `dema_pending_events`. Persists across navigations within the tab.
* Capped at 100 entries — oldest dropped on overflow, so recent funnel state survives.
* Cleared on a successful `emitPendingEvents()` replay.

<Note>
  With Stage + flush, set `page_location: location.href` and `page_referrer` explicitly on every staged event. The library auto-captures these only at the moment `dema.event(...)` actually runs — for queued events that's the flush-tag firing time, potentially a different page than the one that triggered the event.
</Note>

### Strategy 2 — GTM native per-tag consent

Configure each event tag's **Advanced Settings → Consent Settings → Require additional consent for tag to fire** to the appropriate category (typically `analytics_storage`). GTM defers or blocks the tag until consent is granted; no change to the tag bodies.

Use this only when **all** of the following hold:

* The CMP is fully integrated with GTM's consent model.
* The consent state is reliably resolved into the GTM consent variable **before** any tracked event triggers fire.
* You accept losing pre-consent events on a given page — GTM's per-tag defer only survives as long as the tag is waiting on the same page, so if the user navigates away before accepting, those events are gone.

If any of those don't hold, prefer Stage + flush.

***

## Tag implementation

Each event below is implemented as a **Custom HTML** tag in GTM. Every tag includes a self-contained stub initializer — this is intentional and recommended.

<Warning>
  **Always include the stub in every tag that calls `dema.*`.** If an event tag fires before the library is initialized (for example, a user landing directly on an order confirmation page from an email), `dema` will be undefined and the event will be lost. The stub is idempotent and safe to include in every tag.
</Warning>

<Note>
  For events that carry a list of items (`purchase`, `view_cart`, `view_item_list`, `begin_checkout`), pass `{method: 'POST'}` as the second argument to `dema.event()` (and to `dema.addToQueue()`). This avoids hitting URL length limits on GET requests.
</Note>

<Note>
  Each snippet below has two tabs:

  * **Stage + flush** (default, recommended) — applies the consent-aware staging pattern from the [Cookie consent](#cookie-consent) section. Captures events that fire before consent is resolved and replays them on the flush tag.
  * **GTM-native consent** — the bare `dema.event()` call, suitable only when relying on GTM's per-tag consent gating (Strategy 2).
</Note>

### Core event tags

#### `page_view`

Fires on every page view — both PDP and non-PDP pages.

* **Tag Type:** Custom HTML
* **Trigger:** All Pages
* **Data layer fields:** none required — only uses the page referrer

<Accordion title="page_view tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var payload = {
        event_name: 'page_view',
        page_location: location.href,
        page_referrer: {{JS > Referrer}}
      };

      if ({{Consent - Statistics}} === 'granted') {
        dema.event(payload);
      } else {
        dema.addToQueue(payload);
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      dema.event({
        event_name: 'page_view',
        page_referrer: {{JS > Referrer}}
      });
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `view_item`

Fires when a user views a product detail page. Send **in addition to** `page_view` on PDPs.

* **Tag Type:** Custom HTML
* **Trigger:** `view_item` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="view_item tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'view_item',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'view_item',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `purchase`

Fires when a user completes a transaction. Uses POST to avoid URL length limits on item lists.

* **Tag Type:** Custom HTML
* **Trigger:** `purchase` event
* **Data layer fields:**
  * `ecommerce.items[]` — each item with `item_id`, `item_sku`, `index`, `price`, `quantity`
  * `ecommerce.currency` — ISO 4217 currency code
  * `ecommerce.transaction_id` — order ID
  * `ecommerce.value` — order total
  * `ecommerce.shipping` — shipping cost
  * `ecommerce.tax` — tax amount
  * `ecommerce.coupon` — voucher code (optional)
  * `customer.id` — customer identifier
  * `ecommerce.chosen_shipping_method` — shipping provider (optional)
  * `ecommerce.payment_provider` — payment provider, e.g. `"klarna"`, `"stripe"`, `"paypal"` (optional)

<Accordion title="purchase tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      var eventVouchers = [];
      if ({{DLV > ecommerce}}.coupon) {
        eventVouchers.push({code: {{DLV > ecommerce}}.coupon});
      }

      var payload = {
        event_name: 'purchase',
        page_location: location.href,
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        order_id: {{DLV > ecommerce}}.transaction_id,
        total: {{DLV > ecommerce}}.value,
        shipping: {{DLV > ecommerce}}.shipping,
        tax: {{DLV > ecommerce}}.tax,
        vouchers: eventVouchers,
        customer_id: {{DLV > customer.id}},
        shipping_provider: {{DLV > ecommerce.chosen_shipping_method}},
        payment_provider: {{DLV > ecommerce.payment_provider}}
      };
      var options = {method: 'POST'};

      if ({{Consent - Statistics}} === 'granted') {
        dema.event(payload, options);
      } else {
        dema.addToQueue(payload, options);
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      var eventVouchers = [];
      if ({{DLV > ecommerce}}.coupon) {
        eventVouchers.push({code: {{DLV > ecommerce}}.coupon});
      }

      dema.event({
        event_name: 'purchase',
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        order_id: {{DLV > ecommerce}}.transaction_id,
        total: {{DLV > ecommerce}}.value,
        shipping: {{DLV > ecommerce}}.shipping,
        tax: {{DLV > ecommerce}}.tax,
        vouchers: eventVouchers,
        customer_id: {{DLV > customer.id}},
        shipping_provider: {{DLV > ecommerce.chosen_shipping_method}},
        payment_provider: {{DLV > ecommerce.payment_provider}}
      }, {method: 'POST'});
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

### Optional event tags

#### `view_item_list`

Fires when a product list (category page, search results, recommendations) is displayed. Can fire once for all items on the page, or multiple times as the user scrolls and more items become visible.

* **Tag Type:** Custom HTML
* **Trigger:** `view_item_list` event
* **Data layer fields:**
  * `ecommerce.items[]` — each item with `item_id`, `index` (position in the list), `price`
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="view_item_list tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          index: item.index != null ? item.index : i, // position in the list
          price: item.price ? item.price : 0,
          quantity: 1
        });
      }

      if (eventItems.length > 0) {
        var payload = {
          event_name: 'view_item_list',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: eventItems,
          currency: {{DLV > ecommerce}}.currency
        };
        var options = {method: 'POST'};

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload, options);
        } else {
          dema.addToQueue(payload, options);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          index: item.index != null ? item.index : i, // position in the list
          price: item.price ? item.price : 0,
          quantity: 1
        });
      }

      if (eventItems.length > 0) {
        dema.event({
          event_name: 'view_item_list',
          page_referrer: {{JS > Referrer}},
          items: eventItems,
          currency: {{DLV > ecommerce}}.currency
        }, {method: 'POST'});
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `select_item`

Fires when a user selects a product from a list. The `index` field is important — it captures the position at which the item was presented, which powers list performance and click-through analytics.

* **Tag Type:** Custom HTML
* **Trigger:** `select_item` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].index` — position in the list
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="select_item tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'select_item',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // position in the list
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'select_item',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // position in the list
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `add_to_cart`

Fires when a user adds a product to the cart. `quantity` should be the delta (how many were added), not the total in the cart.

* **Tag Type:** Custom HTML
* **Trigger:** `add_to_cart` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].item_sku` — variant/SKU identifier
  * `ecommerce.items[0].index` — position in the cart (hardcode `0` if not supported)
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.items[0].quantity` — quantity added (delta)
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="add_to_cart tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'add_to_cart',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            variant_no: item.item_sku ? item.item_sku : "",
            index: item.index, // hardcode as 0 when cart index is not supported
            price: item.price,
            quantity: item.quantity
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'add_to_cart',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            variant_no: item.item_sku ? item.item_sku : "",
            index: item.index, // hardcode as 0 when cart index is not supported
            price: item.price,
            quantity: item.quantity
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `remove_from_cart`

Fires when a user removes a product from the cart. `quantity` should be the delta (how many were removed), not the total remaining.

* **Tag Type:** Custom HTML
* **Trigger:** `remove_from_cart` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].item_sku` — variant/SKU identifier
  * `ecommerce.items[0].index` — position in the cart (hardcode `0` if not supported)
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.items[0].quantity` — quantity removed (delta)
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="remove_from_cart tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'remove_from_cart',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            variant_no: item.item_sku ? item.item_sku : "",
            index: item.index, // hardcode as 0 when cart index is not supported
            price: item.price,
            quantity: item.quantity
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'remove_from_cart',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            variant_no: item.item_sku ? item.item_sku : "",
            index: item.index, // hardcode as 0 when cart index is not supported
            price: item.price,
            quantity: item.quantity
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `view_cart`

Fires when the cart page is viewed. Uses POST to avoid URL length limits.

* **Tag Type:** Custom HTML
* **Trigger:** `view_cart` event
* **Data layer fields:**
  * `ecommerce.items[]` — each item with `item_id`, `item_sku`, `index`, `price`, `quantity`
  * `ecommerce.currency` — ISO 4217 currency code
  * `ecommerce.value` — cart total

<Accordion title="view_cart tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      var payload = {
        event_name: 'view_cart',
        page_location: location.href,
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        total: {{DLV > ecommerce}}.value
      };
      var options = {method: 'POST'};

      if ({{Consent - Statistics}} === 'granted') {
        dema.event(payload, options);
      } else {
        dema.addToQueue(payload, options);
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      dema.event({
        event_name: 'view_cart',
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        total: {{DLV > ecommerce}}.value
      }, {method: 'POST'});
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `add_to_wishlist`

Fires when a user adds a product to the wishlist.

* **Tag Type:** Custom HTML
* **Trigger:** `add_to_wishlist` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].index` — position in the wishlist (hardcode `0` if not supported)
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="add_to_wishlist tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'add_to_wishlist',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // hardcode as 0 when wishlist index is not supported
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'add_to_wishlist',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // hardcode as 0 when wishlist index is not supported
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `remove_from_wishlist`

Fires when a user removes a product from the wishlist.

* **Tag Type:** Custom HTML
* **Trigger:** `remove_from_wishlist` event
* **Data layer fields:**
  * `ecommerce.items[0].item_id` — product ID
  * `ecommerce.items[0].index` — position in the wishlist (hardcode `0` if not supported)
  * `ecommerce.items[0].price` — unit price
  * `ecommerce.currency` — ISO 4217 currency code

<Accordion title="remove_from_wishlist tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        var payload = {
          event_name: 'remove_from_wishlist',
          page_location: location.href,
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // hardcode as 0 when wishlist index is not supported
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        };

        if ({{Consent - Statistics}} === 'granted') {
          dema.event(payload);
        } else {
          dema.addToQueue(payload);
        }
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var item = {{DLV > ecommerce}}.items && {{DLV > ecommerce}}.items.length > 0 ? {{DLV > ecommerce}}.items[0] : null;
      if (item) {
        dema.event({
          event_name: 'remove_from_wishlist',
          page_referrer: {{JS > Referrer}},
          items: [{
            product_id: item.item_id ? item.item_id : "",
            index: item.index, // hardcode as 0 when wishlist index is not supported
            price: item.price,
            quantity: 1
          }],
          currency: {{DLV > ecommerce}}.currency
        });
      }
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

#### `begin_checkout`

Fires when the user starts the checkout process. Uses POST to avoid URL length limits.

* **Tag Type:** Custom HTML
* **Trigger:** `begin_checkout` event
* **Data layer fields:**
  * `ecommerce.items[]` — each item with `item_id`, `item_sku`, `index`, `price`, `quantity`
  * `ecommerce.currency` — ISO 4217 currency code
  * `ecommerce.value` — cart total

<Accordion title="begin_checkout tag code" icon="code">
  <CodeGroup>
    ```html Stage + flush theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      var payload = {
        event_name: 'begin_checkout',
        page_location: location.href,
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        total: {{DLV > ecommerce}}.value
      };
      var options = {method: 'POST'};

      if ({{Consent - Statistics}} === 'granted') {
        dema.event(payload, options);
      } else {
        dema.addToQueue(payload, options);
      }
    </script>
    ```

    ```html GTM-native consent theme={null}
    <script type="text/javascript">
      !function(){
        var t=window.dema=window.dema||[];
        if(t.bInitialized)return;
        t.init=function(e,v){
          if(!t.bInitialized){
            var i=document.createElement("script");
            i.setAttribute("async",true);
            i.setAttribute("src","https://tag.dema.ai/tag.js?id="+e+"&v="+encodeURIComponent(v));
            document.head.appendChild(i);
          }
        };
        t.functions=["event","addToQueue","emitPendingEvents"];
        for(var e=0;e<t.functions.length;e++){
          t[t.functions[e]]=(function(fn){return function(){Array.prototype.unshift.call(arguments,fn);t.push(arguments);return t;};})(t.functions[e]);
        }
        t.init("{{Dema > ID}}","2.0.0");
      }();

      var eventItems = [];
      var dataLayerItems = {{DLV > ecommerce}}.items || [];
      for (var i = 0; i < dataLayerItems.length; i++) {
        var item = dataLayerItems[i];
        eventItems.push({
          product_id: item.item_id ? item.item_id : "",
          variant_no: item.item_sku ? item.item_sku : "",
          index: item.index != null ? item.index : i,
          price: item.price ? item.price : 0,
          quantity: item.quantity ? item.quantity : 1
        });
      }

      dema.event({
        event_name: 'begin_checkout',
        page_referrer: {{JS > Referrer}},
        items: eventItems,
        currency: {{DLV > ecommerce}}.currency,
        total: {{DLV > ecommerce}}.value
      }, {method: 'POST'});
    </script>
    ```
  </CodeGroup>
</Accordion>

[↑ Back to supported event types](#supported-event-types)

***

## Event data schema reference

The event detail payload must conform to the **TrackedEventData** schema. Below is a summary of the available fields.

<Accordion title="View full TrackedEventData JSON schema">
  ```json theme={null}
  {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "title": "TrackedEventData",
    "description": "Payload containing event data sent with a server-side tracking request.",
    "type": "object",
    "required": ["event_name"],
    "properties": {
      "event_name": {
        "type": "string",
        "description": "Name of the tracked event."
      },
      "type": {
        "type": "string",
        "description": "Event-specific type. For example, for order events the type can be set to `EXCLUDED` to prevent the order from appearing in the platform."
      },
      "user_agent": {
        "type": "string",
        "description": "User agent string of the client sending the tracked event."
      },
      "ip": {
        "type": "string",
        "description": "IP address sent in tracking event or resolved by the tracking system."
      },
      "country": {
        "type": "string",
        "description": "Two-letter ISO 3166-1 alpha-2 country code. When provided, overrides the country resolved from the IP address."
      },
      "region": {
        "type": "string",
        "description": "Region (state/province). When provided, overrides the region resolved from the IP address."
      },
      "zip_code": {
        "type": "string",
        "description": "ZIP/postal code. When provided, overrides the ZIP/postal code resolved from the IP address."
      },
      "city": {
        "type": "string",
        "description": "City. When provided, overrides the city resolved from the IP address."
      },
      "latitude": {
        "type": "number",
        "description": "Latitude coordinate. When provided, overrides the latitude resolved from the IP address."
      },
      "longitude": {
        "type": "number",
        "description": "Longitude coordinate. When provided, overrides the longitude resolved from the IP address."
      },
      "page_id": {
        "type": "string",
        "description": "Language-agnostic identifier of the page on which the event occurred."
      },
      "page_location": {
        "type": "string",
        "description": "Full URL of the page on which the event occurred."
      },
      "page_title": {
        "type": "string",
        "description": "Title of the page on which the event occurred."
      },
      "page_referrer": {
        "type": "string",
        "description": "Referrer URL from which the user navigated to the current page."
      },
      "cart_id": {
        "type": "string",
        "description": "Identifier of the shopping cart."
      },
      "wishlist_id": {
        "type": "string",
        "description": "Identifier of the wishlist."
      },
      "items": {
        "type": "array",
        "description": "List of items associated with the tracked event.",
        "items": {
          "$ref": "#/definitions/TrackedEventDataItem"
        }
      },
      "order_id": {
        "type": "string",
        "description": "Identifier of the order associated with the tracked event."
      },
      "currency": {
        "type": "string",
        "description": "Three letter ISO 4217 Currency code for the transaction."
      },
      "total": {
        "type": "number",
        "description": "Total value of the transaction, including items, tax and shipping."
      },
      "tax": {
        "type": "number",
        "description": "Total tax amount for the transaction including shipping tax."
      },
      "shipping": {
        "type": "number",
        "description": "Shipping cost for the transaction including tax."
      },
      "payment_provider": {
        "type": "string",
        "description": "Payment provider used for the transaction."
      },
      "shipping_provider": {
        "type": "string",
        "description": "Shipping provider used for the transaction."
      },
      "customer_id": {
        "type": "string",
        "description": "Identifier of the customer."
      },
      "vouchers": {
        "type": "array",
        "description": "List of vouchers applied in the transaction.",
        "items": {
          "$ref": "#/definitions/TrackedEventDataVoucher"
        }
      },
      "custom_attribute_1": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_2": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_3": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_4": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_5": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_6": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_7": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_8": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_9": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_10": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_11": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_12": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_13": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_14": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_15": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_16": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_17": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_18": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_19": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_20": { "type": "string", "description": "Custom attribute on tracked event level." },
      "custom_attribute_1_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_2_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_3_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_4_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_5_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_6_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_7_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_8_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_9_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_10_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_11_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_12_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_13_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_14_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_15_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_16_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_17_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_18_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_19_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_20_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
      "custom_attribute_1_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_2_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_3_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_4_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_5_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_6_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_7_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_8_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_9_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_10_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_11_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_12_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_13_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_14_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_15_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_16_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_17_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_18_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_19_float": { "type": "number", "description": "Custom attribute float value on tracked event level." },
      "custom_attribute_20_float": { "type": "number", "description": "Custom attribute float value on tracked event level." }
    },
    "additionalProperties": false,
    "definitions": {
      "TrackedEventDataItem": {
        "type": "object",
        "required": ["product_id"],
        "description": "Item line associated with a tracked event.",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Index of the item line in the tracked event. This value is event context specific e.g. in select_item event it represents the index of the item in the list that was selected, in add_to_cart event it represents the index of the item in the cart etc."
          },
          "product_id": { "type": "string", "description": "Product identifier used for identifying the product in the external system." },
          "variant_no": { "type": "string", "description": "Identifier of the specific product variant." },
          "quantity": { "type": "number", "description": "Quantity of the item line." },
          "price": { "type": "number", "description": "Price of a single item unit." },
          "warehouse": { "type": "string", "description": "Warehouse associated with the item. Use the same warehouse names consistently across orders, inventory, and indeliveries — we recommend using human-readable names rather than internal IDs." },
          "custom_attribute_1": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_2": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_3": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_4": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_5": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_6": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_7": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_8": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_9": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_10": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_11": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_12": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_13": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_14": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_15": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_16": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_17": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_18": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_19": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_20": { "type": "string", "description": "Custom attribute on tracked event item level." },
          "custom_attribute_1_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_2_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_3_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_4_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_5_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_6_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_7_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_8_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_9_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_10_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_11_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_12_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_13_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_14_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_15_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_16_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_17_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_18_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_19_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_20_meta": { "$ref": "#/definitions/CustomAttributeMeta" },
          "custom_attribute_1_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_2_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_3_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_4_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_5_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_6_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_7_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_8_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_9_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_10_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_11_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_12_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_13_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_14_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_15_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_16_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_17_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_18_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_19_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." },
          "custom_attribute_20_float": { "type": "number", "description": "Custom attribute float value on tracked event item level." }
        },
        "additionalProperties": false
      },
      "TrackedEventDataVoucher": {
        "type": "object",
        "description": "A voucher (discount) applied to the transaction.",
        "required": ["code"],
        "properties": {
          "code": { "type": "string", "description": "The voucher's redemption code." },
          "name": { "type": "string", "description": "Voucher name." },
          "discount": { "type": "number", "description": "Value of the voucher discount." },
          "type": { "type": "string", "description": "Type of the discount." }
        },
        "additionalProperties": false
      },
      "CustomAttributeMeta": {
        "type": "object",
        "description": "Per-slot semantics for a custom attribute value. Omitting the companion meta field means plain text.",
        "properties": {
          "dataType": {
            "type": "string",
            "description": "Value type: TEXT (dimension), NUMERIC (unit-less metric), MONETARY (metric requiring currency conversion)."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 source currency for MONETARY values. Ignored for TEXT/NUMERIC."
          },
          "features": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Opt-in behaviour flags. Supported value: SPLIT_ACROSS_ORDER_LINES."
          }
        },
        "additionalProperties": false
      }
    }
  }
  ```
</Accordion>

### Event fields

<AccordionGroup>
  <Accordion title="Required fields" icon="asterisk">
    <ResponseField name="event_name" type="string" required>
      Name of the tracked event (e.g., `page_view`, `view_item`, `purchase`).
    </ResponseField>
  </Accordion>

  <Accordion title="Page & navigation fields" icon="globe">
    <ResponseField name="page_location" type="string">
      Full URL of the page where the event occurred.
    </ResponseField>

    <ResponseField name="page_referrer" type="string">
      Referrer URL from which the user navigated to the current page.
    </ResponseField>

    <ResponseField name="page_title" type="string">
      Title of the page where the event occurred.
    </ResponseField>

    <ResponseField name="page_id" type="string">
      Language-agnostic identifier of the page on which the event occurred.
    </ResponseField>
  </Accordion>

  <Accordion title="Client & location fields" icon="location-dot">
    <ResponseField name="user_agent" type="string">
      User agent string of the client sending the tracked event.
    </ResponseField>

    <ResponseField name="ip" type="string">
      IP address of the client. Used to resolve location fields if not provided explicitly.
    </ResponseField>

    <ResponseField name="country" type="string">
      Two-letter ISO 3166-1 alpha-2 country code. When provided, overrides the country resolved from the IP address.
    </ResponseField>

    <ResponseField name="region" type="string">
      Region (state/province). When provided, overrides the region resolved from the IP address.
    </ResponseField>

    <ResponseField name="city" type="string">
      City. When provided, overrides the city resolved from the IP address.
    </ResponseField>

    <ResponseField name="zip_code" type="string">
      ZIP/postal code. When provided, overrides the ZIP/postal code resolved from the IP address.
    </ResponseField>

    <ResponseField name="latitude" type="number">
      Latitude coordinate. When provided, overrides the latitude resolved from the IP address.
    </ResponseField>

    <ResponseField name="longitude" type="number">
      Longitude coordinate. When provided, overrides the longitude resolved from the IP address.
    </ResponseField>
  </Accordion>

  <Accordion title="Event detail fields" icon="list-check">
    <ResponseField name="type" type="string">
      Event-specific type. For example, for order events the type can be set to `EXCLUDED` to prevent the order from appearing in the platform.
    </ResponseField>

    <ResponseField name="order_id" type="string">
      Identifier of the order associated with the tracked event.
    </ResponseField>

    <ResponseField name="customer_id" type="string">
      Identifier of the customer. Typically an email address, but can be any consistent identifier.
    </ResponseField>

    <ResponseField name="currency" type="string">
      Three-letter ISO 4217 currency code (e.g., `EUR`, `USD`).
    </ResponseField>

    <ResponseField name="total" type="number">
      Total value of the transaction, including items, tax, and shipping.
    </ResponseField>

    <ResponseField name="tax" type="number">
      Total tax amount for the transaction including shipping tax.
    </ResponseField>

    <ResponseField name="shipping" type="number">
      Shipping cost for the transaction including tax.
    </ResponseField>

    <ResponseField name="payment_provider" type="string">
      Payment provider used for the transaction.
    </ResponseField>

    <ResponseField name="shipping_provider" type="string">
      Shipping provider used for the transaction.
    </ResponseField>

    <ResponseField name="cart_id" type="string">
      Identifier of the shopping cart.
    </ResponseField>

    <ResponseField name="wishlist_id" type="string">
      Identifier of the wishlist.
    </ResponseField>

    <ResponseField name="items" type="array">
      List of items associated with the event. See [item fields](#item-fields-items) below.
    </ResponseField>

    <ResponseField name="vouchers" type="array">
      List of vouchers applied to the transaction. See [voucher fields](#voucher-fields-vouchers) below.
    </ResponseField>
  </Accordion>

  <Accordion title="Custom attributes" icon="tags">
    <ResponseField name="custom_attribute_1 – custom_attribute_20" type="string">
      Text value for the custom event-level attribute slot (slots 1–20). Use this field when the attribute is a text dimension.
    </ResponseField>

    <ResponseField name="custom_attribute_1_float – custom_attribute_20_float" type="number">
      Numeric value for the custom event-level attribute slot. Use this field when the attribute is a numeric or monetary metric.
    </ResponseField>

    <ResponseField name="custom_attribute_1_meta – custom_attribute_20_meta" type="object">
      Controls which slot Dema reads for this attribute and how the value is processed. Without meta, the string slot is treated as a plain text dimension. Set `dataType` to `NUMERIC` or `MONETARY` to have Dema read the float slot as a metric — monetary values are then converted to the merchant currency using `currency` as the source currency, and `SPLIT_ACROSS_ORDER_LINES` can be added to `features` to distribute an order-level value across order lines. See [CustomAttributeMeta](#customattributemeta) below.
    </ResponseField>
  </Accordion>
</AccordionGroup>

### Item fields (`items[]`)

<AccordionGroup>
  <Accordion title="Required fields" icon="asterisk">
    <ResponseField name="product_id" type="string" required>
      Product identifier used for identifying the product in the external system.
    </ResponseField>
  </Accordion>

  <Accordion title="Optional fields" icon="list">
    <ResponseField name="index" type="integer">
      Index of the item line in the tracked event. Event context specific — e.g., in `select_item` it represents the item's position in the list; in `add_to_cart` it represents the item's position in the cart.
    </ResponseField>

    <ResponseField name="variant_no" type="string">
      Identifier of the specific product variant.
    </ResponseField>

    <ResponseField name="quantity" type="number">
      Quantity of the item line.
    </ResponseField>

    <ResponseField name="price" type="number">
      Price of a single item unit.
    </ResponseField>

    <ResponseField name="warehouse" type="string">
      Warehouse associated with the item. Use the same warehouse names consistently across orders, inventory, and indeliveries — we recommend using human-readable names rather than internal IDs.
    </ResponseField>

    <ResponseField name="custom_attribute_1 – custom_attribute_20" type="string">
      Text value for the custom item-level attribute slot (slots 1–20). Use this field when the attribute is a text dimension.
    </ResponseField>

    <ResponseField name="custom_attribute_1_float – custom_attribute_20_float" type="number">
      Numeric value for the custom item-level attribute slot. Use this field when the attribute is a numeric or monetary metric.
    </ResponseField>

    <ResponseField name="custom_attribute_1_meta – custom_attribute_20_meta" type="object">
      Controls which slot Dema reads for this attribute and how the value is processed. Without meta, the string slot is treated as a plain text dimension. Set `dataType` to `NUMERIC` or `MONETARY` to have Dema read the float slot as a metric — monetary values are then converted to the merchant currency using `currency` as the source currency. See [CustomAttributeMeta](#customattributemeta) below.
    </ResponseField>
  </Accordion>
</AccordionGroup>

### Voucher fields (`vouchers[]`)

<AccordionGroup>
  <Accordion title="Required fields" icon="asterisk">
    <ResponseField name="code" type="string" required>
      The voucher's redemption code.
    </ResponseField>
  </Accordion>

  <Accordion title="Optional fields" icon="list">
    <ResponseField name="name" type="string">
      Voucher name.
    </ResponseField>

    <ResponseField name="discount" type="number">
      Value of the voucher discount.
    </ResponseField>

    <ResponseField name="type" type="string">
      Type of the discount.
    </ResponseField>
  </Accordion>
</AccordionGroup>

### CustomAttributeMeta

The `custom_attribute_N_meta` companion controls which slot Dema reads for the attribute and how it is processed. Without it, the string slot is used as a plain text dimension and the float slot is ignored.

| Field      | Type      | Description                                                                                                                                                                                                                                                      |
| ---------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dataType` | string    | `TEXT` — read the string slot as a text dimension (default when meta is omitted). `NUMERIC` — read the float slot as a unit-less metric. `MONETARY` — read the float slot as a monetary metric and convert to the merchant currency using `currency`.            |
| `currency` | string    | ISO 4217 source currency of the float value (e.g. `EUR`). Required when `dataType` is `MONETARY`; ignored otherwise.                                                                                                                                             |
| `features` | string\[] | Opt-in processing flags. `SPLIT_ACROSS_ORDER_LINES` — event-level attributes only. Distributes an order-level value across order lines by contributing weight (the same mechanism used for shipping and logistic costs). Has no effect on item-level attributes. |

***

## Additional notes

* **`page_referrer` consistency:** The same `page_referrer` value should be passed across all events fired on the same page view. The `{{JS > Referrer}}` variable above handles this via `sessionStorage`.
* **SPA navigation:** On single-page applications, `document.referrer` does not update on client-side navigation. The `{{JS > Referrer}}` variable bridges this gap — either by reading `gtm.oldUrl` from GTM's History Change events, or via the `sessionStorage` fallback template above.
* **Custom event types:** The `event_name` field is open-ended. You can emit custom events (e.g. `quiz_completed`, `search_performed`) and Dema can build custom metrics on top. Use `snake_case` and avoid collisions with built-in event names.
