Back to Playbooks

Stop Copy‑Pasting Stages: A Spreeflo Journey That Keeps Your CRM Honest

Free

This playbook shows how to turn behavioral events in your e‑commerce app into reliable MQL, SQL, and Won stages in your CRM using Spreeflo journeys, criteria triggers, and webhooks, without building heavy custom integrations.

Industry

Niche

Pattern

Loading sequence...

The founder of “CartWizard” thought they had their funnel under control.

Trial installs were flowing from the Shopify App Store. A handful of high‑value merchants got white‑glove outreach. Deals closed. Life was good.

Then they opened their CRM.

Half the “Leads” column were stores that had already recovered thousands in abandoned carts. A few “Won” deals had actually uninstalled last month. MQL, SQL, Customer… it was all guesswork because the data in their app and the data in their CRM were living separate lives.

So the team did what most small SaaS teams do: spreadsheets, manual stage updates, a few Zapier zaps, and a mental TODO list of “fix this properly one day.”

The sequence at the top of this page is the whole journey, end to end. It’s what “fix this properly” actually looks like: a Spreeflo journey that fires a webhook to your CRM each time a contact hits MQL, SQL, or Won, using no more engineering than a simple webhook receiver.

Why qualification milestones should never depend on memory

As an e‑commerce app developer, you’re already tracking rich behavior: app_installed, feature_used, subscription_upgraded, payment_failed, and so on.

Your product knows long before your CRM does that:

  • A store installed your app and ran their first campaign (marketing qualified).

  • They sent enough volume or hit enough revenue that a human should step in (sales qualified).

  • They upgraded, activated, or stuck around long enough to count as a won deal.

But unless someone is:

  • Manually re‑typing that reality into the CRM, or

  • You’ve sunk time into a custom integration for every tool in your stack,

your sales‑assist motion will always lag behind what’s actually happening inside your app.

For a 4–7 person team, that lag is costly. Every hour you spend wiring systems together is an hour you’re not improving install rate, activation, or retention. Founder‑led businesses win on leverage, not headcount — and sync‑the‑CRM busywork is about as anti‑leverage as it gets.

This is where Spreeflo should sit in your mental model: the behavioral brain that watches what users do, decides what stage they’re in, and then tells your CRM about it automatically.

We’ll walk through how the journey at the top of this page is built, node by node, so you can adapt it to your own app.

The high‑level architecture: Spreeflo as the “stage oracle”

Before we open the journey, it helps to be clear on the roles:

  • Your app and tracking layer send events into Spreeflo via the SDK or the Spreeflo API.

  • Spreeflo uses those events, plus attributes and tags, to determine when someone becomes MQL, SQL, or Won.

  • At each milestone, a journey fires a Webhook action out to a tiny integration service you control.

  • That service calls your CRM (HubSpot, Pipedrive, Close, a home‑grown tool, whatever) and updates the contact and deal records.

No big, brittle, one‑off integration. Just a single webhook endpoint that understands “this contact just became MQL/SQL/Won.”

Inside Spreeflo you define:

  • What “MQL”, “SQL”, and “Won” mean using the segment builder.

  • Three Criteria Match triggers (one per milestone).

  • A short chain of actions in each branch: update attributes, tag, then send Webhook.

Let’s go through each path.

Trigger 1: When a trial becomes an MQL

The first starting point in the sequence is a Criteria Match trigger labelled “Becomes MQL.”

This trigger’s job is: the moment a contact tips over from “just installed” to “worth marketing effort,” they enter this branch.

A practical definition for a Shopify‑style MQL might be:

  • They’re subscribed to email marketing.

  • They installed the app.

  • They ran at least one core workflow (e.g. recovered a cart, sent a campaign, enabled a key feature).

  • They’ve logged in or visited your dashboard more than once in the last week.

You express that in the Criteria Match trigger using the same UI as the segment builder:

  • Email Subscription Status is “Subscribed.”

  • Custom Event app_installed triggered at least 1 time.

  • Custom Event feature_used triggered at least 1 time.

  • Total visits at least 2 in the last 7 days.

Crucial setting: in this journey, you turn Re-enrollment ON for this trigger.

That means:

  • The trigger will enroll a contact the first time they become MQL.

  • If they ever drop below MQL criteria and later re‑qualify, they can flow through again (rare, but safe).

  • Most importantly, it won’t block the SQL or Won triggers later. With Spreeflo’s journey‑wide re‑enrollment rules, at least one trigger in a multi‑entry journey has to allow re‑entry, or everything after the first milestone silently stops working. Setting re‑enrollment to true on all three triggers keeps each milestone independent.

Once the MQL trigger fires, the path is short and sharp.

Node: Update Contact Attribute — set lifecycle_stage to “MQL”

First action: an Update Contact Attribute node sets a TEXT attribute like lifecycle_stage to the literal string MQL.

Configuration:

  • Attribute: lifecycle_stage (custom TEXT attribute).

  • Update type: Update (overwrite).

  • Value: MQL.

This attribute becomes the single source of truth for stage across your tools. Your webhook payload will include it, and your internal reporting in Spreeflo can segment on it later.

Because Update Contact Attribute values are static at design time, this is exactly what they’re good for: writing fixed markers like “MQL”, “SQL”, “Customer” at the exact right moment.

Node: Update Contact Attribute — stamp became_mql_at

Next, add another Update Contact Attribute node targeting a TIMESTAMP attribute became_mql_at:

  • Update type: Set to now.

No manual value needed; Spreeflo writes the current timestamp when the node runs. These “became_*_at” fields are invaluable when you want to analyze funnel velocity later.

Node: Add Tag — “stage-mql”

Tags give you quick filters across tools and backup for when other systems don’t ingest custom attributes fully.

Drop in an Add Tag node:

  • Tags: stage-mql.

  • Force tag trigger: off (you don’t need downstream Added Tag triggers from this tag right now).

You now have three consistent representations of “this is an MQL”: an attribute, a tag, and the triggering condition itself.

Node: Webhook — notify your CRM integration

The final node on this branch is the star: a Webhook action.

Configuration:

  • Webhook URL: your integration endpoint, e.g. https://app.yourdomain.com/crm-sync.

  • Method: POST.

  • Contact fields: All contact attributes (so your CRM has as much context as you’re already tracking).

  • Authentication: toggle on and choose either API Key (header name + key) or Bearer Token, depending on how your backend expects auth.

Spreeflo will POST a JSON payload with the contact’s data. Your endpoint reads lifecycle_stage, became_mql_at, Shopify store domain, plan, and any other attributes you care about, then calls your CRM’s API to:

  • Create or update the contact.

  • Set their lifecycle stage to MQL.

  • Attach any relevant metadata.

Because this branch has no Time Delay or Wait Condition, contacts enter, update, and sync almost instantly — and exit the journey, ready for SQL and Won triggers later.

Trigger 2: When a customer becomes sales‑qualified (SQL)

The second starting node is another Criteria Match trigger: “Becomes SQL.”

Here you encode “this account deserves human attention.” For CartWizard, that might be:

  • App installed at least 7 days ago.

  • Recovered cart value greater than $500, or a threshold number of events.

  • Plan is mid‑tier or higher.

  • They’ve visited the pricing or upgrade page recently.

Again, you express that logic via the Criteria Match trigger using behavioral rules: Custom Events with amount properties, Total Visits, Page Visited filters, and Contact Attributes like plan.

Re‑enrollment is also ON here, for the same reasons: a contact who has already been through MQL must be allowed to pass through SQL when they later qualify.

The SQL branch mirrors the MQL one with slightly different values.

Node: Update Contact Attribute — bump lifecycle_stage to “SQL”

Set the same lifecycle_stage attribute to SQL:

  • Attribute: lifecycle_stage.

  • Update type: Update (overwrite).

  • Value: SQL.

Because every stage writes to the same attribute, there’s no confusion later in the CRM or your BI tools.

Node: Update Contact Attribute — became_sql_at

As before, stamp a TIMESTAMP attribute:

  • Attribute: became_sql_at.

  • Update type: Set to now.

Now you can measure MQL→SQL lag by comparing became_sql_at and became_mql_at.

Node: Add / Remove Tags — stage cleanup

Drop in a small tag hygiene step:

  • Remove Tag node to strip stage-mql if it exists.

  • Add Tag node to apply stage-sql.

This keeps your tags in sync with your primary lifecycle_stage attribute and prevents conflicting labels.

Node: Webhook — CRM update + sales notification

Add another Webhook action, this time pointing to the same endpoint but letting your backend decide what to do with an SQL:

  • Webhook URL: same as MQL.

  • Method: POST.

  • Contact fields: either All contact attributes, or a curated subset that your CRM mapping expects.

Your integration service now sees lifecycle_stage = SQL and became_sql_at and can:

  • Move the deal into the SQL column.

  • Assign an owner.

  • Trigger a CRM‑native task or sequence if you use one.

Optionally, you can follow the Webhook with a Send Internal Email node that pings your team:

  • To: founders or the sales‑assist inbox.

  • Subject/body: “New SQL: {{contact.store_name}} just recovered $1,200.”

That’s not required for the pattern, but small teams often like a heads‑up.

Trigger 3: When the deal is actually Won

The third trigger, “Becomes Customer (Won),” catches the moment a contact truly converts.

For many Shopify apps, that’s some combination of:

  • Custom Event subscription_upgraded fired.

  • Contact Attribute subscription_status is “active.”

  • Custom Event payment_successful triggered at least once.

  • They’ve stayed active past your typical trial length.

Again, this is a Criteria Match trigger, using Custom Events and attributes sourced from your app via the Spreeflo SDK or web tracking and analytics.

Re‑enrollment stays ON. If a store churns and re‑activates months later, you probably want to sync the stage change again.

The Won branch looks very similar structurally:

Node: Update Contact Attribute — lifecycle_stage = Customer

Overwrite the same TEXT attribute:

  • Value: Customer (or Won, if that matches your CRM’s vocabulary).

This is the final stage for most self‑serve deals.

Node: Update Contact Attribute — became_customer_at

Stamp a became_customer_at TIMESTAMP with Set to now.

Now you can compute full funnel times: first seen → MQL → SQL → Won.

Node: Tag hygiene

Clean up tags:

  • Remove stage-sql and stage-mql if present.

  • Add stage-customer.

This keeps segments and quick filters predictable inside Spreeflo and downstream tools.

Node: Webhook — final CRM sync

One last Webhook action sends the full contact payload out.

At this point, your backend might:

  • Move the deal to “Won.”

  • Create a subscription object with plan and MRR in your CRM.

  • Kick off onboarding playbooks that live there.

Because all three milestones share the same basic payload structure, your webhook receiver can be very small: inspect lifecycle_stage and timestamps, route to the right CRM API calls, and return 200 on success.

Why three triggers instead of one big branching monster

You might be wondering why the sequence uses three separate Criteria Match triggers instead of one large journey where everyone enters as a visitor and flows through MQL → SQL → Won in a single path.

Two reasons:

  1. Independence. In reality, you’ll evolve your MQL/SQL/Won definitions over time. Tying each milestone to its own trigger and branch lets you tweak criteria without risking the others. You can even disable one trigger temporarily without touching the rest.

  2. Re‑enrollment semantics. Spreeflo’s journeys apply re‑enrollment at the journey level. If you packed everything behind a single Add to Audience trigger with re‑enrollment off, you’d only ever get one shot at syncing a stage. Using separate Criteria Match triggers with re‑enrollment on means a contact can hit MQL today, SQL in a month, and Won in three months — each milestone runs as its own quick micro‑journey.

From a canvas perspective, it also keeps things readable. Each arm in the sequence at the top of this page is only a handful of nodes.

How to adapt this pattern to your own app

The skeleton of this journey is universal, but the criteria are specific to your product.

Start by writing down, outside of any tool, what MQL / SQL / Won really mean for your e‑commerce app. For example:

  • MQL: installed + sent first campaign + > X store traffic.

  • SQL: recovered > $Y in revenue or running on a store over Z sessions/day.

  • Won: moved to paid plan and stayed past trial.

Then:

  1. Make sure you’re sending the required events and attributes into Spreeflo, either via the SDK or the Spreeflo API. If you’re on the Professional plan, you can see these flowing through in web tracking and analytics.

  2. Model your definitions in the Criteria Match triggers using the same logic you’d use to build a segment. If you need to reuse them elsewhere, also save them as named segments from the audiences and segments view.

  3. Build the journey using the sequence editor described in the campaign and journey automation docs: three Criteria Match triggers, each followed by the attribute/tag/webhook chain.

  4. Implement a tiny webhook receiver in your backend that:
    - Authenticates the request (API key or Bearer).
    - Reads lifecycle_stage and *_at timestamps.
    - Calls your CRM’s API with the right mapping.

Because Spreeflo’s Webhook node can send all contact attributes, you don’t have to change your integration every time you add a new attribute. You just start using it in your CRM mapping.

What to measure once this is live

Two metrics matter for this specific pattern:

  1. Sync success rate. Your webhook endpoint should return 2xx responses on almost every call. Track failed vs successful syncs in your logs. If failures spike, it’s often because the CRM’s schema or auth changed — and you’ll be glad you can see that clearly.

  2. Downstream CRM record completeness. Periodically audit a sample of CRM contacts against what Spreeflo has. Do lifecycle stage, plan, install date, and key usage metrics match? If not, update which fields you send from the Webhook node or how your backend maps them.

Second‑order, you’ll also see qualitative improvements:

  • Fewer “who is this?” questions in sales‑assist channels.

  • Faster response times to genuine high‑intent signals.

  • Cleaner cohort analysis because stage changes are timestamped consistently.

All without another person on the payroll.

The real payoff: your small team stops acting like integration middleware

This journey is not glamorous. No flashy design, no AI‑generated prose. Just a tight loop that watches for qualification milestones and keeps your CRM from lying to you.

But this is exactly where founder‑led SaaS teams win: you set up a system once, and every future lead benefits from it.

Instead of:

  • Manually dragging deals to new columns.

  • Forgetting to update lifecycle stages after a flurry of installs.

  • Burning a week building a one‑off CRM integration,

you invest an afternoon defining criteria, wiring three Criteria Match triggers, and pointing a Webhook node at a tiny piece of backend code.

From then on, every store that installs your app moves through MQL, SQL, and Won with the CRM, your product, and your marketing stack all in agreement.

That’s what leverage looks like: your qualification logic lives where your behavioral data lives, and the busywork is handled by a system that doesn’t forget.

Spreeflo gives you the building blocks — journeys, the segment builder, web tracking, and webhooks — to make that system real. The sequence at the top of this page is one way to put those blocks together. The next step is adapting it to how your app actually defines a great customer, so your CRM finally matches the reality your product already sees.