The “Respect Their Inbox” Playbook: Channel-Preference Routing for Shopify Apps
This playbook walks through a Shopify-style journey that detects each merchant’s preferred channel (email vs web push), stores it as an attribute, and routes future onboarding and upsell messages through that lane, only falling back when engagement is missing.
Industry
Niche
Pattern
Loading sequence...
A Shopify merchant installs your app on a Friday afternoon.
They’re in the middle of prepping a sale, 24 tabs open, phone buzzing. Your welcome email lands in their inbox. At the same time, your SDK quietly asks the browser for web push permission and… they actually click “Allow.” One click later, they’re back in Shopify and your message is buried under Stripe receipts and shipping alerts.
Forty‑eight hours later, you send another email. No open.
But when you ship a tiny “new feature” push notification on Monday, they click instantly, hop into your app, and complete onboarding.
You don’t have a messaging problem. You have a channel problem.
The merchants who would convert fastest are already telling you how they prefer to hear from you. Most apps just aren’t listening.
The sequence at the top of this page is the whole journey, end to end: detect a merchant’s preferred channel (email vs web push), route messages down that lane, and only fall back to the second channel if the first one clearly isn’t landing. It’s built for exactly the Shopify‑style world where your users live in their browser and their inbox in parallel.
To make it concrete, we’ll walk through this pattern using a fictional app:
CartWizard, a Shopify cart recovery app at $49/mo, doing around $100k MRR with 2,000 stores. Their stack is typical: Stripe for billing, a lightweight Rails backend, the Spreeflo SDK on their marketing site and in-app, and a mess of Mailchimp automations they’re itching to replace.
CartWizard’s problem: merchants either love them or churn quietly in the first 30 days. The fastest‑growing cohort are those who respond to their in‑app nudges and browser notifications, not email. But all of their automation is email‑first, so they’re constantly shouting into the wrong room.
Here’s how they use Spreeflo to fix that.
Why channel preference is worth designing for
Channel-preference routing sounds like a nice‑to‑have, until you look at where revenue actually leaks in a Shopify app:
Trial users install your app, miss the first two onboarding emails, and churn without ever setting up their first automation.
Active merchants use your core feature daily, but never see your “upgrade to Plus” pitch because it lives in a monthly newsletter.
Power users happily click your web push alerts, yet keep getting “gentle reminder” emails they never open.
Every one of these is a merchant telling you, with their behavior, “talk to me here, not there.”
Spreeflo is built around that idea: capture detail on every customer so you can speak to each of them uniquely. Channel preference is one of the most valuable “details” you can store.
Instead of guessing, this journey:
Watches how a contact reacts to your first touch.
Writes their preference into a contact attribute.
Uses that attribute to decide where to send future nudges.
Falls back to the secondary channel only when the preferred one is clearly not working.
Let’s go step by step through the sequence at the top of this page and unpack why each node is where it is.
Step 1: One trigger, many behaviors
We start this as a Journey, not a one‑off Campaign. CartWizard wants this to run continuously for every new merchant.
At the top of the flow you’ll see a single trigger node, usually a Custom Event trigger:
Trigger type: Custom Event
Event name: something like
app_installedortrial_startedRe-enrollment: off (
isReEnrollment = false)
Whenever CartWizard’s app posts Spreeflo.track('app_installed', { plan: 'trial' }), that merchant enters the journey.
Why Custom Event instead of Add to Audience? Because for Shopify apps, installation is the real “moment of truth.” Contacts might exist in your audience long before they install (newsletter subscribers, webinar attendees). You only want this routing sequence kicking in once they’ve actually installed and started a trial.
Re-enrollment stays off. You don’t want merchants bouncing in and out of this routing logic every time they reinstall the app. If you do need a separate “reinstall” journey later, it should be its own automation with its own trigger.
Step 2: Tag and timestamp the install
Immediately after the trigger, CartWizard adds a couple of cheap but important actions:
Add Tag
- Tags:trial,onboarding-sequence-47
- Force tag trigger: offUpdate Contact Attribute
- Attribute:onboarding_started_at(TIMESTAMP)
- Update type:SET_NOW
This looks like bookkeeping, but it matters later when you analyze performance in the web analytics dashboard or build segments like “trial users who never finished onboarding.”
Tags make it obvious inside the audience view who has already gone through this journey. The timestamp tells you when it started.
Step 3: Lead with your best guess — email first
Next comes the first message. CartWizard chooses email as the initial channel:
Send Email
- Template: “Trial Day 0 – Set Up Your First Recovery Flow”
- isSendEmailOnlyOnce: on
Why email first when we’re talking about channel preference? Because for most SaaS apps, email is still the default “contractual” channel. Merchants expect receipts, trial confirmations, and setup checklists there.
This email does two jobs:
It asks the merchant to complete a simple, high‑value setup step.
It gives us our first signal: do they open it, or ignore it?
The template is built in Spreeflo’s email builder, and you can use AI personalization here to inject dynamic context like “we saw you installed from the Shopify App Store search for ‘cart recovery’” if you’re tracking UTM data.
Right after sending, we insert a Time Delay:
Time Delay
- 1 day
This gives merchants a full day to open or click the email before we judge the channel.
Step 4: Watch quietly: did they engage with the email?
Now we add a Check Email Activity process node:
Check Email Activity
- Marketing email: that Day 0 onboarding email
- Activities:
- Branchopened: opened
- Branchclicked: clicked
- Else branch: everything else (did nothing / bounced / undelivered)
Why Check Email Activity instead of trying to read this via a segment? Because this node is purpose‑built for exactly this “branch on how they behaved with this one email” use case. It lets you model three meaningful paths:
They clicked (high intent).
They opened but didn’t click (mildly engaged).
They didn’t touch it (inbox might be the wrong place).
You don’t need to specify frequency or time window here; the journey context limits it to activity since you sent that email.
Step 5: If they click, lock in email as preferred
For merchants in the clicked branch, you already have your answer: email is working. CartWizard goes down a short path:
Update Contact Attribute
- Attribute:preferred_channel(TEXT)
- Update type: UPDATE
- Value:emailAdd Tag
- Tags:prefers-email
- Force tag trigger: offSend Email
- Template: “You’re 80% set — finish your recovery flow”
- isSendEmailOnlyOnce: on
You could stop after setting the attribute, but the tag helps you build quick segments in Audiences like “all merchants who prefer email” without remembering the attribute’s exact name.
At this point, every future journey that needs to message this merchant can ask a simple If/Else question: does preferred_channel = email?
We’ll come back to that.
Step 6: If they just open, nudge again on email
For the opened branch (opened but didn’t click), CartWizard decides email is still probably fine — the subject line and from‑name are clearly making it through, but the CTA didn’t land.
So the path looks like:
Update Contact Attribute:
preferred_channel = email(same as before)Time Delay: 1 day
Send Email: “Still need help setting up CartWizard?” with alternate copy
You could choose not to set preferred_channel here and wait for a push signal later, but that slows down learning. If a merchant is reading your email, they’re at least not allergic to the channel. You can always override the attribute later if push performance is clearly better.
Step 7: No email engagement? Switch to push as primary
The interesting part is the Else branch: merchants who didn’t open or click.
Here CartWizard assumes email is low‑leverage for now and tests push as the primary channel:
Send Web Push
- Template: “Set up your first abandoned cart flow in 3 clicks”
- isSendOnlyOnce: onTime Delay
- 1 dayCheck Web Push Activity
- Web push notification: that onboarding push
- Activities:
- Branchclicked: clicked
- Else branch: everything else (delivered but not clicked, dismissed, etc.)
For those who click the push, you again assert:
Update Contact Attribute:
preferred_channel = web_pushAdd Tag:
prefers-push
Then send a follow‑up via push or in‑app guidance, and you’re done. Future journeys should talk to this merchant primarily via web push.
For those who still don’t respond to push, you have two choices:
Accept that they’re cold and move them into a low‑frequency nurture.
Fall back to email again as a last attempt, but with a much stronger subject or incentive.
CartWizard chooses a gentle fallback:
Merge node to recombine the “email‑preferred” paths and the “no signal yet” path.
Send Email: “Is CartWizard still a fit for your store?”
Add Tag:
onboarding-unresponsive
The Merge node is important: remember Spreeflo’s rule that every non‑Merge node can have only one incoming edge. If two branches need to join back up, they must connect into a Merge before continuing.
Step 8: Re-using preference everywhere else
So far we’ve talked about how to detect preference. The whole point, though, is to reuse it.
Once preferred_channel is set on a contact, every new journey CartWizard builds can start with a simple If/Else process node:
If/Else
- Condition (Segment Builder):
- Contact Attributes →preferred_channel→ is →web_push
“Then” path (web push‑first):
Send Web Push
Wait Condition for engagement (e.g. clicked link, custom event
feature_used)If no engagement after X hours, fall back to a Send Email with the same message.
“Else” path (email‑first):
Send Email
Wait Condition for engagement
If no engagement, fall back to a Send Web Push.
You’ll see this pattern in the second half of the sequence at the top of this page: a reusable preference gate before every major campaign.
The Wait Condition node is how you do the “fallback only if no response” part cleanly:
Wait Condition
- Condition: Custom Eventfeature_usedtriggered at least 1 time in the last 1 day
- Timeout: 1 day
This pauses the journey until the merchant either uses the feature you’re promoting (so you don’t need to nag them) or the timeout expires, in which case you move them to the fallback channel.
Because the Wait Condition embeds the full segment builder, you can express rich logic here: “clicked the link OR visited the /pricing page at least once in the last 24 hours.”
Instrumentation: how CartWizard knows this is working
Two metrics matter for this pattern:
Preferred-channel conversion rate
For each branch (preferred_channel = emailvsweb_push), CartWizard measures:
- % who complete onboarding within 7 days
- % who convert from trial to paid
Because tags are applied (prefers-email,prefers-push), they can build simple segments and run one‑off Campaigns to analyze behavior without touching the journeys.Channel-mix optimization
Over a quarter, they want to answer:
- What share of merchants end up preferring web push vs email?
- For power users (say, custom eventfeature_usedat least 10 times in last 30 days), is there a different channel split?
- Does making push the default for certain geos or store sizes improve ARR?
This is where Spreeflo’s campaigns vs journeys model helps. Journeys do the always‑on routing. Occasional analysis campaigns and segments help you pull stats by cohort without editing the logic that already works.
The moment this pays for itself is when CartWizard sees a 20–30% lift in onboarding completion for merchants tagged prefers-push versus the old email‑only path. That uptick in trial‑to‑paid conversion more than covers Spreeflo’s pricing, and the system keeps learning as more merchants flow through.
Design choices worth copying (and traps to avoid)
A few details in this sequence are intentional:
One trigger only. The journey has a single Custom Event trigger with re-enrollment off. Every other behavior change happens via Wait Condition, Check Email Activity, and If/Else nodes. This avoids the multi-trigger trap where later triggers silently never fire.
Attributes plus tags for preference. The
preferred_channelattribute is the “source of truth.” Tags likeprefers-emailare for convenience. If you ever adjust your logic, you only need to update how you interpret the attribute.Fallbacks are deliberate, not instant. The sequence never blasts both email and push at the same time for the same message. It waits for engagement on the first channel, then moves thoughtfully to the second.
“No engagement” has its own label. Merchants who ignore everything aren’t just “not preferred yet”; they’re a distinct segment (
onboarding-unresponsive) that deserves different treatment: fewer messages, maybe a short survey, maybe a win‑back later.
The main trap to avoid is turning this into a branching tree for its own sake. The point of channel-preference routing is to make your communication feel more respectful, not more complex. Start by routing just onboarding and one key upsell through this pattern. Once that’s stable, extend it to other journeys.
The bigger idea: channel preference is just another way of listening
For a Shopify app, the merchant’s time is your scarcest resource. They’re not ignoring you because they hate your product. They’re overwhelmed. Email might be overrun. Browser notifications might be their “quick scan” lane during the day. Or the opposite.
The power of this pattern is that it treats channel as a first‑class piece of customer data, just like plan, MRR, or store size. You stop shouting generically and start conversing where they’ve already voted with their clicks.
Build this once in Spreeflo, and every future journey can respect that choice automatically. That’s what founder‑led businesses win with: systems that remember, adapt, and keep compounding while you’re heads‑down shipping the next feature, not manually fiddling with lists.
Channel preference isn’t magic. It’s just listening, then routing. But for a Shopify app fighting for attention in a crowded inbox and a busy browser, that bit of listening can quietly add up to a lot of saved trials and a lot less silent churn.