Copy as markdown[View .md](https://docs.funnelfizz.com/features/automation "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fautomation.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automation "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fautomation.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automation "Open this page in ChatGPT with context")

# Automation

FunnelFizz automations are **workflows** — DAGs of Send / Wait / Split steps — that fire when a specific event happens (a profile enters a stage, a trial starts, a subscription activates).

For conceptual overview, see [Concepts → Automations](https://docs.funnelfizz.com/concepts/automations.md). This page is the feature reference.

## The canvas editor[​](#the-canvas-editor "Direct link to The canvas editor")

Open any stage → **Automations** tab → **Create automation**.

You get a ReactFlow canvas with:

* **Step palette** (left) — drag any step type onto the canvas.
* **Canvas** (center) — arrange steps and connect them with edges.
* **Settings** (right, when a step is selected) — configure that specific step.
* **Toolbar** — save, validate, activate/deactivate.

Each step is a card with a handle on the left (input) and one or two handles on the right (outputs). Drag from an output handle to another step's input handle to connect them. Splits have two output handles; everything else has one.

## Triggers[​](#triggers "Direct link to Triggers")

Pick the trigger when you create the automation:

| Trigger                  | When it fires                                    |
| ------------------------ | ------------------------------------------------ |
| `subscription_trial`     | Stripe subscription enters `status = "trialing"` |
| `subscription_paid`      | Stripe subscription enters `status = "active"`   |
| `stage_enter` *(legacy)* | Profile enters any stage                         |

Only one trigger per automation. If you want the same sequence to fire on both trial and paid events, create two automations.

## Steps[​](#steps "Direct link to Steps")

### SEND\_EMAIL[​](#send_email "Direct link to SEND_EMAIL")

Composes and queues an email send.

**Config:**

* **Subject** — string (supports `{{variables}}`).
* **Sender** — pick from your verified senders.
* **Blocks** — the email body (same MJML block system as standalone campaigns).

**What happens:** the step writes an `EmailSend` row with `status = "QUEUED"`. The email-queue cron picks it up within a minute and actually sends it.

The step completes immediately after queuing — it doesn't wait for the email to be delivered. If you want the workflow to wait for an *open* or *click*, use a `WAIT_UNTIL` step next.

### WAIT[​](#wait "Direct link to WAIT")

Pauses for a fixed duration.

**Config:**

* **Months / Days / Hours / Minutes** — pick any combination.

**What happens:** sets `nextStepAt = now + delay`. The cron re-claims the run when the delay elapses.

### WAIT\_UNTIL[​](#wait_until "Direct link to WAIT_UNTIL")

Pauses until an event fires, with optional timeout.

**Config:**

* **Event name** — `email_opened`, `email_clicked`, `pageview`, custom event names.
* **Timeout** (optional) — "give up after X duration."

**What happens:** sets status to `WAITING_FOR_EVENT`. When the event fires on this profile, the run flips back to `RUNNING` and continues. If the timeout expires first, the run also continues, but you can branch based on which happened via a CONDITIONAL\_SPLIT immediately after.

### CONDITIONAL\_SPLIT[​](#conditional_split "Direct link to CONDITIONAL_SPLIT")

Branches based on a condition.

**Config:**

* **Condition** — examples:

  <!-- -->

  * `email_opened == true`
  * `pageview_count >= 3`
  * `country in ['US', 'CA']`
  * `profile.plan == 'pro'`

* **Output A** — the "matched" branch.

* **Output B** — the "didn't match" branch.

**What happens:** the cron evaluates the condition against the profile's current state, follows the matching edge.

### PERCENTAGE\_SPLIT[​](#percentage_split "Direct link to PERCENTAGE_SPLIT")

Deterministic A/B split.

**Config:**

* **Percent to A** — e.g., 50 (other 50% goes to B).

**What happens:** `hash(runId + stepId) mod 100 < percent` determines the branch. Same profile always takes the same branch, so you can A/B test variants of downstream content.

## Saving, validating, activating[​](#saving-validating-activating "Direct link to Saving, validating, activating")

* **Save** — writes the DAG to the database. Automation stays in `DRAFT`.
* **Validate** — checks for issues: disconnected nodes, cycles (DAG must be acyclic), missing config on any step.
* **Activate** — flips status to `ACTIVE`. New profiles matching the trigger enter the flow.

Deactivating is symmetric: existing runs keep walking through the graph, but no new profiles enter.

## Per-profile runs[​](#per-profile-runs "Direct link to Per-profile runs")

When an automation is ACTIVE and the trigger fires for a profile, FunnelFizz creates an `AutomationRun` row:

* `automationId` — which automation.
* `profileId` — which profile.
* `currentStepId` — where they are in the graph.
* `status` — RUNNING, WAITING\_FOR\_EVENT, COMPLETED, CANCELLED.
* `nextStepAt` — when to wake them up.

The cron every 5 minutes claims due runs, advances them by one step, updates the row. Each profile is independent of every other.

## Cancellation[​](#cancellation "Direct link to Cancellation")

A run is cancelled when:

* The profile unsubscribes from your emails (workspace-wide).
* You manually pause the automation and explicitly cancel in-flight runs.
* The profile is deleted (rare; happens on data-export/delete requests).

## Common patterns[​](#common-patterns "Direct link to Common patterns")

### Trial-to-paid conversion[​](#trial-to-paid-conversion "Direct link to Trial-to-paid conversion")

```
trigger: subscription_trial

    │

[SEND_EMAIL: "Welcome, here's the quickstart"]

    │

[WAIT: 2 days]

    │

[SEND_EMAIL: "Here are 3 things Pro users do"]

    │

[WAIT: 4 days]

    │

[CONDITIONAL_SPLIT: profile.subscriptionStatus == "active"]

    │                              │

    ▼ true (already upgraded)      ▼ false

[END]                            [SEND_EMAIL: "Trial ends tomorrow"]
```

### Churn recovery[​](#churn-recovery "Direct link to Churn recovery")

```
trigger: stage_enter CUSTOMER where bucket == "Churned"

    │

[WAIT: 7 days]

    │

[SEND_EMAIL: "Can you tell us why you left?"]

    │

[WAIT_UNTIL: email_clicked (timeout: 14 days)]

    │                              │

    ▼ clicked                      ▼ timed out

[SEND_EMAIL: 20% off comeback]    [SEND_EMAIL: 30% off last chance]
```

### A/B test your onboarding[​](#ab-test-your-onboarding "Direct link to A/B test your onboarding")

```
trigger: subscription_trial

    │

[PERCENTAGE_SPLIT: 50% to A]

    │                │

    ▼ A              ▼ B

[SEND: variant A]   [SEND: variant B]
```

After enough runs, compare the downstream conversion rates in the campaign dashboard.

## Plan limits[​](#plan-limits "Direct link to Plan limits")

|                       | FREE | HOBBY | PRO |
| --------------------- | ---- | ----- | --- |
| Automations per stage | 0    | 1     | 5   |
| Max step depth        | —    | 3     | 5   |

Depth is the longest path from trigger to any leaf. A 3-step linear sequence with one branch is depth 3, not 4.

## What's not here (yet)[​](#whats-not-here-yet "Direct link to What's not here (yet)")

* **Cross-funnel automations.** Automations are scoped to one stage in one funnel. Global automations are on the roadmap.
* **Custom HTTP action step.** No "make an API call" step today. Use Zapier or n8n for outbound webhooks if you need them.
* **Conditional entry filters.** Every profile matching the trigger enters. If you need to filter, put a `CONDITIONAL_SPLIT` as the first step with an "end" branch for non-matches.

***

**Next:** [Splitting →](https://docs.funnelfizz.com/features/splitting.md) — the eight split conditions.
