The Anniversary “We Miss You” Playbook for Shopify Apps
This playbook walks Shopify and ecommerce app teams through building an anniversary “we miss you” journey in Spreeflo that targets soft-churn customers, reactivates inactive stores, and quietly saves recurring revenue with one well-timed, contextual email.
Industry
Niche
Pattern
Loading sequence...
A year after launch, Elena from CartWizard thought the hard part was behind her.
Her Shopify cart-recovery app was sitting at ~$100k MRR. Installs looked healthy. Churn was “fine”. But every month, Stripe told a different story: a slow bleed of stores cancelling after nine to twelve months, usually after going quiet for a while.
When she pulled usage data, the pattern was obvious. Stores didn’t wake up one morning and churn. They went inactive first: campaigns paused, dashboards untouched, “app_installed” still true but “feature_used” flat. Weeks of silence, then uninstall.
What hurt most was that she barely spoke to any of them on the way out. A generic monthly newsletter to “all users” wasn’t cutting it.
So she built a simple anniversary touch: one thoughtful, highly contextual email that lands on the “customer-since” birthday of inactive stores and asks, bluntly, “It’s been a year — what changed?”
That message alone started recovering thousands of dollars of MRR each quarter.
The sequence at the top of this page is the whole journey, end to end. Let’s walk through how to build it in Spreeflo, why each node is there, and how to adapt it for your own Shopify or e‑commerce app.
Why milestones beat generic “we miss you” blasts
For e‑commerce apps, there are two types of churn:
Hard churn: uninstall or payment failure.
Soft churn: they’re still paying (or installed) but not using you.
Most teams only react to the first, even though the second gives you weeks of early warning.
The problem is that most “re‑engagement” attempts feel like bulk marketing: a template sent to everyone who hasn’t logged in for 60 days. It reads like a campaign, not a conversation.
Milestones change that. “You installed us a year ago today.” “You’ve recovered 4,312 carts with us.” “You launched your first campaign six months ago.” These are specific, human moments that:
Prove you’re paying attention to their store.
Give you a natural excuse to ask what changed.
Make a single email feel like a check‑in, not a promotion.
Spreeflo is built for this kind of thing: data‑driven, personal touches that run on autopilot. You wire the behaviour once in the campaigns and journeys builder, then let it quietly watch for the right moment.
Here’s how the anniversary “we miss you” journey works.
Step 1: The Cyclic trigger that knows who’s “one year in and quiet”
This is a journey, not a one‑off campaign. It runs every day and scoops up any contact who matches your anniversary + inactivity rules.
In Spreeflo, that starts with a Cyclic trigger node.
Configuration:
Repeat interval: every 1 day
Time of day: pick your sending window (e.g. 09:00)
Timezone: wherever most of your merchants are
Re‑enrollment: On (you want this trigger to fire for each contact every time they hit the configured anniversary, subject to your criteria)
The power lives in the criteria, powered by the same segment builder you use for audiences.
For a Shopify app, you’re typically tracking install and feature usage via the SDK or the Spreeflo API. Let’s assume:
app_installedcustom event when the app is installed.app_uninstalledcustom event if they remove it.feature_usedcustom event when a key feature runs (e.g. a recovery flow sends, a report is viewed).
Your criteria group inside the Cyclic trigger might look like:
Exactly-one-year-since-install window
Still a customer
Inactive recently
Contact can be marketed to
Custom Events →
app_installed
Operator: AT_LEAST 1
Time window: in the last 365 daysCustom Events →
app_installed
Operator: HAS_NOT_TRIGGERED
Time window: in the last 364 days
This combination means: “they installed at least once in the last 365 days, but there have been zero install events in the last 364 days”. In practice, that’s the 1‑year anniversary of their most recent install.
Custom Events →
app_uninstalled
Operator: HAS_NOT_TRIGGERED
Time window: over all time
Pick signals that actually matter for your app. For CartWizard, that might be:
Custom Events →
feature_used
Operator: HAS_NOT_TRIGGERED
Time window: in the last 45 days
AND
Email Activity → any marketing email
Operator: DID_NOT_OPEN
Time window: in the last 45 days
You’re looking for “installed a year ago, has not used a key feature or opened email in the last month or two.”
Email Subscription Status is Subscribed
Marketing Status is Marketing
This is a textbook example of Brand Message #1: you’re capturing detailed behavioural data on each customer, then using it to speak to them at a moment that matters.
Once those criteria are set, every morning the Cyclic trigger feeds a handful of at‑risk, one‑year‑in stores into the rest of the journey.
Step 2: Tag them as anniversary inactives
First action after the trigger: an Add Tag node.
Tag: something like anniversary-inactive.
Why bother?
It gives you an easy way to pull reporting later (“how many touches did we send, and what did they do?”).
You can suppress these folks from other aggressive win‑back journeys while this one runs.
If you want to A/B test your anniversary email content later, you already have a stable cohort.
Keep “Force tag trigger” off unless you rely on Added Tag triggers elsewhere and want to fire them even if the tag already exists.
From here, every contact flows linearly into a brief bit of personalization logic.
Step 3: Multi-way Split to speak to different store profiles
Not every store should get the same “we miss you” email. A $29 hobby shop and a $5M brand who went quiet deserve different messages.
Use a Multi-way Split node to branch on store profile.
Your conditions here depend on what you sync into Spreeflo as contact attributes. Common examples for Shopify apps:
average_order_volume(NUMBER)shopify_plan(TEXT)monthly_gmv(NUMBER)vertical(TEXT – fashion, beauty, electronics, etc.)
You might define three branches:
High-value stores
Condition:monthly_gmvGREATER_THAN 100000Growing stores
Condition:monthly_gmvGREATER_THAN 10000 AND LESS_THAN 100000Smaller stores (else branch)
Everyone else.
Order matters. Put the high‑value condition first, then growing, then let the else branch catch the rest.
This is where you stop thinking “one email to all” and start thinking about individual businesses. Same milestone. Different stakes.
Each branch now flows into its own Send Email node.
Step 4: Craft the “it’s been a year — what changed?” email
Inside each branch, drop a Send Email node.
Configuration:
Template: one per branch, built in the email builder.
Sender identity: whichever address your users expect automated comms from.
Send only once: keep this on. You never want someone to get the same anniversary email twice because of a re‑entry edge case.
The copy can be simple, but it must be anchored in their history.
For example, for growing stores:
Subject: “You installed CartWizard a year ago. Still recovering carts?”
Opening line: reference when they installed and what they set up back then.
Middle: acknowledge the gap. “We’ve not seen any recovery campaigns run in the last 45 days.”
Ask: “Did your stack change, or did we drop the ball on making the app valuable enough?”
Offer: a quick “let’s fix this” path. That might be a one‑click link to restart a recommended playbook, or a 15‑minute audit for higher‑value accounts.
For high‑value stores, the same bones, but perhaps with:
A stronger personal tone from a founder or success lead.
A tangible incentive: extended free access to a new feature, or a done‑for‑you setup.
The key is that this isn’t a drip sequence. It’s one, well‑timed, high‑context message.
After the email send, you need to watch what happens.
Step 5: Wait up to a week for signs of life
Right after each Send Email node, add a Wait Condition node.
Condition:
Create a group that counts as “reactivated” for your app. You can combine multiple rules with OR, for instance:
Custom Events →
feature_used
Operator: AT_LEAST 1
Time window: in the last 7 days
OR
Email Activity → this specific anniversary email
Operator: CLICKED
Time window: in the last 7 days
OR
Email Activity → this specific anniversary email
Operator: REPLIED
Time window: in the last 7 days
Timeout: 7 days (unit: days)
What this does:
If they re‑engage quickly (use a feature, click, reply), the journey moves forward as soon as that happens.
If nothing happens, they automatically continue after a week.
Wait Condition doesn’t branch by itself. It just holds contacts. To split on outcome, you follow it with an If/Else.
Step 6: If/Else — did they come back or stay quiet?
Drop an If/Else node after the Wait Condition, and reuse the exact same condition you used there: the “reactivated” group.
Yes branch: contacts who used a feature or meaningfully engaged with the email within the window.
Else branch: still inactive after a week.
Now you can close the loop differently for each.
Reactivated path
These folks got value from the nudge. Treat them accordingly:
Update Contact Attribute
Add Tag
Optional: Send Internal Email
Attribute:
last_reengaged_source(TEXT)Update type: UPDATE
Value:
anniversary_email
And optionally:
Attribute:
last_reengaged_at(TIMESTAMP)Update type: SET_NOW
This bakes attribution into the contact record. When you’re later slicing data or building segments, you know which journeys are actually saving accounts.
Tag: reengaged-anniversary.
For very high‑value stores (you can gate this with another If/Else on GMV or plan), add a Send Internal Email node to alert your team. Include context in the body:
Store URL
Plan tier
What they just did (feature_used name, if you store it)
Link to their record in your app’s admin
This is where Spreeflo’s “founder‑leverage” angle shows up. You don’t need a CSM team watching dashboards. A one‑time journey design plus a short email to your inbox tells you exactly who deserves a human follow‑up.
No more marketing emails in this path. They’ve taken the hint.
Still‑inactive path
For those who ignore the email and remain inactive:
Add Tag
Optional: Webhook
Leave them alone, marketing‑wise
Tag: anniversary-still-inactive.
If you route at‑risk accounts into another system (Slack alert, lightweight CRM, internal tooling), use a Webhook node here:
Method: POST
URL: whatever endpoint receives your “at-risk-store” payload
Contact fields: either all attributes or a curated subset (email, store URL, plan, metrics)
The receiving service can then decide whether to:
Queue them for manual outreach.
Trigger an in‑app message when they next log into your UI.
Downgrade their plan after warning, if they’re clearly not using certain paid features.
Resist the urge to fire more emails immediately. They’ve had a thoughtful, personal ping and chose not to respond. Hitting them again right away burns goodwill and drives unsubscribes.
Because the Send Email node is set to “send only once”, they won’t get duplicate anniversary messages if they bounce around this journey in weird ways. And because the Cyclic trigger is gated on the anniversary condition, they won’t re‑enter tomorrow anyway.
Why this beats a generic inactivity drip
Most apps already have some flavour of “if they don’t log in for 30 days, send a reminder.” Those flows are blunt instruments:
They don’t acknowledge any relationship history.
They usually repeat on the same cadence, so they feel like nagging.
They ignore meaningful milestones like “one year with us” or “100th campaign sent.”
This anniversary pattern does the opposite:
It uses behavioural data and milestones from your web tracking and analytics to time outreach precisely.
It only fires for a small, high‑risk group: still‑paying customers at the edge of soft churn.
It trains your users to expect that you notice when things change, which makes future context‑rich emails more welcome.
From a numbers perspective, you can track:
Reactivation rate among anniversary inactives (feature usage, plan upgrades).
Net MRR saved vs. a control period before you turned this on.
Qualitative sentiment: replies that say “thanks for checking in” are a leading indicator that your automation feels human, not spammy.
All of this runs on a single journey that you build once as a journey, then mostly forget.
Adapting the pattern for your own app
A few variations worth considering:
Different milestone: 6‑month anniversary, or “90 days after install” for apps with shorter natural lifecycles.
Different inactivity window: for high‑usage apps (analytics, daily tools), 14–21 days might be enough. For apps tied to campaigns or seasons, 60–90 days is more reasonable.
Different “reactivated” definition: maybe it’s
subscription_upgraded,report_exported, orintegration_connectedrather than genericfeature_used.
The core idea never changes: use milestones to make re‑engagement personal, and use behavioural data to avoid talking past your users.
Most Shopify app teams already send install and usage events somewhere. Wiring those events into Spreeflo with the SDK or API, then using them inside the segment builder and journey nodes, gives you a way to stop leaking lifetime value quietly.
You don’t need more newsletters. You need one well‑timed email that says, “We noticed. What changed?”
Design that once, let Spreeflo run it, and you’ve added a small, compounding safety net under your MRR — exactly the kind of leverage founder‑led teams win with.
If you’re already tracking events but not acting on them, this is one of the lowest‑effort, highest‑impact journeys you can build.