Summarize this documentation using AI
Overview
Deep links are one of those retention mechanics that look simple until you try to measure them. If you’re using Customer.io for push/SMS/email and you care about repeat purchase and cart recovery, deep links are the difference between “we sent messages” and “we drove revenue.” If you want help pressure-testing your tracking and routing before you scale, book a strategy call.
In most retention programs, deep links fail quietly: the link opens the app, but drops the customer on the home screen, the SDK never records the open, and your campaign looks like it underperformed. The fix isn’t copy—it’s app-side identity + event tracking + deterministic routing.
How It Works
At a practical level, deep links connect three systems: the message click from Customer.io, your app’s router (iOS/Android), and your Customer.io SDK event stream. When those three agree on identity and context, you can run high-intent flows (cart recovery, replenishment, winback) without losing people between tap and checkout.
- Customer.io generates the link in the message. For SMS and WhatsApp, you’ll typically rely on short links (often branded). For email, you may use standard links with tracking parameters.
- The OS resolves the deep link. Universal Links (iOS) / App Links (Android) open your app directly when installed; otherwise they fall back to web.
- Your app parses the deep link payload. This is where you decide: PDP vs cart vs checkout vs collection page vs “resume subscription.”
- Your Customer.io SDK records what happened. You want to capture at least: message click/open attribution (where possible), a deep-link-open event, and downstream revenue events (add_to_cart, checkout_started, order_completed).
- Identity stitching ties the session to a person. If the user is logged in, you should call
identify()before logging conversion events; if they’re anonymous, capture the deep-link-open and stitch later when they authenticate.
Real D2C scenario: A customer taps a “Finish your checkout” push notification. If your deep link routes to the cart but the user isn’t identified yet, you’ll see a click but miss the purchase. The operational fix is to (1) deep-link into a cart screen that can render for anonymous users, (2) fire deep_link_opened immediately, and (3) when the user logs in, call identify() and continue firing the commerce events so Customer.io can attribute the conversion to the same person.
Step-by-Step Setup
Before you touch Customer.io, get the app-side plumbing correct. Deep links are only “done” when the app routes reliably and the SDK events show up with the right person attached.
- Pick your deep link strategy (and be consistent).
- Prefer Universal Links/App Links for reliability and security; use custom schemes as a fallback.
- Decide your canonical format, e.g.
https://app.yourbrand.com/pdp?sku=...orhttps://app.yourbrand.com/cart.
- Implement OS-level routing.
- iOS: Associated Domains + apple-app-site-association file; ensure the app can handle the path and query params.
- Android: Digital Asset Links + intent filters; verify links open the app from Gmail/Chrome.
- Install and initialize the Customer.io SDK.
- Initialize as early as possible in app startup so you can record the deep-link-open even on cold start.
- Confirm your environment (prod vs staging) so test events don’t pollute revenue reporting.
- Implement identity correctly with
identify().- Call
identify(customerId)immediately after login/registration. - If you support logout, call the SDK’s reset/clear identity method (platform-specific) to avoid cross-user contamination on shared devices.
- Pass stable identifiers (your internal customer ID). Email/phone can be attributes, but don’t rely on them as the primary ID if they can change.
- Call
- Track a dedicated deep link event on open.
- Fire an event like
deep_link_openedwith properties:source_channel(push/sms/email),campaign_id(if you pass it),path,sku,collection,cart_id, etc. - Fire it even if the user is anonymous; stitch later after
identify().
- Fire an event like
- Instrument the revenue path with consistent event names.
- Minimum set for retention:
product_viewed,add_to_cart,checkout_started,order_completed. - Include order metadata on purchase:
order_id,value,currency,items(SKU/qty), and any subscription flags.
- Minimum set for retention:
- Build the link in Customer.io messages and keep parameters predictable.
- Use the same deep link format across push/SMS/email so your app router doesn’t need channel-specific hacks.
- If you add tracking params, keep them stable (e.g.
utm_source,utm_campaign,cio_delivery_idif you pass it) and make sure your app ignores unknown params safely.
- QA like an operator: cold start, logged out, and delayed login.
- Test: app installed vs not installed, user logged in vs logged out, and network slow.
- Confirm the event order: deep link open → (optional) login/identify → commerce events → purchase.
When Should You Use This Feature
Deep links are worth the effort when you’re sending messages that assume intent. If the customer has to “find the thing” after tapping, you’ll bleed conversion and misread performance.
- Cart recovery (mobile-heavy brands): Route to cart with items preloaded, not the homepage. Pair with a
checkout_startedguardrail to avoid spamming people who already resumed. - Replenishment / repeat purchase: Deep link directly to the last purchased SKU PDP with “Buy again” or subscription options.
- Back-in-stock or price drop: Route to the variant-specific PDP (size/color) so the customer doesn’t land on an out-of-stock default.
- Winback to a curated collection: For lapsed buyers, deep link into a “Best sellers for you” collection built from category affinity—then track
collection_viewed→product_viewed.
Operational Considerations
Deep links don’t live in isolation—they change how you segment, how you suppress, and how you trust your reporting. The biggest wins come from treating deep-link events as first-class signals in Customer.io.
- Segmentation:
- Create segments that key off
deep_link_openedbut didn’t convert (e.g. opened cart link in last 24h AND noorder_completed). - Split by platform (iOS/Android) because deep link reliability and permission rates differ; your follow-up timing should differ too.
- Create segments that key off
- Data flow and identity stitching:
- In practice, this tends to break when anonymous sessions aren’t merged after login. Make sure your SDK supports anonymous tracking and that your login flow calls
identify()consistently (including social login and magic links). - Deduplicate purchase events using
order_id. If your app retries on flaky connections, you’ll inflate revenue unless you enforce idempotency.
- In practice, this tends to break when anonymous sessions aren’t merged after login. Make sure your SDK supports anonymous tracking and that your login flow calls
- Orchestration realities:
- Use deep-link-open as a suppression signal. If someone tapped the cart link, don’t keep sending “you left items” messages every 2 hours—switch to a different angle or stop.
- Pass the same deep link into push + SMS fallback so your downstream events stay comparable across channels.
Implementation Checklist
If you want this to hold up when you scale sends, you need to validate routing, attribution, and identity under real user conditions—not just in a simulator.
- Universal Links (iOS) and App Links (Android) configured and verified on real devices
- Customer.io SDK initialized early enough to capture cold-start deep link opens
identify()called on every login path; identity cleared on logoutdeep_link_openedevent fires with useful properties (path, sku, cart_id, channel)- Commerce events standardized and firing consistently (
add_to_cart,checkout_started,order_completed) - Purchase events deduped by
order_id - Customer.io campaigns use deep links that match the app router’s canonical format
- QA covers: installed/not installed, logged out/logged in, slow network, delayed login
Expert Implementation Tips
The difference between “deep links work” and “deep links drive incremental revenue” is usually a handful of small decisions that prevent silent leakage.
- Track the landing screen, not just the open. Fire something like
screen_viewedwithscreen_nameafter routing. It helps you catch cases where the deep link opens but routes incorrectly. - Make links resilient to missing context. If the cart ID is invalid, route to cart anyway and show a recovery state. Don’t dead-end.
- Use deep link properties to personalize the next message. If someone opened a PDP deep link for SKU A but didn’t add to cart, follow up with reviews/UGC for SKU A—not a generic “still thinking?”
- Build channel parity into the app router. Don’t create “push-only” paths. In real programs, SMS becomes the fallback when push permissions drop.
Common Mistakes to Avoid
Most teams don’t fail because they didn’t add a link—they fail because the app and the data model weren’t built for attribution and orchestration.
- Opening the app but dumping users on the homepage. This kills cart recovery performance and makes “clicks” meaningless.
- Firing events before identity is set. If
order_completedfires on an anonymous profile and you never stitch it, you’ll undercount revenue and over-message. - Inconsistent event naming across platforms. iOS sends
CheckoutStarted, Android sendscheckout_started—now your segments and suppression rules miss half the audience. - No dedupe on purchase. Retry logic can double-fire purchases; your “repeat purchase” automations will trigger incorrectly.
- Not testing cold starts. Deep links often fail only when the app is fully closed—exactly the state most customers are in when they tap a message.
Summary
If your retention program depends on high-intent taps (cart, replenishment, winback), deep links are infrastructure—not a nice-to-have. Get routing right, fire a deep-link-open event, and stitch identity cleanly so Customer.io can suppress and personalize based on real behavior. If you can’t trust the click-to-purchase chain, you’ll misread performance and over-send.
Implement Deep Links with Propel
When we help teams implement deep links, the work usually isn’t the URL—it’s making sure the SDK event stream and identity stitching hold up under real-world conditions (logged out, cold start, flaky network) so your Customer.io segments and suppression rules behave. If you’re building or fixing this inside Customer.io and want a second set of operator eyes, book a strategy call.