Client-Side Tracking with Shopify Custom Pixels
How does it work?
When a user clicks on one of your products on moebel.de, meubles.fr, meubelo.nl, moebel24.at, moebel24.ch, mobi24.es, mobi24.it, living24.pl or living24.uk, our server generates a unique clickID (moeclid) which is appended as a query parameter to your landing page URL. With the integration of the base code pixel, the clickID is written into the local storage for 90 days. Our server does not get any requests from you unless the user converts.
If the user makes a purchase on your site and has an existing moeclid value in the local storage of the browser, a conversion event is sent to our server including the clickID and further purchase-related data.
This integration is technically equivalent to our Google Tag Manager client-side setup. The difference is that it is integrated entirely into the Shopify environment: it runs as two custom pixels, without a tag manager and without a change to your theme.
Prerequisites
Before you proceed with this integration, make sure the following conditions are met:
-
You have received your Partner ID or dedicated Sales Tracking Key.
-
You have told your account manager whether your Shopify market settings include tax in product prices and shipping rates.
-
You have access to Settings → Customer events in your Shopify admin.
Integration
The client-side sales tracking for Shopify consists of two custom pixels - the base code pixel and the conversion pixel. Both are created in your Shopify admin under Settings → Customer events with the "Add custom pixel" button.
Both pixels are meant to run for every visitor who reached your shop through one of our portals. Shopify preselects the customer privacy settings of a new custom pixel more restrictively than that, so check them while you create each pixel:
-
Permission is preselected as "Required" with Marketing and Analytics. A pixel with that setting does not run for visitors who decline those purposes, and their sales do not reach us. "Not required" lets it run for every visitor.
-
Data sale is preselected as "Data collected qualifies as data sale". A pixel with that setting stops collecting data for visitors who opted out of the sale of their data.
Adjust both settings to match your own privacy policy. The information the integration records is listed in Tracking of Individual Information.
1. Base Code Pixel
The base code pixel writes the appended moeclid parameter value of the landing page URL into the local storage of the browser. It only stores a value if the user reached your site through one of our portals. It doesn’t affect your traffic from other marketing channels.
-
Click "Add custom pixel", enter a name for the pixel and confirm with "Add pixel"
-
Copy the code snippet of the base code pixel and paste it. This pixel is the same for all portals and contains nothing you have to replace.
analytics.subscribe('page_viewed', (pageViewEvent) => {
const pageUrl = pageViewEvent.context?.document?.location?.href;
if (!pageUrl) return;
const clickId = new URL(pageUrl).searchParams.get('moeclid');
if (!clickId) return;
browser.localStorage.setItem(
'MOEBEL_CLICKOUT_ID',
JSON.stringify({ date: new Date().toISOString(), clickId })
);
});
-
Click "Save" and then "Connect" to activate the pixel
| Since local storage cannot be shared across domains/subdomains, saving the clickID (moeclid) to use at checkout will not work if the actual checkout page is on a different domain/subdomain than the product page the user was sent from us. |
2. Conversion Pixel
The conversion pixel enables sending us a purchase event at the time the user converts. It reads the order data from the Shopify checkout and hands it to our push script. If there is no clickID from the base code pixel in the local storage of the browser, calling the sale function will do nothing. Moreover, this script removes obsolete clickIDs (older than 90 days) from local storage.
-
Click "Add custom pixel", enter a name for the pixel and confirm with "Add pixel"
-
Copy the code snippet of the conversion pixel and paste it. In the row at the top, enter the host name of your shop, the market of the portal your visitors come from and your partner key, exactly as provided by your account manager. The markets are: moebel.de - de, meubles.fr - fr, meubelo.nl - nl, moebel24.at - at, moebel24.ch - ch, mobi24.es - es, mobi24.it - it, living24.pl - pl, living24.uk - gb.
// REPLACE ME: one row per host name of your shop, with the market of the
// portal your visitors come from and the partner key you received for it
const MOEBEL_ROUTES = [
{ hostName: 'www.your-shop.de', market: 'de', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
];
analytics.subscribe('checkout_completed', (checkoutEvent) => {
const checkoutData = checkoutEvent.data?.checkout;
if (!checkoutData) return;
const pageUrl = checkoutEvent.context?.document?.location?.href;
const hostName = pageUrl ? new URL(pageUrl).hostname : undefined;
const routes = MOEBEL_ROUTES.filter((route) => route.hostName.toLowerCase() === hostName);
if (routes.length === 0) {
console.error(`moebel.de sales tracking: no row for host name ${hostName}, nothing was reported`);
return;
}
const lineItems = checkoutData.lineItems.map((lineItem) => ({
item_id : lineItem.variant?.sku,
quantity : lineItem.quantity,
price : lineItem.variant?.price?.amount,
item_category : lineItem.variant?.product?.type,
}));
const salePayload = {
total : checkoutData.subtotalPrice?.amount,
shipping : checkoutData.shippingLine?.price?.amount ?? 0,
currency : checkoutData.subtotalPrice?.currencyCode,
orderId : checkoutData.order?.id,
items : lineItems,
};
// The push script serves every market; data-market selects the endpoint.
// One script per row, so each partner key only goes to its own market.
routes.forEach(({ market, partnerKey }) => {
if (!market?.trim() || !partnerKey?.trim()) {
console.error(`moebel.de sales tracking: the row for ${hostName} needs a market and a partner key, nothing was reported for it`);
return;
}
const pushScript = document.createElement('script');
pushScript.src = 'https://www.moebel.de/partner/push.js';
pushScript.dataset.partnerKey = partnerKey;
pushScript.dataset.market = market;
pushScript.onload = () => {
if (window.MOEBEL_SALES) {
window.MOEBEL_SALES.sale(salePayload);
}
};
pushScript.onerror = () => {
console.error('moebel.de sales tracking: push.js failed to load');
};
document.head.appendChild(pushScript);
});
});
-
Click "Save" and then "Connect" to activate the pixel
Enter the host name as it appears in the address bar of your shop, without protocol, path or port, for example www.your-shop.de. A host name with and without www. counts as two host names: add a row for each if your shop uses both. If the checkout runs on a host name without a row, the pixel reports nothing and logs that host name in the browser console, so place a test order for each checkout flow you offer, check the console and verify the reported amounts against the order.
|
If your store runs on several host names, or one host name serves several of our markets, add one row per combination as described under Several Shops or Markets.
All values of the sale are read from the Shopify checkout, so there is nothing else to configure. The table below describes the parameters and provides example values:
| Parameter | Description | Mandatory Field | Data Type | Example | Comment |
|---|---|---|---|---|---|
total |
total basket value from Shopify’s checkout subtotal ( |
yes |
float |
1499.97 |
2 digits, "." as decimal delimiter, no thousand delimiters |
shipping |
shipping costs |
yes |
float |
29.99 |
2 digits, "." as decimal delimiter, no thousand delimiters |
currency |
currency of the amounts |
yes |
string |
EUR |
ISO 4217 code, taken from the Shopify checkout |
orderId |
your identifier for the order |
no |
string |
1172210481407 |
taken from the Shopify checkout, unique per order |
items |
array of unique items - - - - |
yes |
array |
[{ "item_id": "47886359", "quantity": 1, "price": 899.99, "item_category": "Boxspringbetten" }, { "item_id": "31118801", "quantity": 2, "price": 299.99, "item_category": "Heimtextilien" }] |
Several Shops or Markets
If your store runs on several host names, or one host name serves several of our markets, the rows at the top of the conversion pixel select the markets and partner keys used to report each sale. Add each combination of host name, market and partner key only once.
| The base code pixel stays as it is: one pixel for all host names and markets, because it stores the clickID regardless of the market the click came from. |
Several Host Names in One Store
If your store is reachable under several host names, for example one domain per country, add one row per host name, each with the market of the portal your visitors come from and the partner key you received for that market:
const MOEBEL_ROUTES = [
{ hostName: 'www.your-shop.de', market: 'de', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
{ hostName: 'www.your-shop.at', market: 'at', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
{ hostName: 'www.your-shop.ch', market: 'ch', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
];
One Host Name for Several Markets
If one host name serves several of our markets, for example www.your-shop.com for moebel.de, moebel24.at and moebel24.ch, the host name cannot choose the market. This also applies to country subfolders such as /de-at/, because Shopify does not document that the checkout keeps them. Add one row per market with the same host name, each with the partner key you received for that market:
const MOEBEL_ROUTES = [
{ hostName: 'www.your-shop.com', market: 'de', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
{ hostName: 'www.your-shop.com', market: 'at', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
{ hostName: 'www.your-shop.com', market: 'ch', partnerKey: 'INSERT-YOUR-PARTNER-KEY' },
];
| Each sale then reaches all of these markets, each with its own partner key. Our system assigns it to the partner account the click belongs to; in the other markets it is recorded as not assignable. |