Stop Guessing Who Installed Your Shopify App: Build a Webhook Enrichment Journey
This playbook shows how a Shopify app like CartPilot can use Spreeflo custom event triggers and webhook-driven enrichment to segment new installs by store profile and send tailored onboarding journeys automatically.
Industry
Niche
Pattern
Loading sequence...
The install graph in your Shopify Partner dashboard looks great. Installs are up, reviews are solid, trial starts keep climbing.
But when you actually click into individual stores, it feels messy.
A tiny side project from a solo seller gets the same onboarding as a $5M brand. A discount code that would convert a new dropshipper is irrelevant for an established agency running 50 stores. You know this. You also know you don’t have time to research every install manually.
That gap between “someone installed” and “I know who they are and what they need” is where most Shopify apps quietly leak lifetime value.
The sequence at the top of this page is the whole journey, end to end. It shows how a single install event can kick off a Spreeflo journey that calls an enrichment service via Webhook, waits for fresh attributes to land on the contact, and then routes each new customer into the right onboarding experience for them.
We’ll walk through it node by node. But first, let’s ground this in a real app.
The app: CartPilot, and the enrichment problem
Picture CartPilot, a four-person remote team building an abandoned-cart and upsell app for Shopify.
Pricing: $29–$129/mo depending on order volume
Stage: ~$80k MRR, mostly self-serve installs from the App Store
Stack: Shopify Partner API, Stripe, Intercom, a basic ESP for newsletters
Their problem is familiar:
They don’t know which new installs are tiny side hustles vs serious brands.
Trial emails are generic, so larger stores don’t feel seen.
The team wastes time manually looking up “interesting” installs in LinkedIn and Crunchbase.
They already emit events like app_installed and app_uninstalled into their backend. They’ve now added the Spreeflo SDK to their marketing site and started sending custom events into Spreeflo as well, following the web tracking and analytics docs.
What they need is a repeatable system:
On every
app_installedevent, send the store domain and contact to an enrichment API.Have that service write back attributes like company size, vertical, and estimated GMV to the contact in Spreeflo.
Use those attributes immediately in onboarding and upsell paths.
That’s exactly what the webhook-driven enrichment journey does.
Why enrichment for Shopify apps is worth the effort
For Shopify and ecommerce apps, the difference between a “good” store and a “great” store is huge:
A hobby store at $1k/month GMV might churn within a few months.
A serious DTC brand or agency can easily be a $5k–$20k LTV customer.
If you can’t tell which is which, you end up:
Over-investing in tiny accounts.
Under-investing in potential whales.
Sending everyone the same onboarding and upsell messaging.
Two of Spreeflo’s core beliefs land hard here:
Winning customers come from rich customer data. If you know store size, vertical, geography, and whether they’re an agency or brand, you can speak to each install differently and move them to value faster.
Founder-led businesses win on leverage, not headcount. You don’t want a human “enrichment team.” You want a journey that fires on every install, calls an API, and updates your contact record without manual work.
This pattern uses Spreeflo’s Custom Event trigger and Webhook action (a Pro-plan node) to build exactly that. You design it once, then let the system run.
The journey at a glance
Before we get tactical, here’s what the journey in the visual sequence is doing:
Trigger on
app_installedusing a Custom Event trigger.Add a tag so you can track who entered this enrichment path.
Call an enrichment API via Webhook, sending store domain, contact email, and any known metadata.
Wait for enrichment to complete using a Wait Condition that checks for an “enrichment_status” attribute, with a timeout.
Split enriched vs not with an If/Else node based on that attribute.
If enriched, route by ICP segment using a Multi-way Split (e.g., Enterprise, Growth, Long tail).
Send tailored onboarding emails using different Send Email nodes per segment.
Merge paths back together and tag contacts so you can measure success later with Spreeflo’s segment builder.
Now let’s walk each node and its configuration decisions.
1. Start where the truth lives: Custom Event trigger
Node: Custom Event trigger
Event: app_installed
Re-enrollment: true
CartPilot already fires a backend webhook when someone installs the app. They extend that to call Spreeflo.track("app_installed", {...}) via the SDK or the Spreeflo API, passing:
The store domain.
The primary contact email.
Any Shopify metadata they have (plan, country, etc.).
Using a Custom Event trigger instead of Add to Audience means you’re triggering the journey from the real install moment, even if the contact was in your audience before (e.g., from a newsletter).
Setting Re-enrollment to true matters. A store might uninstall, then come back six months later with a bigger team and new funding. You want the enrichment call to fire again on the new app_installed event.
The journey-scoped lock still protects you from duplicates. If a second app_installed event fires while the contact is already mid-journey, that event is ignored until they exit.
2. Tag that this contact should be enriched
Node: Add Tag
Tags: needs-enrichment
Right after the trigger, the journey uses an Add Tag action to apply a needs-enrichment tag.
Why bother, when you already know they just installed?
You get a static marker that enrichment was attempted at least once. That makes it easy later to see who fell through the cracks.
You can drive other journeys or reporting off that tag without re-creating logic.
Leave the “Force tag trigger” toggle off unless you want downstream Added Tag triggers to fire even when a contact already has the tag.
3. Call your enrichment service via Webhook
Node: Webhook
Method: POST
Payload fields: Store domain, contact email, any known attributes
Auth: Whatever your enrichment microservice expects
This is the heart of the pattern.
The Webhook action sends an HTTP request from Spreeflo to your enrichment endpoint. On the other side you can run any logic you like:
Call an enrichment provider with the store’s domain.
Map their response into your own schema.
Call back into the Spreeflo API and update the contact’s attributes.
Important product detail: the Webhook node itself does not read or react to the HTTP response. It’s a one-way call. The enrichment service is what must write data back to Spreeflo, typically by:
Calling the REST API described in the Spreeflo API docs, or
Using
Spreeflo.identify(email, attributes)from your backend.
A simple approach is to always write at least these attributes on the contact:
company_size(e.g.,"1-10","11-50","51-200","200+")store_vertical(e.g.,"beauty","fashion","agency")est_monthly_gmv(numeric)enrichment_status(text:"success"or"not_found")
Every contact that passes through the Webhook node should end up with enrichment_status set one way or another.
Because Webhook is a Pro-plan node, this is also where you sanity-check your plan choice and how you think about Pro features in your own pricing.
4. Wait for enrichment to finish (without blocking forever)
Node: Wait Condition
Condition: enrichment_status is not blank
Timeout: 30 minutes
Your enrichment service won’t return instantly every time:
The provider might rate-limit you.
Your own microservice might be cold-starting.
Something might fail quietly.
The Wait Condition action is built for this. It pauses the journey until either:
The contact matches a condition, or
A timeout passes.
Here, the condition is simple: in the Segment Builder UI you define a rule on your custom attribute:
Category: Contact Attributes
Field:
enrichment_statusOperator:
is not blank
Set the timeout to something reasonable for your service, say 30 minutes.
This means:
If your microservice writes
enrichment_statusquickly, the contact moves on almost immediately.If anything goes wrong, they still move on after 30 minutes instead of sitting in limbo.
You’re deliberately not branching here. Everyone flows into the same next node, and you check what happened there.
5. Branch enriched vs not with If/Else
Node: If/Else
Condition: enrichment_status is "success"
Now you need to know whether enrichment actually succeeded.
You could inspect many attributes, but a dedicated enrichment_status flag keeps the logic clean. In the If/Else node:
Set the condition group to check
enrichment_status is "success".The “Yes” branch becomes your Enriched path.
The “Else” branch handles fallbacks when enrichment failed or returned no data.
This is where the journey’s behavior starts to diverge.
In the Else branch, CartPilot keeps things simple:
Add a tag
enrichment_failed.Send a generic onboarding email that doesn’t depend on enrichment attributes.
In the Yes branch, they go further.
6. Use Multi-way Split to route by ICP segment
Node: Multi-way Split
Branches:
enterprise–company_size is "51-200" OR "200+"growth–company_size is "11-50"long_tail–company_size is "1-10"
Else branch: Catch-all (e.g., weird or missing values)
This is where enrichment pays off.
Using the same criteria tools that power Spreeflo’s segment builder, the Multi-way Split node lets you define multiple branches with their own conditions.
CartPilot defines three clear cohorts based on company size alone. You could bring in:
Vertical (
store_vertical is "agency") for agency-specific onboarding.Geography for language-specific messaging.
Estimated GMV to prioritize who gets human outreach.
Order matters. The Multi-way Split evaluates branches top to bottom and sends each contact down the first branch whose condition they meet. The Else branch catches anything unexpected.
7. Send the right onboarding email for each segment
Node: Send Email (one per branch)
Content: Tailored onboarding per segment
Now that you know who you’re talking to, you can actually talk to them as such.
For each of the three Multi-way branches, the journey uses a separate Send Email node:
Enterprise: “How CartPilot fits a multi-brand, multi-store setup” with content about workflows, teams, and reporting.
Growth: “Turn your existing traffic into found revenue this week” with a focused checklist.
Long tail: “The 15-minute setup that pays for your app” keeping it minimal and outcome-focused.
Because each branch has its own email node, it’s easy to:
Use different subject lines.
Swap in different templates in the email builder.
Pull in enriched attributes with AI-assisted copy, using your internal guidelines and prompt library.
There’s no risk of spammy back-to-back sends here. This journey sends only a single marketing email per contact. If you extend it into a full onboarding series, remember to insert a Time Delay of at least a day between each Send Email node on any path.
Each email node can keep “Send only once” toggled on. If a store ever re-installs and re-enters the journey (thanks to the trigger’s re-enrollment flag), they won’t be hit with the same first email twice.
After each Send Email, all branches point to the same Merge node.
8. Merge everything and tag for analytics
Node: Merge → Add Tag
The Merge node lets you obey Spreeflo’s “one incoming edge per node” rule while keeping your canvas tidy. All three enriched-email branches flow into it, as does the fallback email path from the non-enriched branch.
From Merge, CartPilot adds a final Add Tag node that applies:
onboarding-personalizedfor enriched contacts, oronboarding-genericfor the fallback path.
You can implement that either:
As two separate Add Tag nodes (one on each path before Merge), or
With one Add Tag after Merge, and a tiny tweak upstream where the non-enriched path applies a different tag.
The sequence at the top of this page follows the first approach because it keeps each path self-explanatory when you zoom in.
Either way, you now have:
A complete enrichment and onboarding flow.
Clear markers on each contact for “enrichment attempted,” “enrichment succeeded,” and “which onboarding they got.”
Measuring enrichment success and attribute completeness
The pattern’s core metrics are:
Enrichment success rate: Of all contacts who passed through enrichment, what share ended up with
enrichment_status = "success"?Attribute completeness after pass: For enriched contacts, how many of the key attributes (company size, vertical, GMV) are non-blank?
You can approximate both with a couple of saved segments:
“Enrichment attempted” segment
- Condition:contact is tagged with "needs-enrichment"“Enrichment succeeded” segment
- Condition:enrichment_status is "success"“Core attributes complete” segment
- Conditions in an AND group:
-company_size is not blank
-store_vertical is not blank
-est_monthly_gmv is not blank
Segment counts give you:
Success rate =
size of Enrichment succeeded/size of Enrichment attemptedCompleteness rate =
size of Core attributes complete/size of Enrichment succeeded
If you want this in a recurring report, a small Cyclic journey can run weekly, filter on those segment memberships, and send a Send Internal Email digest to your own inbox.
Adapting the pattern to your own stack
The specific events and attributes will differ by app, but the skeleton holds:
Any event that marks a new relationship (install, account_created, workspace_invited) can drive the Custom Event trigger.
Any third-party data source that can be called over HTTP and mapped to contact attributes can sit behind the Webhook node.
Any meaningful segmentation you care about (team size, vertical, geography, tech stack) can be expressed in conditions with the same tools you use to build a journey.
A few practical tweaks to consider:
Backfill old installs with a second journey that uses a Criteria Match trigger on
enrichment_status is blank AND contact added date is before X.Add a high-intent branch after enrichment: if
est_monthly_gmvis above a threshold, send a Send Internal Email notifying the founder so they can do a quick personal reach-out.If your enrichment provider is expensive, use a Multi-way Split before the Webhook to only call it for contacts above a certain plan or visit threshold.
The core idea stays the same: use automation once, then keep compounding the benefit on every new contact.
Why this earns its spot in your stack
Most Shopify apps already send a welcome email. Fewer systematically ask: “Who is this store, really, and how should that change what we say next?”
Webhook-driven enrichment mid-journey is how you close that gap without adding headcount. You capture more detail on every customer so you can speak to each one uniquely, and you do it in a way your small team can actually maintain.
Design the journey once. Wire it to your enrichment service. Ship it.
From then on, every install is no longer just a store name and an email. It’s a data-rich profile that your onboarding, upsell, and retention flows can act on automatically.
For a founder building an ecommerce app, that’s what leverage actually looks like.