Skip to main content

Journeys & automations

Why this matters for your business

The single biggest source of leaked revenue in a Shopify store is timing. The customer abandoned the cart 35 minutes ago and hasn't heard from you yet. The first-time buyer received their order yesterday and is wondering whether to re-buy. The loyal customer hasn't visited in three weeks — the win-back window is open today, closed in another two.

You can't manage that timing manually across thousands of customers. Trying to, with email blasts and spreadsheets, is how merchants either over-send (and burn their list) or under-send (and leak revenue). Journeys solve this by setting up the right follow-up once per scenario — and from then on, every qualifying customer gets the right message at exactly the right moment, on the right channel, automatically.

The compounding effect is real. A cart-recovery journey that recovers 2% of its triggered carts is meaningful at 100 carts/day: that's 2 sales/day, $73K/year at $100 AOV, with zero ongoing work. A platform with 7 journeys running well at the same time turns a one-employee marketing function into the equivalent of a five-person team.

What this typically unlocks

OutcomeTypical result
Cart recovery rate8–22% depending on tier and channel mix
Repeat-purchase within 60d (post-purchase journey)+34% vs. no journey
Win-back conversion3–7% at the right window (vs. 0.4% on blanket re-engagement)
Hours/week on manual nurture~10 hours saved — once configured, journeys run themselves
Time from abandonment → first follow-up~30 minutes vs. days/never without journeys
Customers reached at "the right moment"~85% vs. ~10% with manual scheduling

What you actually get

A journey is a multi-step, event-triggered sequence that runs per customer, forever in response to a defined trigger. Where campaigns are one-shots ("send to this list on Friday"), journeys are continuous: every customer who matches the trigger enters and progresses through the steps until they exit (purchased, opted out, completed, or hit fatigue).

The seven built-in journeys (free out of the box)

Every shop gets these pre-wired on install. All seven are fully editable — change the timing, swap the message, add or remove steps:

JourneyTriggered whenGoalPlan
Welcome seriesNew customer signs upConvert first visit → first orderStarter+
Cart recoverycart_abandoned eventRecover the abandoned orderStarter+
Post-purchase upsellorders/createDrive 2nd order in 60 daysStarter+
Review requestOrder fulfilled + 14dCapture review for product pageStarter+
Win-backLifecycle → at_risk (30d silent)Re-engage before they churnStarter+
VIP upgradeFirst time hitting loyalCement the loyal relationshipPro+
Churn preventionAt-risk for 14d, no contact yetLast attempt before lostPro+

That's about 80% of the value most stores need. Custom journeys (Pro+) let you build any trigger + step combination — restock notifications, replenishment reminders, B2B re-orders, etc.

How it powers every part of your store

Where journeys earn their keepMechanism
Recover cartsMulti-step nudge with channel fallback (WhatsApp → email)
Welcome + activateFirst-purchase incentive when fresh-baked
Drive repeat purchases2nd-order push in the post-purchase peak window
Get reviews on autopilot14-day-after-fulfilment ask, perfect timing
Win back lapsed customersWin-back at the moment they cross 30d silent — not 60d (too late)
Reward loyaltyVIP upgrade flow nurtures the highest-LTV cohort
Replenish consumablesReplenishment reminder fired by predictedReplenishProductId (Customer 360)
Restock alertsTrigger on inventory back-in-stock + customer-viewed-this-SKU
B2B re-ordersCustom trigger on order anniversary for known repeat SKUs
Subscription engagementPre-renewal nudge + post-skip recovery

How it works (without the technical bits)

A journey is event-triggered, step-by-step, and per-customer. Each customer has their own progression:

Step types — the building blocks

You don't write code. You compose journeys from these:

StepWhat it does
WaitPause for a duration (30 min, 24h, 7d). Customer-time-zone aware.
SendSend a message on a chosen channel. Auto-respects opt-in + fatigue + quiet hours.
ConditionBranch based on the customer profile or recent behaviour.
Channel splitTry WhatsApp first; if blocked, fall back to email.
TagAdd or remove a tag from the customer's profile (cross-flow signaling).
WebhookNotify an external system (Zapier, custom integration).
ExitTerminal — the journey is done for this customer.

Conditions in plain English

What you can predicate on at any branch:

  • Profile attributes — lifecycle stage, country, total spend, tags, AOV, custom fields
  • Recent behaviour — opened the previous step, clicked, did they buy, did they visit the site
  • Segment membership — "member of loyal-uk segment"
  • Time — "is it currently between 9 AM and 5 PM in their time zone?"
  • Cohort — "first ordered before March 2026"

Combine with AND / OR / NOT, nested up to 4 levels deep.

The flagship example: cart recovery

This is the journey every store needs and the easiest to understand. Walking through it once teaches every concept.

The decisions baked in:

  • 30-minute wait first — gives customers time to come back on their own (about 40% do)
  • Email before WhatsApp — email costs less in fatigue budget
  • Skip the WhatsApp step if email was opened — opener signals "I'll get to it"; nagging via WA is what drives unsubs
  • 48h gap before the second WA push — respects customer patience
  • 15% off only at last-chance — protects margin for the ~half who'd have recovered with 10%

You can edit any of those decisions in the visual builder.

Re-enrolment — what happens when triggers fire again

By default, a customer can only be in one instance of a journey at a time. If they trigger cart_abandoned while already mid- recovery on a previous cart, the system either:

  • Restarts the journey (default) — a new cart is a new attempt
  • Updates the existing enrolment (configurable) — useful for journeys where state matters more than the latest event
  • Skips (configurable) — prevents stacking discounts on repeat triggers (good for high-value items)

You set this once per journey when authoring.

Channel splits + fallback

The "WhatsApp → email fallback" pattern handles WhatsApp's 24-hour conversation window:

Step N: Send
primary: WhatsApp (template ok)
fallback: email

Triggers fallback when WA is blocked because:
- 24h customer-initiated window has closed and no template available
- Customer opted out of WA
- WA fatigue cap reached for the day
- Provider unavailable after 3 retries

The same pattern works for SMS → email, email → SMS, etc. The fallback channel respects its own consent + fatigue gates.

How journeys end (exit reasons)

Exit reasonWhat it means
completedReached the terminal step naturally
purchasedGoal achieved (cart recovered, repeat order, etc.)
cancelledManually cancelled (admin action)
pausedPaused (worker downtime, admin pause)
exited_fatigueCouldn't send next step because fatigue was capped — journey gives up rather than queue forever
exited_unsubscribedCustomer globally unsubscribed

The exit-reason mix is the most important journey health metric. A journey mostly exiting via fatigue is over-sending. One mostly exiting via unsubscribed is alienating customers. One mostly hitting purchased is doing exactly what it's for.

Real merchant scenarios

Scenario A — First-time merchant turns on cart recovery

Setup. New merchant, 800 customers, ~12 carts abandoned per day. Free tier — uses the built-in cart-recovery journey (one- click activation).

Decision. Accept the defaults. Don't over-engineer.

60-day result.

MetricOutcome
Carts abandoned in window720
Recovered91 (12.6%)
Recovered revenue$9,170
Avg order value of recovered orders$101
WhatsApp step contribution38% of recoveries
Customers exited via opt-out4
Customers exited via fatigue2
Time merchant spent~10 minutes (one click + creative review)

The merchant's instinct was to manually email each abandoner — this would have meant 720 hand-typed emails over 60 days, ~24 hours of work. The journey did it in 10 minutes of setup.

Scenario B — Mid-market brand with 7 journeys running

Setup. 50K customers, Pro tier. Welcome, cart recovery, post-purchase upsell, review request, win-back, VIP upgrade, churn prevention — all built-in journeys, customized.

The hard part wasn't building, it was tuning. Initial run showed:

JourneyIssueFix
Cart recovery22% exit_fatigueReduced from 4 steps to 3
Welcome series8% unsubscribeRemoved the third "have you read our story?" email
Win-back0.3% conversionWindow was 60d; moved to 30d
VIP upgradeConflicted with welcomeAdded priority — VIP wins if both eligible
Post-purchaseOKNo change
Review request18% review-rate (great)No change
Churn prevention0.1% conversionMoved to "exclude if WhatsApp not opted-in"

90 days post-tuning.

MetricResult
Total journey-attributed revenue$487K
% of total revenue from journeys27%
Hours/week spent maintaining2 (review dashboards + tweaks)
Customers in active enrolments at any time~6,500

Scenario C — Replenishment for a coffee subscription brand

Setup. Sells coffee, average customer reorders every 28 days (consumable). Custom journey on Pro tier.

Trigger. Customer360.predictedReplenishProductId is set AND lastOrderAt is at least predictedReplenishIntervalDays - 5 days ago.

Steps.

  1. Wait until D-5 (5 days before predicted re-order)
  2. Email: "Running low on [their previous coffee]?" with the reorder link
  3. Wait 3d
  4. If no order → SMS reminder
  5. Wait 7d (give them a week)
  6. If no order → email "Switch up your blend?" with 3 alternatives
  7. Exit

60-day result.

MetricOutcome
Customers who hit step 11,840
Reordered after step 2 (email)612 (33%)
Reordered after step 4 (SMS)additional 280 (15% of remaining)
Reordered after step 6 (alt email)additional 124 (8% of remaining)
Total replenishment revenue$108K
Vs. control (no journey)+$71K incremental (estimated via 10% holdout)

The brand's CEO described this as "the journey that pays the team's salaries" — set up once, runs forever.

Scenario D — Win-back tuning at an apparel brand

Setup. Apparel brand, ~80K customers, lots of seasonal one- time buyers. Default win-back journey was firing at 30 days silent — wrong window for a brand where customers naturally have 6–12 month gaps.

The hypothesis. Most "at-risk" customers in apparel aren't at risk; they're seasonal. Win-back should fire at 90 days, not 30. Confirmed by looking at re-purchase rates by silence window.

The change. Moved win-back trigger to lifecycleStage = 'at_risk' AND lastActivityAt > 90 days. Customized the lifecycle threshold per shop (Pro+ feature).

Before/after.

MetricBefore (30d window)After (90d window)
Win-back triggered~600/week~120/week
Conversion rate0.6%4.2%
Recovered revenue/week$720$1,940
Customer "fatigue from win-back" complaints9/quarter1/quarter

Sending less to better-timed customers drove more revenue and fewer complaints. The whole point.

Scenario E — Subscription brand catching pre-cancel signal

Setup. Subscription box, 12K active subs, churn ~6%/month.

The journey. Custom trigger: subscription nextBillingAt is within 5 days AND customer's last engagement (email open, in-app visit) is more than 21 days ago.

Steps.

  1. Send "Skip or pause this month?" email (puts the choice in their hands proactively)
  2. Wait 2d
  3. If no action → "Quick survey — what do you want next?"
  4. Wait 3d
  5. If no order placed in window → exit (they were going to skip anyway, don't fight)

The counterintuitive bit. The journey doesn't try to prevent skipping. It tries to capture pre-cancel signal — customers who proactively skip stay subscribed; customers who fade silently churn entirely.

90-day result.

MetricOutcome
Customers in journey1,640
Proactively skipped (vs. churned)240
Of those 240, retained 90d later198 (83%)
LTV preserved~$78K

The skip option is the retention play. The data made it possible to find the customers who needed it.

Scenario F — Built-in welcome series gone wrong, then right

Setup. Beauty brand, 3K customers. Default welcome series was 4 emails over 14 days. Unsubscribe rate at 9%.

The diagnosis. Email 3 ("Read our founder's story") had 38% unsubscribe rate on its own. Customers in the discovery phase don't want a 1,200-word brand story; they want product info.

The fix. Replaced step 3 with a "shop by skin type" guide and shortened the series to 3 emails over 7 days.

Before/after on ~700 new customers tested:

MetricBeforeAfter
Series completion rate41%78%
Unsubscribes63 (9%)14 (2%)
First-purchase rate within 14d11%22%

The lesson: built-in journeys are defaults, not gospel. Watch the exit-reason mix; tune the steps where customers leave for the wrong reason.

Best practices

Watch the exit-reason mix. A healthy journey has most exits as purchased or completed. If fatigue or unsubscribed dominate, you're over-sending.

Set realistic re-enrolment rules. Cart recovery should restart on each new cart. VIP upgrade should fire once per customer ever. Win-back should respect a 90-day cool-down.

Use channel fallback for time-sensitive journeys. Cart recovery's effectiveness drops 30-fold by hour 6. WhatsApp → email fallback ensures the message goes out even if WA window is closed.

Run the dry-run simulator before launching. Pick a real test customer; the simulator walks them through every step without sending real messages. Catches every config error before customers see the broken flow.

Tune lifecycle thresholds per shop. Default at_risk = 30 days. Apparel might need 90 days; flash-sale stores might need 7 days. Pro+ feature.

Don't disable fatigue in a journey "to make sure it sends". The fatigue cap is what's preserving your sender reputation. Override once → it becomes the pattern → unsub creep is your fault.

Don't build journeys that trigger themselves. The cycle detector will catch most of these and disable the journey, but the symptom (every customer in the journey forever) is hard to diagnose if it slips through.

Don't run more than 7 journeys simultaneously without audit. Customers in many journeys at once hit fatigue caps constantly — they get nothing useful from any of them.

Don't put discount stacking in journeys. A customer who hits cart-recovery then post-purchase shouldn't see two 15%-off codes. Use journey-level coupon caps or set re-enrol = skip.

Plan tiers

CapabilityFreeStarterProAgencyEnterprise
Built-in journeys (7)view-onlyeditediteditedit
Custom journeys
Max custom journeys550unlimitedunlimited
Max steps per journeyn/a825unlimitedunlimited
Channel fallback
Custom lifecycle thresholds
Dry-run simulator
Re-enrolment policiesdefaultconfigurableconfigurableconfigurableconfigurable
Webhook step
Approval gate per journey
Multi-store journey templates
Cycle / loop detector

Frequently asked

Customers seem stuck at step 3 — is something broken? Step 3 is probably a wait step. Wait progress doesn't show in real time; check the enrolment detail to see exactly when the next step fires.

Can I pause a journey for everyone at once? Yes — pause from the journey detail page. All active enrolments freeze; no new ones are created. Resume picks up where it left off (no skipped steps).

A customer is in two journeys — which wins? Both run in parallel. Each respects its own send-time gauntlet, and the global fatigue cap is shared — so a customer in two journeys may receive fewer messages from each than if they were in just one.

Can I A/B test journey variants? Yes (Pro+) — create two versions of the journey, route via a random condition step (50/50), compare exit reasons + revenue. Or use formal Experiments & holdouts.

What happens to in-flight enrolments when I edit a journey? By default, in-flight enrolments stay on the old version until they exit; new enrolments use the new version. You can also force all in-flight to migrate (rare — used during major restructures).

Can a journey trigger another journey? Yes via the tag step or a webhook step + custom rule. The cycle detector still applies — A→B→A loops are caught.

How does fatigue interact with journeys? Each Send step passes through the same gauntlet as a campaign. A customer at fatigue cap doesn't receive that step; the journey either pauses (default) or exits with exited_fatigue depending on the step's policy.

Can I export which customers are in which journeys? Yes — the v1 admin API exposes /api/v1/journeys/:id/enrollments returning the full membership list with current step + exit reason if applicable.

See also