Summarize this documentation using AI
Overview
If your cart recovery or repeat purchase journeys feel “random,” the root cause is usually tracking—missing identify calls, events firing with the wrong IDs, or anonymous activity that never gets stitched. This guide translates Customer.io troubleshooting into the checks that actually matter when you’re running retention programs; if you want a second set of eyes on your data flow, you can book a strategy call.
In most retention programs, we’ve seen teams spend weeks tweaking creative when the real issue is that “Added to Cart” is firing from the app, but the user is still anonymous—so the abandoned cart campaign never sees them.
How It Works
Customer.io’s SDKs sit in your app (mobile or web) and send two core things: identity updates (who the user is) and behavioral events (what they did). Your automations only work when those two streams line up consistently.
- Installation and environment: The SDK has to initialize cleanly in the right environment (dev vs prod). If you’re testing in a staging build that points to prod keys (or vice versa), you’ll chase ghosts.
- Identity stitching: Most apps start users as anonymous (device-based) and later convert them to known users (email/user_id). The SDK needs a clear moment where you call
identifyso anonymous events get associated to the right profile. - Event delivery: Events like
product_viewed,added_to_cart,checkout_started, andorder_completedmust fire with the same identifier strategy as youridentifycall. If events use one ID and profiles use another, journeys won’t trigger or will trigger on the wrong person. - Downstream usage: Segments and Journeys evaluate events/attributes as they arrive. If your event arrives late, arrives without required properties, or lands on a duplicate profile, your orchestration logic breaks even though “tracking is working.”
Step-by-Step Setup
When troubleshooting SDK issues, you want to narrow the problem to one of three buckets: SDK initialization, identity, or events. Run these steps in order—skipping ahead usually wastes time.
- Confirm you’re looking at the right workspace + environment.
Double-check API keys, site ID, region, and whether your app build is pointing to dev/staging vs production. A surprising amount of “events not showing up” is just the wrong destination. - Verify the SDK initializes without errors.
Turn on verbose logging in your dev build and confirm the SDK starts on app launch/page load. If initialization is flaky (race conditions, blocked network, consent gating), you’ll see inconsistent event counts. - Pick one test user and make identity deterministic.
Use a known identifier (e.g.,user_id=12345andemail=test+cio@brand.com). In your app:- Start logged out → generate anonymous activity.
- Log in → call
identifyonce with stable identifiers. - Do not call
identifyrepeatedly with different IDs (that’s how duplicates happen).
- Validate the anonymous-to-known stitch.
Reproduce a realistic flow: view product → add to cart while anonymous → log in → continue browsing. Then confirm those pre-login events appear on the same final profile afteridentify. - Audit your event names and required properties.
For retention, consistency matters more than volume. Confirm:- Event names match what Journeys/segments expect (case-sensitive).
- Cart events include stable keys like
cart_id,sku,price,quantity,currency. - Order events include
order_idandtotalso you can dedupe and drive post-purchase timing.
- Check for duplicate profiles.
If you see “some users get two abandoned cart emails” or “reactivation hits recent buyers,” you likely have split identities (email profile + user_id profile). Confirm your app always identifies users the same way across sessions and devices. - Confirm real-time journey entry.
Trigger a single event that should start a journey (e.g.,added_to_cart) and verify the user enters within the expected delay window. If entry is delayed, you’re usually dealing with queued events, offline delivery, or time window constraints.
When Should You Use This Feature
SDK troubleshooting isn’t a one-time “implementation task.” It’s the work you do whenever retention performance looks off—especially after app releases, analytics refactors, or changes to login/checkout.
- Cart recovery is underperforming in-app vs web. Common scenario: iOS fires
added_to_cartanonymously and never stitches after login, so the cart journey only catches web shoppers. - Repeat purchase timing feels wrong. If
order_completedfires twice (or not at all), your replenishment and cross-sell sequences will spam or miss buyers. - Reactivation audiences look polluted. If identity is fragmented, “90-day lapsed” segments accidentally include active customers on a different profile ID.
- You’re launching push/in-app and need device accuracy. SDK/device registration issues show up as “push delivered” but nobody clicks—because you’re targeting stale or missing device tokens.
Operational Considerations
Once tracking is “working,” the operator reality is keeping it reliable as the product evolves. The best retention teams treat identity and event contracts like production infrastructure, not analytics garnish.
- Segmentation depends on event hygiene. Define a small set of canonical retention events and lock their schemas. If PMs rename events casually, your segments quietly degrade.
- Data flow latency changes orchestration outcomes. Mobile SDKs often queue events offline and flush later. That can cause a user to receive an abandoned cart push after they already purchased—unless you add purchase-based exit conditions and short “grace” delays.
- Identity strategy must be consistent across platforms. If web uses email as primary and mobile uses user_id (or vice versa), you’ll create duplicates and split attribution. Pick a primary identifier and enforce it everywhere.
- Consent gating can create blind spots. If you only initialize the SDK after marketing consent, you’ll miss early funnel behavior (product views, cart builds). In practice, we’ve seen teams initialize for strictly necessary events and gate promotional messaging separately.
- Dedupe is not optional for revenue events. Purchases and refunds should be idempotent (same
order_iddoesn’t create multiple conversions). Without dedupe, LTV reporting and post-purchase suppression break.
Implementation Checklist
Use this as the quick “are we safe to scale spend?” checklist before you ramp acquisition or roll out new journeys.
- SDK initializes reliably on app launch/page load (no silent failures)
- Clear
identifymoment (login/account creation) with stable identifiers - Anonymous events stitch to known profile after
identify - Canonical retention events implemented:
product_viewed,added_to_cart,checkout_started,order_completed - Event properties include IDs needed for dedupe and personalization (cart_id, sku, order_id)
- No duplicate profiles for the same customer across email/user_id/device
- Journeys have purchase-based exit conditions to prevent “late” messages
- Device tokens register correctly (push audience size matches logged-in base)
Expert Implementation Tips
These are the small decisions that prevent months of messy cleanup later—especially once you’re running multi-channel retention (email + SMS + push + in-app).
- Make identity explicit, not implied. Call
identifyimmediately after authentication succeeds, not after the user lands on a home screen. That reduces the window where high-intent events get logged anonymously. - Version your event contract. If you need to change event shape (e.g., cart items array), ship
added_to_cart_v2in parallel and migrate journeys deliberately. Silent schema changes are how personalization breaks mid-campaign. - Add a “source of truth” purchase event. If your app can emit both client-side and server-side purchase events, pick one as canonical for suppression and LTV logic. Double-firing is extremely common during payment retries.
- Use a short delay before cart messaging on mobile. A 15–30 minute delay plus an “exit if purchased” check catches offline flushes and reduces false positives.
- Test with a real device on a flaky network. If your tracking only works on office Wi-Fi, it’s not ready. Offline queuing is where identity stitching issues show up.
Common Mistakes to Avoid
Most “Customer.io isn’t working” tickets boil down to a handful of predictable implementation mistakes.
- Calling
identifywith different identifiers over time. Example: first session uses email, next session uses internal user_id—now you have two people records and split event history. - Tracking cart events without a stable cart key. Without
cart_id(or equivalent), you can’t dedupe, you can’t reliably suppress, and your cart personalization becomes guesswork. - Relying on “Last Visited” style fields for retention logic. Those fields can lag or be overwritten; event-based logic is usually more reliable for cart and browse recovery.
- Testing in production with your own real customer profile. You’ll contaminate segments and can trigger live messages. Use explicit test identifiers and suppress them from sends.
- Ignoring duplicates until deliverability suffers. Duplicate profiles inflate audience size, increase message volume, and can create “spammy” behavior that hurts inbox placement over time.
Summary
If journeys aren’t triggering, start with identity: consistent identify, clean stitching, and no duplicates. Then validate event names/properties and latency so suppression and timing work in the real world. Once those are solid, your retention optimizations actually compound instead of fighting broken plumbing.
Implement Troubleshooting with Propel
If you’re seeing inconsistent cart recovery entry, duplicate profiles, or “late” mobile events, it’s usually faster to map the full identity + event flow end-to-end than to debug one journey at a time. We’ll pressure-test your Customer.io SDK tracking against real retention use cases (cart, post-purchase, winback) and tighten the stitching so segments and suppression behave; if helpful, book a strategy call.
In practice, this tends to break right after app releases or login changes—so we’ll also leave you with a lightweight QA checklist your team can run before every deploy.