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.
Supported event types
The V2 tracking uses a single genericdema.event() method. At minimum, send the following events:
page_view
Page visits across your site
view_item
Product details page
purchase
Purchase transactions
view_item_list
Product list viewed
select_item
Item selected from a list
add_to_cart
Item added to cart
remove_from_cart
Item removed from cart
view_cart
Cart page viewed
add_to_wishlist
Item added to wishlist
remove_from_wishlist
Item removed from wishlist
begin_checkout
Checkout started
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.
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
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.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 whengtm.historyChangedoesn’t fire (or you can’t enable the History Change listener). No companion DLVs needed. Caveat: this version readslocation.hrefat evaluation time, so it assumes the SPA updates the URL before the dataLayer event that triggers the tag fires. The normalpushState → 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.
- SPA navigation — the previous URL is captured and returned as referrer (
gtm.oldUrlreads it from the History Change event; the fallback compares the cachedlast_urlagainstlocation.href). - 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. - Page reload after SPA navigation —
sessionStoragesurvives the reload, so the SPA referrer is preserved. - First page load — caches and returns
document.referrer(external referrer).
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:- Events fire before the user has decided. A first-time visitor lands on a category page and the
view_item_listtrigger 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 insessionStorageand replayed the moment consent is granted. - 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.
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 gatesdema.event() on {{Consent - Statistics}} — a Custom JavaScript GTM variable that returns 'granted' or 'denied' based on the CMP’s current statistics/analytics consent state.
Define the `{{Consent - Statistics}}` GTM variable
Define the `{{Consent - Statistics}}` GTM variable
Create a new Custom JavaScript variable in GTM named Consent dataLayer event — use this as the Custom Event trigger for the flush tag:
Verify the event name in GTM Preview before publishing — some setups customise it. The variable must resolve to
Consent - Statistics. Pick the body matching your CMP.| CMP | Event name |
|---|---|
| Cookiebot | cookie_consent_update |
| OneTrust | OneTrustGroupsUpdated |
| Usercentrics | consent_status |
| Iubenda | iubenda_consent_given |
| Google Consent Mode v2 | consent_update |
'granted' at the moment that event fires.dema.event(...) call with a consent check, queueing the payload when consent isn’t granted yet:
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:
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:
- First-time grant — the user accepts the banner, the CMP pushes the consent event, and
{{Consent - Statistics}}flips to'granted'. - 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.
view_item_list, purchase, …) to the flush tag — its only job is to drain the queue.
Queue behaviour:
- Stored in
sessionStorageunder keydema_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.
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.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 (typicallyanalytics_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.
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.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.Each snippet below has two tabs:
- Stage + flush (default, recommended) — applies the consent-aware staging pattern from the 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).
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
page_view tag code
page_view tag code
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_itemevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].price— unit priceecommerce.currency— ISO 4217 currency code
view_item tag code
view_item tag code
purchase
Fires when a user completes a transaction. Uses POST to avoid URL length limits on item lists.
- Tag Type: Custom HTML
- Trigger:
purchaseevent - Data layer fields:
ecommerce.items[]— each item withitem_id,item_sku,index,price,quantityecommerce.currency— ISO 4217 currency codeecommerce.transaction_id— order IDecommerce.value— order totalecommerce.shipping— shipping costecommerce.tax— tax amountecommerce.coupon— voucher code (optional)customer.id— customer identifierecommerce.chosen_shipping_method— shipping provider (optional)ecommerce.payment_provider— payment provider, e.g."klarna","stripe","paypal"(optional)
purchase tag code
purchase tag code
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_listevent - Data layer fields:
ecommerce.items[]— each item withitem_id,index(position in the list),priceecommerce.currency— ISO 4217 currency code
view_item_list tag code
view_item_list tag code
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_itemevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].index— position in the listecommerce.items[0].price— unit priceecommerce.currency— ISO 4217 currency code
select_item tag code
select_item tag code
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_cartevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].item_sku— variant/SKU identifierecommerce.items[0].index— position in the cart (hardcode0if not supported)ecommerce.items[0].price— unit priceecommerce.items[0].quantity— quantity added (delta)ecommerce.currency— ISO 4217 currency code
add_to_cart tag code
add_to_cart tag code
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_cartevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].item_sku— variant/SKU identifierecommerce.items[0].index— position in the cart (hardcode0if not supported)ecommerce.items[0].price— unit priceecommerce.items[0].quantity— quantity removed (delta)ecommerce.currency— ISO 4217 currency code
remove_from_cart tag code
remove_from_cart tag code
view_cart
Fires when the cart page is viewed. Uses POST to avoid URL length limits.
- Tag Type: Custom HTML
- Trigger:
view_cartevent - Data layer fields:
ecommerce.items[]— each item withitem_id,item_sku,index,price,quantityecommerce.currency— ISO 4217 currency codeecommerce.value— cart total
view_cart tag code
view_cart tag code
add_to_wishlist
Fires when a user adds a product to the wishlist.
- Tag Type: Custom HTML
- Trigger:
add_to_wishlistevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].index— position in the wishlist (hardcode0if not supported)ecommerce.items[0].price— unit priceecommerce.currency— ISO 4217 currency code
add_to_wishlist tag code
add_to_wishlist tag code
remove_from_wishlist
Fires when a user removes a product from the wishlist.
- Tag Type: Custom HTML
- Trigger:
remove_from_wishlistevent - Data layer fields:
ecommerce.items[0].item_id— product IDecommerce.items[0].index— position in the wishlist (hardcode0if not supported)ecommerce.items[0].price— unit priceecommerce.currency— ISO 4217 currency code
remove_from_wishlist tag code
remove_from_wishlist tag code
begin_checkout
Fires when the user starts the checkout process. Uses POST to avoid URL length limits.
- Tag Type: Custom HTML
- Trigger:
begin_checkoutevent - Data layer fields:
ecommerce.items[]— each item withitem_id,item_sku,index,price,quantityecommerce.currency— ISO 4217 currency codeecommerce.value— cart total
begin_checkout tag code
begin_checkout tag code
Event data schema reference
The event detail payload must conform to the TrackedEventData schema. Below is a summary of the available fields.View full TrackedEventData JSON schema
View full TrackedEventData JSON schema
Event fields
Required fields
Required fields
Name of the tracked event (e.g.,
page_view, view_item, purchase).Page & navigation fields
Page & navigation fields
Client & location fields
Client & location fields
User agent string of the client sending the tracked event.
IP address of the client. Used to resolve location fields if not provided explicitly.
Two-letter ISO 3166-1 alpha-2 country code. When provided, overrides the country resolved from the IP address.
Region (state/province). When provided, overrides the region resolved from the IP address.
City. When provided, overrides the city resolved from the IP address.
ZIP/postal code. When provided, overrides the ZIP/postal code resolved from the IP address.
Latitude coordinate. When provided, overrides the latitude resolved from the IP address.
Longitude coordinate. When provided, overrides the longitude resolved from the IP address.
Event detail fields
Event detail fields
Event-specific type. For example, for order events the type can be set to
EXCLUDED to prevent the order from appearing in the platform.Identifier of the order associated with the tracked event.
Identifier of the customer. Typically an email address, but can be any consistent identifier.
Three-letter ISO 4217 currency code (e.g.,
EUR, USD).Total value of the transaction, including items, tax, and shipping.
Total tax amount for the transaction including shipping tax.
Shipping cost for the transaction including tax.
Payment provider used for the transaction.
Shipping provider used for the transaction.
Identifier of the shopping cart.
Identifier of the wishlist.
List of items associated with the event. See item fields below.
List of vouchers applied to the transaction. See voucher fields below.
Custom attributes
Custom attributes
Custom attributes for event-level data.
Item fields (items[])
Required fields
Required fields
Product identifier used for identifying the product in the external system.
Optional fields
Optional fields
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.Identifier of the specific product variant.
Quantity of the item line.
Price of a single item unit.
Warehouse associated with the item.
Custom attributes for item-level data.
Voucher fields (vouchers[])
Required fields
Required fields
The voucher’s redemption code.
Additional notes
page_referrerconsistency: The samepage_referrervalue should be passed across all events fired on the same page view. The{{JS > Referrer}}variable above handles this viasessionStorage.- SPA navigation: On single-page applications,
document.referrerdoes not update on client-side navigation. The{{JS > Referrer}}variable bridges this gap — either by readinggtm.oldUrlfrom GTM’s History Change events, or via thesessionStoragefallback template above. - Custom event types: The
event_namefield is open-ended. You can emit custom events (e.g.quiz_completed,search_performed) and Dema can build custom metrics on top. Usesnake_caseand avoid collisions with built-in event names.

