The field-level spec for the four Tie ecommerce events. All examples use a fictional acme-coffee.com store; swap in your real product data.
Start with the table below. How much of this you implement depends on your platform, and on two of the three setups you do not implement all four events. Reading the spec without checking that first is the most common way this goes wrong in both directions: building code you did not need, or leaving a gap that looks fine right up until the reports are empty.
Who implements which event
Your setup |
|
|
Standard Shopify (Shopify-hosted theme) | Tie | Tie |
Headless Shopify (Hydrogen, Oxygen, Next.js or similar front end on a Shopify backend) | Your developers | The Customer Events pixel you add in Shopify Admin. No code. |
Everything else (WooCommerce, Magento, BigCommerce, Salesforce Commerce Cloud, fully custom) | Your developers | Your developers |
On standard Shopify you implement nothing on this page. The Tie Pixel loader and the Customer Events pixel together produce all four events for you, so treat this article as reference only.
On headless Shopify your storefront runs your code but your checkout still runs on Shopify. Your app cannot see the checkout, so the Customer Events pixel captures those two events instead. Adding that pixel is a required step, not an optional one: skip it and Tie receives browsing activity with no checkouts and no orders, which looks healthy from the outside.
On everything else you implement all four. Read the note on rr_purchase below before you scope the work, because on these platforms that one event carries more weight than the other three.
On non-Shopify platforms, rr_purchase is your order data
Tie reads order history directly from Shopify on both Shopify setups. On every other platform it does not, so rr_purchase is the only way Tie ever learns about an order.
That makes it the event to get right and the event to verify on its own. It is also the one most often missed, because it lives on the order confirmation page, a different template from your product and cart pages. A site can have three events firing perfectly and still deliver no orders at all.
How it works
Your site pushes events. As a shopper browses, your storefront pushes the
rr_*events intowindow.dataLayer.The loader listens. The Tie Pixel loader picks up those pushes and reads the fields it needs.
Tie resolves identity. Those signals go to Tie for matching against Tie's opted-in identity graph.
Tie responds. Matched and enriched attributes come back into your data layer as an
rrid_responseevent, which is also how you confirm the round trip is working.Your tools receive it. Enriched attributes flow on to your email, SMS, and ad platforms.
Tie requires a consent management platform on every site where the tracker runs, and the loader must not execute before the shopper grants consent. See Install the Tie Pixel with your consent banner (CMP).
The four events
Event | Fires when | Payload bucket |
| Product detail page load |
|
| Add to cart |
|
| Checkout started |
|
| Order confirmation page |
|
The rr_ prefix is required and the names are fixed. Tie's loader only detects the rr_* events, so a site pushing an un-prefixed view_item will not be tracked. The prefix is historical: Tie was formerly named Revenue Roll.
The two-push pattern
Every event clears the previous ecommerce object first, then pushes:
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({ event: "rr_<event_name>", ecommerce: { /* ... */ } });
Common fields
Field | Meaning |
| The |
| Unique cart or browsing-session ID (cookie- or backend-generated) |
| Shopper email. Required on |
| ISO 4217 currency, for example |
| Product display name, as shown on the product page |
| Absolute product image URL. Send this on all four events. It is what populates product images in your email and SMS personalization, and it is the field most often left empty on headless and custom storefronts |
| Internal product ID or SKU |
| Variant ID (size, color, option) |
| Shopify global ID |
| Price of the variant (numeric) |
| Brand or vendor |
| Human-readable variant label, for example |
| Quantity. Required on |
| Variant SKU for ERP or fulfillment. Checkout and purchase events |
rr_purchase extra fields
The purchase event also carries buyer identity and order financials, which is what lifts the match rate on orders:
Field | Meaning |
| Buyer billing name |
| Buyer email. Required on this event |
| Buyer phone, digits only (strip parentheses, dashes, and spaces) |
| Your internal customer or user ID |
| Billing address components |
| Order number or transaction ID |
| Gross order total, including tax and shipping |
| Tax amount |
| Shipping cost |
| Promo code used |
| Discount value applied |
Sample: rr_view_item
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: "rr_view_item",
rr_cart_id: cartId,
email_address: customer.email || null, // optional: populate if the shopper is logged in
ecommerce: {
currencyCode: "USD",
detail: {
products: [{
name: "Cold Brew Concentrate 32oz",
id: "PROD-1001",
image: "https://cdn.acme-coffee.com/products/cold-brew-32.png",
variantId: "VAR-200",
price: 18.50,
brand: "Acme Coffee",
variant: "32oz"
}]
}
}
});
Sample: rr_add_to_cart
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: "rr_add_to_cart",
rr_cart_id: cartId,
email_address: customer.email || null, // optional: populate if the shopper is logged in
ecommerce: {
currencyCode: "USD",
add: {
products: [{
name: "Cold Brew Concentrate 32oz",
id: "PROD-1001",
image: "https://cdn.acme-coffee.com/products/cold-brew-32.png",
variantId: "VAR-200",
price: 18.50,
brand: "Acme Coffee",
variant: "32oz",
quantity: 1
}]
}
}
});
Sample: rr_begin_checkout
On headless Shopify you do not build this event. The Customer Events pixel produces it.
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: "rr_begin_checkout",
rr_cart_id: cartId,
email_address: customer.email || null, // optional: populate if known at checkout start
ecommerce: {
currencyCode: "USD",
checkout: {
actionField: { step: 1 },
products: [{
name: "Cold Brew Concentrate 32oz",
id: "PROD-1001",
variantSku: "CB-32-CONC",
image: "https://cdn.acme-coffee.com/products/cold-brew-32.png",
variantId: "VAR-200",
price: 18.50,
brand: "Acme Coffee",
variant: "32oz"
}]
}
}
});
Sample: rr_purchase
On headless Shopify you do not build this event either. The Customer Events pixel produces it. On every other non-Shopify platform this event is your order data, so treat it as the priority.
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: "rr_purchase",
rr_cart_id: cartId,
first_name: customer.firstName,
last_name: customer.lastName,
email_address: customer.email,
phone_number: normalizePhone(customer.phone), // digits only
customer_id: customer.id,
country: billing.countryCode,
region: billing.provinceCode,
city: billing.city,
zip: billing.zip,
ecommerce: {
currencyCode: "USD",
purchase: {
actionField: {
id: String(order.name), // for example "#1042"
affiliation: shop.name,
revenue: order.totalPrice, // decimal
tax: order.taxPrice,
shipping: order.shippingPrice,
coupon: order.discountTitle || "",
discount_amount: order.discountAmount || 0
},
products: [{
name: "Cold Brew Concentrate 32oz",
id: "PROD-1001",
variantSku: "CB-32-CONC",
image: "https://cdn.acme-coffee.com/products/cold-brew-32.png",
variantId: "VAR-200",
price: 18.50,
brand: "Acme Coffee",
variant: "32oz"
}]
}
}
});
Validation checks
Each event you own appears once per user action, and no more.
ecommerceis never null on a fired event. The null push is the reset that precedes it.Every product object carries at least
name,id,image,price,brand, andvariant.currencyCodeandrr_cart_idappear on every ecommerce event.On non-Shopify platforms,
rr_purchasefires on a real completed order. Check this one on its own.
When your implementation passes these, work through the full verification guide, then tell your Tie contact so we can validate end to end with you.
Troubleshooting
What you see | Likely cause | Fix |
No events at all in the console | The loader has not run, or consent has not been granted | Confirm the loader is in the |
Console shows | Namespace mismatch | Add the |
Events appear but | The push fired before the product data was available | Move the push to after your product or order data resolves |
Nothing fires until you reload with DevTools open | Events fired on page load, before you were watching | Open DevTools first, then load the page |
Template placeholders appear as literal text in the payload | Variables are not resolving in your template | Populate the values server-side, or from your own data-layer variables |
Product images are blank in your email or SMS |
| Map and push an absolute image URL on all four events. This is the most common cause, and it is specific to headless and custom storefronts, where the image is rarely in the data layer by default |
Everything fires except | The order confirmation page was never instrumented | Instrument it. On non-Shopify platforms this is your only order data |
Still stuck? Work through Installed but not seeing data yet, or reach out in your shared Slack channel with Tie.
FAQ
I already have a data layer. Why can't Tie just use it? Tie's loader listens for a specific set of event names and a specific structure. If your existing data layer uses different names, for example view_item instead of rr_view_item, Tie will not detect or process those events.
Can I keep my existing data layer? Yes. The Tie data layer runs alongside an existing one without interfering with it, as long as the rr_* events are implemented. Your other tools are unaffected.
Why implement Tie's schema if I already track the same data elsewhere? Your existing data layers are not wired to Tie's enrichment logic. Tie depends on its own schema to know when to resolve an identity and what to send back, so without it the enrichment, attribution, and downstream activation do not happen.
Can Tie set up the data layer for us? Not on the platforms listed above as your developers. The data layer runs inside your site's code, which means changing it requires production access to your storefront, and the events can carry personal data that you are responsible for under your own privacy and consent obligations. Your developers or your agency implement it; Tie then validates the structure and confirms the enrichment flow works with you. On standard Shopify none of this applies, because Tie installs everything.
Do we need this if we're on Shopify? On standard Shopify, no. On headless Shopify, partly: your developers build the two browsing events and the Customer Events pixel handles checkout and purchase. The table at the top of this page is the short answer.