Event types
Cart detection wraps both
fetch and XMLHttpRequest for cart mutation URLs,
then compares the cart state before and after the call. Legacy non-AJAX form
posts to cart add URLs are beaconed optimistically. Heartbeats are skipped
when no product identity exists on the page.
Anonymous identity
- The session id is a client-generated UUID stored in
localStorageand regenerated after 30 minutes of inactivity. Pages that block storage fall back to a per-page random id. - Product identity is the numeric product id when the theme exposes it,
otherwise the URL handle from
/products/<handle>. The variant id is attached when present. - Device class (
mobile,tablet,desktop) derives from the user agent. - Referrer host and UTM parameters are attached only to the first event of a session; later events carry no source data.
View dedupe (two layers)
- Client, per tab — a
sessionStorageflag records that this tab already reported a view of this product. The flag is written only after the send actually succeeds, so a blocked or failed send stays retryable. - Server, per shop + session + product — a configurable window (default 30 minutes, merchant-adjustable 5–1440 in Settings) prevents duplicate view events. The server-side window is what keeps counts honest across reloads and repeated navigation.
Consent gating
Collection is gated by the Shopify Customer Privacy API and only runs when analytics processing is allowed:- At boot the tracker waits up to ~4 s for the consent API, polling and explicitly loading the consent feature if needed.
- A
visitorConsentCollectedsubscription restarts evaluation when the visitor answers the banner, with a slow fallback re-check for late flips. - Every outgoing batch re-checks consent before sending.
- A first-party opt-out flag in
localStorage(_lpi_opt_out = 1) blocks sending regardless of the API state. - Storefronts without the consent API (unmanaged) proceed; an API error also fails open rather than breaking the page.
Transport
- Events are batched (
POST, JSON body{ "events": [...] }, up to 25 per batch) to the app-proxy endpoint with the shop domain as a query parameter. - Non-cart events are debounced ~900 ms; cart events flush immediately.
pagehideand tab-hide flush the queue viasendBeacon; the request iskeepaliveand same-origin.- Failed flushes retry up to 5 times with increasing backoff, then drop the queue rather than retry forever.
The app proxy strips cookies, so requests carry no server-side identity —
the HMAC-signed
shop parameter and the anonymous session UUID are all the
identity that exists.Live View Counter block
- Polls the stats endpoint immediately, once more after ~3 s (so the visitor’s own first heartbeat is counted), then every 5 s; it also refreshes when the tab becomes visible or the section re-renders.
- Renders only when the response is
visible: trueandcount > 0; in every other case it hides itself. It never displays zero or a placeholder number. - Supports singular and plural wording automatically. The label text and the dot, text, and background colors are merchant-configurable in the theme editor; the label default is “people are viewing this right now”.
- Per-product suppression and the minimum-views threshold are enforced server-side (see Settings), so the block cannot be tricked into showing a suppressed product.