Automations
An automation is a DAG of steps, Send / Wait / Split, that fires when something happens (a profile enters a stage, a trial starts, a subscription activates). Each profile walks through the graph independently. Use them for trial onboarding, drip campaigns, churn recovery, A/B onboarding tests.
Triggers
Pick one when you create the automation:
| Trigger | When it fires |
|---|---|
subscription_trial | Stripe subscription enters status = "trialing" |
subscription_paid | Stripe subscription enters status = "active" |
subscription_canceled (Churned) | Stripe subscription enters status = "canceled", i.e., a trialer who didn't convert (TRIAL stage) or a paying customer who cancelled (CUSTOMER stage). |
tier_changed | Subscription's product or price changed (upgrade, downgrade, monthly↔yearly swap). Fires on the new tier's track. |
custom_event | A custom event from your tracking site fires (e.g. pricing_clicked). Use this when the natural trigger is a behaviour, not a subscription state change. |
stage_enter (legacy) | Profile enters any stage. Catch-all when none of the more specific triggers fit. |
One trigger per automation, for sequences that should fire on both trial and paid, create two. The CUSTOMER stage's editor surfaces subscription_canceled as the Churned entry-point card alongside the Start trigger.
Step types
| Step | Behavior |
|---|---|
| SEND_EMAIL | Renders MJML blocks, queues an EmailSend row, completes immediately. Use a WAIT_UNTIL after it to wait for opens/clicks. |
| WAIT | Pause for a fixed duration (months / days / hours / minutes). |
| WAIT_UNTIL | Pause until an event fires (email_opened, pageview, custom event). Optional timeout. |
| CONDITIONAL_SPLIT | Branch on a condition (email_opened == true, country in ['US','CA'], profile.plan == 'pro'). Two output branches. |
| PERCENTAGE_SPLIT | Deterministic A/B, hashes (runId + stepId) mod 100. Same profile always takes the same branch. |
Editor
Stage → Automations tab → Create automation. ReactFlow canvas with a step palette, drag-to-connect edges, per-step settings panel. Validation catches disconnected nodes, cycles, missing config. Activate flips the automation to ACTIVE, new profiles entering the trigger window start walking the graph.
Deactivating is symmetric: existing runs continue, no new enrollments.
Enrollment respects the cohort
An automation attached to a stage only enrolls profiles who are actually in that stage's cohort. If the stage sits downstream of a split — a "US trial" branch, a "Hobby" sub-track, an extension on a nested track — only profiles who crossed every ancestor split on the prescribed track are eligible. The same filter that gates the stage card gates the recipient list. See splitting → cohort propagation.
This means: a profile who entered TRIAL via a different track than the one your automation lives on will not be enrolled, even if they technically entered the TRIAL stage.
Why didn't my automation fire?
Every time a trigger evaluates, FunnelFizz writes an automation_trigger_evaluated event onto the profile's activity timeline. If the profile didn't enroll, the event records the reason:
| Reason | Meaning |
|---|---|
no_matching_stages | The trigger source didn't match a stage attached to any active automation. |
cohort_filter_excluded_all_stages | The profile entered a stage but isn't in that cohort (e.g. wrong split track). |
no_matching_automations | A stage matched, but no active automations are attached to it. |
So "trigger ran, didn't fire" is visible from the profile detail page rather than silent.
Run lifecycle
When the trigger matches a profile, FunnelFizz creates an AutomationRun with status: RUNNING. A cron runs every 5 minutes, claims due runs, and advances them by one step:
RUNNING→ walking through steps.WAITING_FOR_EVENT→ parked on aWAIT_UNTIL.COMPLETED→ reached a leaf.CANCELLED→ profile unsubscribed, automation paused with cancel-in-flight enabled, or profile deleted.
Unsubscribes are workspace-wide and cancel any in-flight runs immediately.
Common patterns
Trial onboarding (subscription_trial):
[SEND: welcome] → [WAIT 2d] → [SEND: 3 tips] → [WAIT 4d]
→ [SPLIT: subscriptionStatus == 'active'?]
↳ yes → END
↳ no → [SEND: "trial ends tomorrow"]
Churn recovery (subscription_canceled on a CUSTOMER-stage automation, exposed in the editor as Churned):
[WAIT 7d] → [SEND: "tell us why?"]
→ [WAIT_UNTIL email_clicked, timeout 14d]
↳ clicked → [SEND: 20% off comeback]
↳ timed out → [SEND: 30% off last chance]
A/B onboarding (subscription_trial, then PERCENTAGE_SPLIT 50%):
┌─→ [SEND: variant A]
[SPLIT 50%/50%] ───┤
└─→ [SEND: variant B]
Compare downstream conversion in the campaign dashboard.
Plan limits
| FREE | HOBBY | PRO | |
|---|---|---|---|
| Automations per stage | 0 | 1 | 5 |
| Max step depth | — | 3 | 5 |
Depth = longest path from trigger to any leaf. A 3-step sequence with one branch is depth 3.
Replay does not trigger automations
When you connect Stripe (or create a funnel, add a split, add an extension, edit conversion config), FunnelFizz replays your Stripe history so existing customers populate the funnel at their actual historical timestamps. This backfill never enrolls anyone in an automation. Past-state customers don't get welcome emails six months late.
If you want to message backfilled customers — say, everyone who became a Pro subscriber before you set up the funnel — create a one-shot campaign instead.
Not here (yet)
- Cross-funnel automations. Scoped to one stage in one funnel today.
- HTTP action step. No "call an API" step. Use Zapier / n8n for outbound webhooks.
- Entry filters. Put a
CONDITIONAL_SPLITas the first step with an "end" branch.
Next: Conversion → · Email →