Ecommerce Events in Customer.io (Data-In): how to track the right events so retention flows actually trigger

Customer.io partner logo

Table of Contents

Summarize this documentation using AI

This banner was added using fs-inject

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Overview

If you’re running retention in Customer.io, ecommerce events are the difference between “we have flows” and “flows fire when they should.” If you want a second set of eyes on your tracking plan before you ship it, you can book a strategy call—most issues we see are upstream data decisions, not copy or cadence.

In practice, cart recovery, browse recovery, post-purchase upsells, and winbacks all depend on the same thing: clean event data entering Customer.io with consistent identities and predictable schemas. When that breaks, segmentation gets fuzzy and triggers become unreliable (duplicate sends, missed sends, or people stuck in journeys).

How It Works

Customer.io retention automations generally listen for events (and sometimes attribute changes) to decide who enters a campaign, what they receive, and when they exit. Ecommerce events are just structured “facts” you send in—like product_viewed, added_to_cart, checkout_started, order_completed—paired with a person identity and properties (SKU, price, cart contents, order ID, etc.).

  • Events enter Customer.io via your integration layer (commonly Track API, Pipelines, or a CDP). The key is that every event must be tied to a person profile (or captured anonymously and later merged) so journeys can resolve who to message.
  • Identity resolution determines whether triggers are trustworthy. If the same shopper shows up as multiple people (different emails, different IDs, anonymous vs known), you’ll see double cart emails or missing post-purchase messages. Your goal is one durable identifier (customer ID) plus stable secondary identifiers (email/phone) when available.
  • Event naming + property mapping drive segmentation accuracy. Customer.io doesn’t “guess” that Checkout and checkout_started are the same thing. Pick a naming convention and stick to it. Likewise, decide exactly where key fields live (e.g., order_id, total, currency, items[], sku, quantity), because you’ll use those fields for filters, branching, and personalization.
  • Deduping is essential for ecommerce. Many platforms fire the same purchase event multiple times (client + server, retries, thank-you page reloads). If you don’t dedupe by order_id, your “post-purchase cross-sell” can send twice and your revenue reporting gets noisy.
  • Anonymous activity matters for recovery flows. A large share of carts happen before login. If you capture anonymous events (cookie/device) and later identify the person at email capture/checkout, you need a plan to merge that anonymous history so “cart abandon” doesn’t miss high-intent shoppers.

Real D2C scenario: A shopper views a product, adds two items to cart, then abandons. If your added_to_cart event doesn’t include an items array (or includes inconsistent SKU keys), your cart email can’t render the right products. If the shopper later checks out on mobile with a different identity, Customer.io can’t connect the dots—so your abandon flow still sends after purchase unless your order_completed event reliably resolves to the same person and includes a dedupe-safe order_id.

Step-by-Step Setup

The fastest path is to define the event contract first (names, required properties, identity rules), then implement tracking, then validate in Customer.io with real traffic. Don’t start by building journeys—you’ll just end up debugging data inside campaign logic.

  1. Pick your canonical identifiers.
    • Decide your primary key (usually customer_id from your ecommerce platform).
    • Define when you’ll send email and/or phone (collected at signup, checkout, or SMS opt-in).
    • Write down rules for anonymous users (cookie/device ID) and when you “identify” them.
  2. Define your ecommerce event taxonomy.
    • Minimum viable set for retention: product_viewed, added_to_cart, checkout_started, order_completed.
    • If you do subscriptions: subscription_created, subscription_cancelled, subscription_renewed.
    • Keep names lowercase, consistent, and version-safe (avoid spaces and ad-hoc renames).
  3. Lock a schema for each event (required properties).
    • product_viewed: sku, product_id, name, category, price, currency.
    • added_to_cart: cart_id, items[] (each item: sku, product_id, name, quantity, price), cart_value, currency.
    • checkout_started: cart_id, items[], checkout_value, currency.
    • order_completed: order_id, items[], subtotal, discount, shipping, tax, total, currency, payment_method.
  4. Implement event sending into Customer.io (Data In).
    • Send events server-side where possible (more reliable than browser-only).
    • If you also track client-side, implement dedupe (see next step) to prevent double-fires.
    • Ensure every event includes the person identity (or an anonymous ID that you later merge).
  5. Add deduplication rules for purchase and checkout.
    • Use order_id as the idempotency key for order_completed.
    • Use cart_id for cart/checkout events, and decide whether multiple updates should overwrite or append.
  6. Verify in Customer.io with real examples.
    • Open a test profile and confirm events appear with the expected properties.
    • Confirm that anonymous-to-known merging works (cart events show up on the identified profile after email capture).
    • Validate that a completed order prevents cart recovery from firing (or exits the journey) for the same person.

When Should You Use This Feature

If your retention program depends on behavior-based triggers, ecommerce events are the backbone. The moment you want more than “blast everyone who bought in the last 30 days,” you need consistent event data entering Customer.io.

  • Cart recovery that doesn’t embarrass you. Trigger on added_to_cart, suppress on order_completed, and personalize from items[] so the email matches the cart.
  • Post-purchase repeat purchase orchestration. Use order_completed with SKU/category properties to drive replenishment timing, cross-sell, and “how to use it” education without guessing.
  • Reactivation based on true inactivity. Segment on “no order_completed in 90 days” plus “has viewed products in last 14 days” to separate warm browsers from truly dormant customers.
  • Product discovery and browse recovery. product_viewed events let you retarget interest without relying on fragile page URL parsing.

Operational Considerations

This is where most teams get tripped up: the data might be “in Customer.io,” but it isn’t usable at scale. The goal is to keep segmentation stable and triggers deterministic as your site, catalog, and tracking evolve.

  • Segmentation depends on consistent property types. If price is sometimes a string and sometimes a number, filters and Liquid formatting will behave inconsistently. Lock types early.
  • Event volume and cardinality matter. Sending every micro-event (every quantity change, every variant hover) bloats noise and makes journey logic harder. Track what you’ll actually use to trigger, branch, or personalize.
  • Orchestration breaks when identities drift. If your ESP identity is email-based but your backend is customer-ID-based, you need a clear mapping strategy so “order completed” reliably exits “cart abandon.”
  • Catalog changes can silently break personalization. If you rename sku to variant_sku in a refactor, your cart templates won’t error loudly—they’ll just render blank product blocks. Treat event schemas like APIs: version them and test changes.
  • Time zones and timestamps affect windows. Recovery flows often rely on “within X hours.” Make sure event timestamps represent when the action happened, not when a batch job ran.

Implementation Checklist

Before you build a single journey, get the data contract tight. This checklist keeps your triggers reliable and your segments clean when you scale.

  • Primary identifier defined (e.g., customer_id) and consistently sent with events
  • Rules documented for anonymous tracking + merge into known profiles
  • Canonical event names finalized (no mixed casing, no duplicates)
  • Required properties defined per event (including items[] structure)
  • Deduping implemented for order_completed (idempotency via order_id)
  • Test profiles validated in Customer.io (events, properties, merge behavior)
  • Suppression/exit logic supported by data (purchase events resolve to same person as cart events)
  • Monitoring plan: alerts for missing properties, sudden drops in key events, schema drift

Expert Implementation Tips

In most retention programs, the “winning” setup is boring: fewer events, stronger identity, and a schema that doesn’t change every sprint. Here are the moves that keep performance steady.

  • Favor server-side for revenue-critical events. Track order_completed from your backend/order system so ad blockers and thank-you page reloads don’t distort purchase truth.
  • Send both IDs and human identifiers when you have them. A stable customer_id plus email/phone makes downstream reconciliation easier (support, data warehouse, CDP joins).
  • Include category/collection fields on purchase line items. It unlocks retention segmentation like “bought skincare but not moisturizer” without needing a separate product catalog sync.
  • Model cart as an evolving object, not just a moment. If carts update frequently, decide whether you send an “updated cart” event or send added_to_cart per line item. Choose the one that makes your recovery email accurate at send time.
  • Build a small QA dashboard segment. Create internal segments like “Placed order in last 1 day” and “Started checkout but no order in 4 hours” to sanity-check counts against Shopify/your ecommerce platform.

Common Mistakes to Avoid

Most failures aren’t dramatic—they’re small data inconsistencies that compound until your journeys stop behaving. These are the ones we see repeatedly.

  • Tracking cart events anonymously but never merging. Result: your highest-intent shoppers never enter recovery flows because the email address lives on a different profile.
  • No dedupe on purchases. Result: duplicate post-purchase sends, inflated conversion reporting, and customers getting “thanks for your order” twice.
  • Inconsistent item schemas. Result: cart templates break for certain products/variants, usually right after a catalog update.
  • Using email as the only identifier. Result: missed exits/suppressions when shoppers check out with a different email, use Shop Pay, or switch devices.
  • Event name drift across teams. Result: segmentation splits across Order Completed, order_completed, purchase, and nobody trusts the numbers.
  • Over-instrumentation. Result: noisy segments, slow debugging, and journeys that are impossible to reason about when performance dips.

Summary

If you want cart recovery, repeat purchase, and winback flows to fire reliably, treat ecommerce events like a product API: consistent identities, stable names, and strict schemas.

Get the data right first, then build orchestration on top—otherwise you’ll spend your time patching journeys to compensate for broken inputs.

Implement Ecommerce with Propel

If you’re wiring ecommerce events into Customer.io, the highest-leverage work is usually identity + schema design, then validating triggers against real shopper behavior (anonymous cart → known checkout → purchase). If you want an operator to pressure-test your tracking plan and mapping before it hits production, you can book a strategy call and we’ll walk through the event contract, dedupe strategy, and the segments your retention program will rely on.

That upfront rigor is what keeps your recovery and post-purchase flows stable as your site, offers, and catalog evolve.

Contact us

Get in touch

Our friendly team is always here to chat.

Here’s what we’ll dig into:

Where your lifecycle flows are underperforming and the revenue you’re missing

How AI-driven personalisation can move the needle on retention and LTV

Quick wins your team can action this quarter

Whether Propel AI is the right fit for your brand, stage, and stack