Your standard Heap installation won't automatically capture Shopify checkout events, like when a customer starts or completes a purchase. To bridge that gap, Shopify provides Web Pixels (Customer Events), a native way to run JavaScript in the checkout context and send data to third-party tools like Heap.
This client-side integration is specifically for capturing checkout events. To learn about capturing additional Shopify events, see our server-side Shopify integration guide.
Overview
This guide walks you through setting up a custom Web Pixel that sends checkout_started and checkout_completed events to Heap using heap.track(). Once configured, you'll be able to:
- Capture checkout funnel events: Know exactly where customers drop off between starting and completing a purchase
- Tie checkout activity to known users by calling heap.identify() with a consistent identifier (like email), checkout events merge with the rest of a user's journey in Heap
- Enrich events with order data: Pass properties like total price, currency, and order ID for richer analysis
- Unlock full behavioral analysis in Heap: break down Shopify checkout performance by browser, device, UTM campaign, referrer, geography, and any other automatically captured Heap user property
Prerequisites
- Familiarity with code (unlike our server-side Shopify integration, this setup process is not done in the Heap UI)
- Your Heap environment ID (env_id)
- Store Owner or Staff access to your Shopify admin
- A user property to use as the identity for matching (typically email, matching how you identify users on your storefront)
Key requirements
-
Load Heap first: The Heap snippet must be loaded before your Web Pixel runs so that
heap.track()is available. -
Identity consistency: Use the same identity (e.g., email) that you use with
heap.identify()on your storefront so checkout events are associated with the correct user in Heap. - Join key: If you use the Heap Shopify integration for server-side order data, ensure your Web Pixel identity aligns with the join key (e.g., email) so events can be reconciled.
-
Event timing: The
checkout_completedevent fires when the "Thank You" page (or first post-purchase upsell page) loads. If the customer closes the tab before that page loads, the event will not fire.
Setup
1. Add a Custom Pixel in Shopify
- In Shopify Admin, go to Settings > Customer events
- Click Add custom pixel
- Give your pixel a name (e.g., “Heap Tracking”)
- Paste the Heap loader and relevant Web Pixel pixel listener snippets (from the examples below)
• Note: Paste your JavaScript directly (no<script>tags) - Replace
YOUR_HEAP_ENV_IDin the Heap loader snippet with your Heap environment ID. You can find this in Heap under Project Settings > Environments - Save and enable the pixel
2. Choose your identity
For checkout events, identify the user before tracking so that events are associated with the correct user in Heap. Use a stable identifier that matches how you identify users elsewhere:
-
Email (
checkout.email): recommended if you use email as your join key or identify users by email on your storefront -
Phone (
checkout.phone): if you identify by phone number -
Checkout token (
checkout.token): for anonymous checkouts; may not match a user identified elsewhere
Call heap.identify() with the chosen identifier before heap.track() when the identifier is available.
3. Label the Heap events
Once the Web Pixel fires successfully, raw events from Shopify will appear in Heap. You can label these events from your Live data feed or the Explore raw events page and use them in analysis.
If you also have the server-side integration set up, we recommend you give these client-side events a distinct name (see Avoid duplicate events below).
Event mapping
You can customize property names to match your internal taxonomy; these are an example:
| Heap event name | Shopify standard event | Identity recommended | Example properties |
| Checkout Started | checkout_started |
checkout.email (fallback: checkout.token) |
checkout_token, subtotal_price, currency
|
Checkout Completed |
checkout_completed |
checkout.email |
order_id, total_price, currency, checkout_token
|
Heap loader and Web Pixel pixel listener code examples
heap.track() is available when your Web Pixel runs.Use one of the examples below for your Shopify Web Pixel (Step 1 in the setup instructions). We recommend the first example because it includes both Checkout Started and Checkout Completed .
Place the Heap loader before your analytics.subscribe(...) listeners.
A) Combined Pixel (both events) — recommended
// Heap loader — must be first
window.heapReadyCb=window.heapReadyCb||[],window.heap=window.heap||[],heap.load=function(e,t){window.heap.envId=e,window.heap.clientConfig=t=t||{},window.heap.clientConfig.shouldFetchServerConfig=!1;var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="https://cdn.us.heap-api.com/config/"+e+"/heap_config.js";var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(a,r);var n=["init","startTracking","stopTracking","track","resetIdentity","identify","getSessionId","getUserId","getIdentity","addUserProperties","addEventProperties","removeEventProperty","clearEventProperties","addAccountProperties","addAdapter","addTransformer","addTransformerFn","onReady","addPageviewProperties","removePageviewProperty","clearPageviewProperties","trackPageview"],i=function(e){return function(){var t=Array.prototype.slice.call(arguments,0);window.heapReadyCb.push({name:e,fn:function(){heap[e]&&heap[e].apply(heap,t)}})}};for(var p=0;p<n.length;p++)heap[n[p]]=i(n[p])};
heap.load("YOUR_HEAP_ENV_ID");
// checkout_started
analytics.subscribe("checkout_started", (event) => {
const checkout = event.data?.checkout;
if (!checkout) return;
const identity = checkout.email ?? checkout.token;
if (!identity) return;
if (window.heap && typeof window.heap.identify === "function") {
heap.identify(identity);
}
if (window.heap && typeof window.heap.track === "function") {
heap.track(
"Placed Order (client)",
{
checkout_token: checkout.token,
subtotal_price: checkout.subtotalPrice?.amount,
currency: checkout.currencyCode ?? checkout.subtotalPrice?.currencyCode,
},
"shopify"
);
}
});
// checkout_completed
analytics.subscribe("checkout_completed", (event) => {
const checkout = event.data?.checkout;
if (!checkout) return;
const identity = checkout.email;
if (!identity) return;
if (window.heap && typeof window.heap.identify === "function") {
heap.identify(identity);
}
if (window.heap && typeof window.heap.track === "function") {
heap.track(
"Confirmed Order (client)",
{
order_id: checkout.order?.id ?? checkout.order_id,
total_price: checkout.totalPrice?.amount,
currency: checkout.currencyCode ?? checkout.totalPrice?.currencyCode,
checkout_token: checkout.token,
},
"shopify"
);
}
});
B) Tracking checkout_started (full Custom Pixel script)
// Heap loader — must be first
window.heapReadyCb=window.heapReadyCb||[],window.heap=window.heap||[],heap.load=function(e,t){window.heap.envId=e,window.heap.clientConfig=t=t||{},window.heap.clientConfig.shouldFetchServerConfig=!1;var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="https://cdn.us.heap-api.com/config/"+e+"/heap_config.js";var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(a,r);var n=["init","startTracking","stopTracking","track","resetIdentity","identify","getSessionId","getUserId","getIdentity","addUserProperties","addEventProperties","removeEventProperty","clearEventProperties","addAccountProperties","addAdapter","addTransformer","addTransformerFn","onReady","addPageviewProperties","removePageviewProperty","clearPageviewProperties","trackPageview"],i=function(e){return function(){var t=Array.prototype.slice.call(arguments,0);window.heapReadyCb.push({name:e,fn:function(){heap[e]&&heap[e].apply(heap,t)}})}};for(var p=0;p<n.length;p++)heap[n[p]]=i(n[p])};
heap.load("YOUR_HEAP_ENV_ID");
// Shopify Web Pixel listener
analytics.subscribe("checkout_started", (event) => {
const checkout = event.data?.checkout;
if (!checkout) return;
const identity = checkout.email;
if (!identity) return;
if (window.heap && typeof window.heap.identify === "function") {
heap.identify(identity);
}
if (window.heap && typeof window.heap.track === "function") {
heap.track(
"Placed Order (client)",
{
order_id: checkout.order?.id ?? checkout.order_id,
total_price: checkout.totalPrice?.amount,
currency: checkout.currencyCode ?? checkout.totalPrice?.currencyCode,
checkout_token: checkout.token,
},
"shopify"
);
}
});
C) Tracking checkout_completed (full Custom Pixel script)
// Heap loader — must be first
window.heapReadyCb=window.heapReadyCb||[],window.heap=window.heap||[],heap.load=function(e,t){window.heap.envId=e,window.heap.clientConfig=t=t||{},window.heap.clientConfig.shouldFetchServerConfig=!1;var a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src="https://cdn.us.heap-api.com/config/"+e+"/heap_config.js";var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(a,r);var n=["init","startTracking","stopTracking","track","resetIdentity","identify","getSessionId","getUserId","getIdentity","addUserProperties","addEventProperties","removeEventProperty","clearEventProperties","addAccountProperties","addAdapter","addTransformer","addTransformerFn","onReady","addPageviewProperties","removePageviewProperty","clearPageviewProperties","trackPageview"],i=function(e){return function(){var t=Array.prototype.slice.call(arguments,0);window.heapReadyCb.push({name:e,fn:function(){heap[e]&&heap[e].apply(heap,t)}})}};for(var p=0;p<n.length;p++)heap[n[p]]=i(n[p])};
heap.load("YOUR_HEAP_ENV_ID");
// Shopify Web Pixel listener
analytics.subscribe("checkout_completed", (event) => {
const checkout = event.data?.checkout;
if (!checkout) return;
const identity = checkout.email ?? checkout.token;
if (!identity) return;
if (window.heap && typeof window.heap.identify === "function") {
heap.identify(identity);
}
if (window.heap && typeof window.heap.track === "function") {
heap.track(
"Confirmed Order (client)",
{
checkout_token: checkout.token,
subtotal_price: checkout.subtotalPrice?.amount,
currency: checkout.currencyCode ?? checkout.subtotalPrice?.currencyCode,
},
"shopify"
);
}
});Other Web Pixel events (optional expansion)
Shopify Web Pixels support additional standard events you can subscribe to using the same heap.track() pattern, including:
| Event | Description |
checkout_address_info_submitted |
Customer submitted address info |
checkout_contact_info_submitted |
Customer submitted contact info |
checkout_shipping_info_submitted |
Customer submitted shipping info |
payment_info_submitted |
Customer submitted payment info |
cart_viewed |
Customer viewed cart |
product_added_to_cart |
Product added to cart |
product_removed_from_cart |
Product removed from cart |
product_viewed |
Product viewed |
collection_viewed |
Collection viewed |
page_viewed |
Page viewed |
search_submitted |
Search submitted |
Avoid duplicate events (Client-side vs Server-side)
If you are also using the Shopify server-side integration to send order data to Heap, you should use a different event name for your Web Pixel events.
Why this matters
- The Shopify integration may already send a server-side purchase event (for example, an order or completed checkout event).
- If your Web Pixel also sends Checkout Completed with the same name, you may see duplicate conversions in charts, funnels, and metrics.
Recommended approach
Use distinct names for client-side (Web Pixel) events. This keeps your tracking clear and prevents double-counting. For example:
| Source | Suggested event name |
| Server-side (Shopify integration) | Placed Order |
| Client-side (Web Pixel) | Placed Order (client) |
| Client-side (Web Pixel) | Confirmed Order (client) |