Shopify Implementation Guide
DATA Reshape installs on Shopify with three assets:
- A theme snippet rendered from the theme layouts — runs in the storefront window.
- A Custom Pixel added in Shopify Admin (Customer events) — the only surface in Shopify where
checkout/thank-you/order statusevents are observable. - Webhooks under Notifications — for what Shopify knows but the browser never sees.
The snippet and the pixel share the same configuration (subdomain + script ID + mode). One config controls both.
Prerequisites
Only install after your custom domain and SSL certificate are active. The tracking subdomain (e.g. dre2.YOUR_DOMAIN.TLD) must share the same root domain as the storefront for cookies and CORS to work.
You will need:
- Custom tracking subdomain — e.g.
dre2.YOUR_DOMAIN.TLD - Script ID — provided by the DATA Reshape team
Operating modes
Pick the mode that fits your store. The choice is set via the parent field in the shared config.
Mixed mode (parent: 1) — recommended
The snippet runs in the storefront and the Custom Pixel covers checkout, thank-you and order status. You install both; the coordination between them is automatic.
Use it when you can edit theme.liquid. It gives the richest behaviour on storefront pages — full DOM access and in-page consent banner integration.
Pixel-only mode (parent: 0)
Everything runs inside the Custom Pixel. The theme snippet, if installed, detects parent: 0 and exits.
Use it when you cannot or do not want to add scripts to the theme. Trade-off: no DOM access on storefront pages.
Step 1 — Add the theme snippet
Before installing the snippet, delete or comment out every other piece of code in the theme (and every other Custom Pixel / Shopify app) that talks to a destination DATA Reshape manages on this account — Meta Pixel / Conversions API, Google Tag (gtag.js, GTM), TikTok Pixel, Pinterest Tag, Snap Pixel, Klaviyo onsite tracking, etc.
For every destination on your account, DATA Reshape is the one that sends. If theme code or another pixel also fires for the same destination, that destination receives everything twice: double events, double conversions, inflated CPA and inconsistent attribution. The two emitters know nothing about each other, so nothing reconciles them.
Concrete checklist:
- In
theme.liquid/layout/*.liquid— search forfbq(,gtag(,ttq.,pintrk(,snaptr(, any inline<script>containing pixel IDs (G-...,GTM-..., Meta numeric IDs, TikTokpixel_code, etc.). Delete or comment out. - In Customer events (where you're about to add this Custom Pixel) — disable or remove any other custom pixel for the same destinations.
- In Apps — uninstall or pause apps only if tracking is their sole function. If the app also provides other services you actively use (catalog sync, ad management, shop pages, merchant feed, customer support widget, etc.), keep it installed and turn off only the tracking / data-sharing setting inside the app. See the next callout for sales channels specifically.
- In checkout.liquid (Plus only) and in
additional_scripts(Plus) — same audit.
For every destination on your account, DATA Reshape should be the only tracking surface on the store. Anything else either duplicates it or contradicts it.
Do the audit before you go live: duplicates are usually noticed weeks later, in the destination's dashboard, and are painful to unwind.
The official sales channel apps (Facebook & Instagram, Google, TikTok) do more than tracking — they sync your product catalog, manage shop on Facebook, push the merchant feed to Google Merchant Center, etc. If any of those non-tracking features are in use, keep the channel installed and turn off only the tracking / data-sharing portion so it doesn't run in parallel with DATA Reshape. Uninstall the channel only if you are not using anything from it.
For each channel app that maps to a destination DATA Reshape manages on your account, open the app's Settings → Share data section and set the data-sharing mode to off / disabled. Catalog sync, shop pages, merchant feed and other non-tracking features can stay on.
Example — Facebook & Instagram sales channel:
| Setting | Required when DATA Reshape handles Meta tracking | Why |
|---|---|---|
| Share data | Data sharing is off | With this on, Shopify sends its own Meta tracking in parallel with DATA Reshape and every event is counted twice. |
| Catalog | Catalog in sync (keep on) | Catalog sync feeds product data into Meta for Shops, ads and dynamic creatives — it doesn't emit tracking events. Safe and recommended to leave enabled. |
Apply the same logic to the Google sales channel (turn off Google Tag / Enhanced Conversions data sharing, keep Merchant Center feed) and the TikTok sales channel (turn off Pixel / Events API data sharing, keep catalog + Shop integrations).
If DATA Reshape does not manage a particular destination on your account (e.g. Meta tracking handled by DATA Reshape, but Pinterest is left to the official Pinterest channel), leave that channel's data sharing on. The rule applies per destination, not blanket.
Recommended pattern: create a dedicated Liquid snippet so the tracking code lives in one isolated file that's trivial to find, review, and update.
1a. Create the snippet file
Enter your domain and everything below is written with it.
Open Shopify Admin → Online Store → Themes → … → Edit code → in the left tree, scroll to Snippets → Add a new snippet → name it dataReshapeCode (file becomes snippets/dataReshapeCode.liquid). Paste this content:
{%- comment -%} DO NOT MODIFY BELOW {%- endcomment -%}
<script>
const Reshape = window.Reshape = {
shopify: {
config: [
{ sub: 'dre2.YOUR_DOMAIN.TLD', id: 'YOUR_SCRIPT_ID', parent: 1 }
]
}
};
window.reshape = window.reshape || [];
(function(R,e,s,h,a,p,E){function g(x){var c=(""+x).split("."),d=/\.co\.|\.com\.|\.org\.|\.edu\.|\.net\.|\.asn\./.test(x)?3:2;return c.slice(-d).join(".")}var r=g(R.location.hostname),c,i,S=h.shopify,C=S.config;for(i=0;i< C.length;i++){if(g(C[i].sub)===r){c=C[i];break}}if(!c){console.warn("[DATA Reshape] Domain not configured:",R.location.hostname);return}S.parent=c.parent;if(!c.parent)return;var _r=R._reshape=R._reshape||[];R.addEventListener("message",function(v){if(v.data&&v.data.reshape)_r.push(v.data.reshape)});h.setCookie=function(n,v,t,d){try{e.cookie=n+"="+v+";max-age="+t+";domain="+d+";path=/;SameSite=None;Secure"}catch(e){}};h.id=c.id;h.cdn=c.sub;h.sts=new Date().getTime();E=e.getElementsByTagName(s)[0];var P=e.createElement(s);P.async=true;P.src="https://"+c.sub+"/main.js?id="+c.id;E.parentNode.insertBefore(P,E);(function A(n){if(R.Shopify&&R.Shopify.analytics&&R.Shopify.analytics.publish){R.Shopify.analytics.publish("reshape:parent",{})}else if(n<60){setTimeout(function(){A(n+1)},50)}})(0)})(window,document,"script",Reshape);
</script>
{%- if customer -%}
<script>
window.reshape.push({
"user": {
"id": "{{ customer.id }}",
"email": {{ customer.email | json }},
{% if customer.phone %}"phone": {{ customer.phone | json }},{% endif %}
"first_name": {{ customer.first_name | json }},
"last_name": {{ customer.last_name | json }},
{% if customer.default_address %}
"country": {{ customer.default_address.country | json }},
"region": {{ customer.default_address.province | json }},
"city": {{ customer.default_address.city | json }},
"street": {{ customer.default_address.street | json }},
"postal_code": {{ customer.default_address.zip | json }},
{% endif %}
"orders_total_number": {{ customer.orders_count }},
"orders_total_value": {{ customer.total_spent | times: 0.01 | round: 2 }}
}
});
</script>
{%- endif -%}
{%- comment -%} PRODUCT PAGE ENRICHMENT (OPTIONAL) {%- endcomment -%}
{%- if template contains 'product' -%}
{% liquid
assign active_currency = cart.currency.iso_code | default: shop.currency
assign created_timestamp = product.created_at | date: '%s' | times: 1000
assign total_price = 0
for variant in product.variants
assign total_price = total_price | plus: variant.price
endfor
assign average_price = total_price | times: 1.0 | divided_by: product.variants.size | times: 0.01
%}
<script>
window.reshape.push({
"event": {
"name": "product_viewed",
"value": {{ average_price | round: 2 }},
"currency": {{ active_currency | json }}
},
"context": {
"environment": "prod",
"page_type": "product"
},
"products": [
{% for variant in product.variants %}
{
"id": "{{ variant.id }}",
"parent_id": "{{ product.id }}",
"sku": {{ variant.sku | json }},
"parent_sku": {{ product.variants.first.sku | json }},
{% if variant.barcode != blank %}"gtin": {{ variant.barcode | json }},{% endif %}
"name": {{ product.title | json }},
"parent_name": {{ product.title | json }},
"brand": {{ product.vendor | json }},
"type": {% if product.variants.size > 1 %}"variable"{% else %}"simple"{% endif %},
"price_base": {% if variant.compare_at_price > 0 %}{{ variant.compare_at_price | times: 0.01 | round: 2 }}{% else %}{{ variant.price | times: 0.01 | round: 2 }}{% endif %},
"price": {{ variant.price | times: 0.01 | round: 2 }},
"currency": {{ active_currency | json }},
"tax_included": {{ shop.taxes_included }},
"quantity": 1,
"stock_status": {{ variant.available }},
"created_at": {{ created_timestamp }},
"url": {{ variant.url | prepend: shop.secure_url | json }},
"parent_url": {{ product.url | prepend: shop.secure_url | json }},
"image": {{ variant.featured_image.src | default: product.featured_image.src | image_url: width: 1024 | prepend: 'https:' | json }},
"images": [
{% for image in product.images limit: 5 %}
{{ image.src | image_url: width: 1024 | prepend: 'https:' | json }}{% unless forloop.last %},{% endunless %}
{% endfor %}
],
"category": {{ product.type | json }},
"categories": [
{% for collection in product.collections limit: 5 %}
{ "name": {{ collection.title | json }}, "id": "{{ collection.id }}" }{% unless forloop.last %},{% endunless %}
{% endfor %}
],
"properties": {
{% assign variant_values = "" %}
{% for option in variant.options %}
{% if option %}
{% if variant_values != "" %}{% assign variant_values = variant_values | append: " / " %}{% endif %}
{% assign variant_values = variant_values | append: option %}
{% endif %}
{% endfor %}
"variant": {{ variant_values | json }}
}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
});
</script>
{%- endif -%}
Replace YOUR_SCRIPT_ID with the Script ID provided to you — and the tracking subdomain too, if you did not fill it in above — then save.
1b. Include it in every layout
Open layout/theme.liquid and add the render call immediately below {{ content_for_header }}, wrapped in marker comments so future editors don't move or delete it accidentally:
{%- comment -%} DATA RESHAPE TRACKING (PLEASE DO NOT DELETE OR MOVE) {%- endcomment -%}
{% render 'dataReshapeCode' %}
{%- comment -%} END DATA RESHAPE TRACKING {%- endcomment -%}
Save the file.
theme.liquid is the default layout, but it is not always the only one. A template can ask for a different layout with {% layout 'name' %} — page builders such as PageFly, Shogun or GemPages often add their own for landing pages, and a page rendered by one of those layouts would load nothing.
Open the Layout folder in the code editor and add the same three lines to every file that serves pages you want tracked, in the same position. In practice that is theme.liquid plus any custom layout an app or your developer added; password.liquid and gift_card.liquid can be skipped unless you have a reason to track them.
A template starting with {% layout none %} has no layout at all, so nothing you put in the Layout folder reaches it. If such a page matters to you, the snippet has to be rendered from the template itself.
The snippet must run after {{ content_for_header }} so Shopify-injected scripts initialize first. Placing it inside the <head> before content_for_header can leave the snippet initializing before Shopify is ready, delaying the coordination between the theme snippet and the Custom Pixel.
If you are deploying in pixel-only mode, set parent: 0 in the config. The snippet will still render, detect the mode, and exit without injecting anything in the storefront. Keeping the snippet rendered (even in pixel-only mode) makes it trivial to flip back to mixed mode later — just change parent: 1 in the snippet file, no theme edit needed.
Step 2 — Install the Custom Pixel
In Shopify Admin go to Settings → Customer events → Add custom pixel. Name it DATA Reshape, configure the Customer privacy block as described in the section below, and paste this code:
// == CONFIG =========
const Reshape = window.Reshape = {
shopify: {
id: '',
config: [
{ sub: 'dre2.YOUR_DOMAIN.TLD', id: 'YOUR_SCRIPT_ID', parent: 1 }
]
}
};
// == PIXEL ==========
// NOTE: Do not modify below
(function(R,e,s,h,a,p,E){
function g(x){var c=(""+x).split("."),d=/\.co\.|\.com\.|\.org\.|\.edu\.|\.net\.|\.asn\./.test(x)?3:2;return c.slice(-d).join(".")}
var l=a?.context?.document?.location||{},o=l.hostname||"",f=l.pathname||"",w=l.origin||"*",r=g(o),c,i,S=h.shopify,C=S.config;
for(i=0;i< C.length;i++){if(g(C[i].sub)===r){c=C[i];break}}if(!c){console.warn("[DATA Reshape] Domain not configured:",o);return}
h.id=c.id;h.cdn=c.sub;h.sts=new Date().getTime();S.api=api;S.parent=c.parent;i=f&&(f.indexOf(S.id)===1||f.indexOf("checkout")===1||f.indexOf("orders")===1);
var t=c.parent&&!i,_r=R._reshape=R._reshape||[];p.subscribe("all_events",function(d){_r.push(d);if(t){try{parent.postMessage({reshape:d},w)}catch(e){}}});
function M(){E=e.getElementsByTagName(s)[0];var P=e.createElement(s);P.async=true;P.src="https://"+c.sub+"/main.js?id="+c.id;E.parentNode.insertBefore(P,E);}
if(!c.parent||i){M()}else{var y=0;p.subscribe("reshape:parent",function(){y=1});setTimeout(function(){if(!y){S.parent=0;t=0;M()}},1e3)}
})(window,document,"script", Reshape, init, analytics);
Replace YOUR_SCRIPT_ID with the Script ID provided to you — and the tracking subdomain too, if you did not fill it in above. Leave shopify.id as the empty string unless we tell you otherwise. Save and Connect the pixel.
The config array in the theme snippet and the Custom Pixel must contain the same entries. If they drift, the storefront and the pixel will disagree on parent mode or load the wrong script ID.
Customer privacy settings
In the Custom Pixel edit screen, set the Customer privacy block exactly like this:
| Field | Value |
|---|---|
| Permission | Not required (The pixel will always run) |
| Data sale | Data collected does not qualify as data sale (The pixel will collect data when the customer opts out of their data being sold) |
Consent is read from the Shopify-native signal and enforced per destination further down the line, so it is not ignored — it is applied where it can be applied per destination rather than by switching the pixel off. See Consent Overview.
Step 3 — Add the webhooks
Some events never reach a browser. Webhooks bring them over from Shopify.
Go to Shopify Admin → Settings → Notifications → Webhooks → Create webhook and add:
| Event | Format | URL |
|---|---|---|
| Order creation | JSON | https://dre2.YOUR_DOMAIN.TLD/webhooks/shopify/event?id=YOUR_SCRIPT_ID |
| Order cancellation | JSON | https://dre2.YOUR_DOMAIN.TLD/webhooks/shopify/event?id=YOUR_SCRIPT_ID |
The URL is the same for every one of them. Use the same tracking subdomain and Script ID as in the snippet, and leave the API version at the one Shopify offers by default. Your account may need further events — we will tell you which.
When the DATA Reshape team connects your store through the Shopify app, the webhooks are registered automatically. Check the Webhooks list first — if the entries are already there, skip this step.
The setup above describes a single shop with one currency and one storefront domain. If Shopify Markets is active, prices are presented in more than one currency, or the same shop serves several country domains, the settings can differ — contact [email protected] before you create the webhooks and we will tell you what to enter.
Optional — product page enrichment
The Custom Pixel covers the standard storefront and checkout events automatically (product_viewed on PDP, product_added_to_cart, checkout_started, checkout_completed, etc.). On Online Store 2.0 themes, the last part of the snippet you pasted in Step 1a — under the PRODUCT PAGE ENRICHMENT comment — pushes a richer product_viewed from the storefront, so destinations receive variant images, categories and parent SKU — fields the Custom Pixel does not surface on its own.
It is already installed if you copied the whole file. Nothing else to paste.
What it adds to product_viewed: every variant of the product with price, base price, SKU and parent SKU, barcode as gtin, up to five images at 1024px, product type as category, up to five collections as categories, and the variant options as properties.variant.
It only runs on product templates. On every other page the block renders nothing.
Safe to remove. Delete everything from the PRODUCT PAGE ENRICHMENT comment to the end of the snippet file. Tracking keeps working from the Custom Pixel alone, with a lighter product_viewed payload. Do not delete the {% if customer %} part above it: that one is not part of this feature and runs on every page.
Configuration reference
The same shape is used in both places:
Reshape.shopify.config = [
{ sub: 'dre2.YOUR_DOMAIN.TLD', id: 'YOUR_SCRIPT_ID', parent: 1 }
];
| Field | Type | Required | Purpose |
|---|---|---|---|
sub | string | yes | Tracking subdomain that serves the runtime script. Must share the storefront's root domain. The match also covers www., shop., etc. |
id | string | yes | Tracking script ID assigned by DATA Reshape. Used as ?id=... on the runtime script URL. |
parent | number | optional (default 1) | 1 = mixed mode (runtime script in storefront + Custom Pixel coordinating with it). 0 = pixel-only mode (runtime script inside the Custom Pixel only). |
Multi-store config (one merchant, multiple country shops)
The right entry is auto-selected at runtime by matching the current hostname's root domain against sub. One snippet + one pixel can serve every shop.
config: [
{ sub: 'dre2.yourdomain.ro', id: 'SCRIPT_ID_RO', parent: 1 },
{ sub: 'dre2.yourdomain.eu', id: 'SCRIPT_ID_EU', parent: 0 },
{ sub: 'dre2.YOUR_DOMAIN.TLD', id: 'SCRIPT_ID_COM' } // parent defaults to 1
]
Pushing custom events
Beyond the events the Custom Pixel and the optional Liquid block produce automatically, you can push any of the events documented in Events Reference — lead_created, email_subscribed, click, custom interactions, etc.
The methods below are the only way to feed DATA Reshape from your own code. Anything else on the page is ignored.
The right method depends on which surface your code is running on. In mixed mode (parent: 1), most theme code runs in the storefront so you use reshape.push(...) there; code running inside the Custom Pixel still uses the pixel-side method below. In pixel-only mode (parent: 0), only the pixel-side method is available.
- Storefront (theme / theme apps)
- Custom Pixel
Available when parent: 1 is configured. Use it from theme.liquid, any other Liquid template, theme JS files, or third-party storefront apps:
window.reshape = window.reshape || [];
reshape.push({
event: {
name: "lead_created",
value: 250,
currency: "USD",
id: "lead_abc123"
},
user: {
email: "[email protected]",
first_name: "Example First Name"
}
});
Available in any mode, from inside the Custom Pixel code. Uses Shopify's Web Pixels API emitting method on the dedicated reshape:push channel:
const reshapeData = {
event: {
name: "lead_created",
value: 250,
currency: "USD",
id: "lead_abc123"
},
user: {
email: "[email protected]",
first_name: "Example First Name"
}
};
Shopify.analytics.publish('reshape:push', reshapeData);
reshapeData has the exact same shape as the object you would pass to reshape.push(...) in the storefront — see Events Reference for the payload of each event.
Verification
End-to-end verification is performed by the DATA Reshape team. Reach out at [email protected] once the snippet and the Custom Pixel are installed, and we will run the confirmation tests, validate the data flowing to each connected destination, and confirm the configuration is correct.
Troubleshooting
Most installation-time issues you can fix yourself surface as a single browser console message:
| Symptom | Likely cause | Fix |
|---|---|---|
[DATA Reshape] Domain not configured: … in the browser console | The active hostname's root doesn't match any sub entry | Add the right sub for this store, or correct a typo. The match uses root domain only. |
For anything else — events that don't seem to arrive, discrepancies in a destination, any doubt about data quality — write to [email protected] and we will check it.
Next steps
- Confirm destinations — see Destinations for per-platform setup.
- Add custom events — see Events Reference for the full catalog of events DATA Reshape can capture on Shopify beyond what the Custom Pixel covers automatically.
- Validate consent flow — see Consent Overview for how Shopify-native consent signals are picked up automatically.