# Workflows (/concepts/workflows)

A workflow is an automation modelled as a graph: contacts advance step by step — receiving emails, pausing, taking conditional branches, hitting webhooks, having their data updated, or exiting. Every contact travels the graph on their own, in an independent **workflow execution**.

## Triggers

Each workflow has exactly one **trigger**, which determines how contacts get in:

| Trigger type | When it fires                                                                                                                                                                |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EVENT`      | Whenever a matching event lands on a contact — a custom event from `/v1/track`, a system event like `email.received` or `contact.subscribed`, or a segment entry/exit event. |
| `MANUAL`     | Never on its own; only when you start an execution yourself via `POST /workflows/:id/executions`.                                                                            |
| `SCHEDULE`   | On a recurring cadence described by `triggerConfig` (a cron expression, for instance).                                                                                       |

Workflows created through the API default to an `EVENT` trigger using the event name you supplied. Switching to `MANUAL` or `SCHEDULE` is done afterwards, by updating `triggerType` and `triggerConfig` with `PATCH /workflows/:id`.

Once a workflow has run at least once, its trigger event name is **frozen** — choose it deliberately. Everything else about the configuration (step delays, in-step conditions) stays editable.

## Lifecycle and the `enabled` flag

Every workflow starts life with `enabled: false`, and **nothing executes until you set `enabled` to `true`**. If a workflow seems to never fire, check this flag first — it's the usual culprit.

Whether a contact may pass through the same workflow twice is governed by `allowReentry` (off by default). Keep it disabled for one-time sequences like welcome or onboarding emails; enable it when the journey should be repeatable, such as recurring nudges or re-prompts.

## Step types

The first step of any workflow is a `TRIGGER` step, created automatically. Everything after it is built by adding steps and wiring them together with **transitions** (the graph's edges).

| Step type        | Purpose                                                                                                                              | Required config                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- |
| `TRIGGER`        | The auto-created entry point, carrying the trigger configuration.                                                                    | `eventName` (for `EVENT` trigger)                   |
| `SEND_EMAIL`     | Emails the contact from a template; variables are filled from contact data plus the execution context.                               | `templateId`, optional `from` override              |
| `DELAY`          | Holds the execution for a set amount of time before moving on.                                                                       | `amount`, `unit` (`minutes` / `hours` / `days`)     |
| `WAIT_FOR_EVENT` | Holds the execution until a given event is tracked on the contact, falling through on timeout.                                       | `eventName`, `timeout` (seconds)                    |
| `CONDITION`      | Splits the path based on contact or event data; every `CONDITION` step has two outgoing transitions labelled `yes` / `no`.           | A filter expression (same shape as segment filters) |
| `WEBHOOK`        | POSTs contact and execution context as JSON to an external HTTPS endpoint. `{{variables}}` work in `url`, header values, and `body`. | `url`, optional `method`, `headers`, `body`         |
| `UPDATE_CONTACT` | Applies a patch to the contact's data — a common way to tag progress (`{ stage: "activated" }`).                                     | `data` object                                       |
| `EXIT`           | Ends the execution, optionally noting an `exitReason` for later analysis.                                                            | optional `reason`                                   |

## Transitions

The edges connecting steps are called transitions. Most of the time a transition just means "go here next"; on `CONDITION` steps, each outgoing transition also carries a `branch` value (`"yes"` or `"no"`) that tells the engine which way to route once the condition is evaluated.

Removing a step from the middle of a chain? Add `?splice=true` to `DELETE /workflows/:id/steps/:stepId` and the neighbouring transitions are stitched back together automatically — without it, the graph is left with a gap.

## Executions

A `WorkflowExecution` is created for every contact that enters. Its possible states:

| Status      | Meaning                                                                                                        |
| ----------- | -------------------------------------------------------------------------------------------------------------- |
| `RUNNING`   | A step is being processed, or is about to be.                                                                  |
| `WAITING`   | Held inside a `DELAY` or `WAIT_FOR_EVENT` step.                                                                |
| `COMPLETED` | Made it to the end of the graph without issues.                                                                |
| `EXITED`    | Reached an `EXIT` step; the cause is stored in `exitReason`.                                                   |
| `FAILED`    | Stopped by an unrecoverable error — for instance a webhook still non-2xx after retries, or a missing template. |
| `CANCELLED` | Terminated by hand through the API or dashboard.                                                               |

Every execution has its own `context` JSON object. During template rendering and condition evaluation, it's merged into the contact's `data`. Manual starts via `POST /workflows/:id/executions` accept an initial `context`, which is the mechanism for per-execution variables — a coupon code, a referrer's name — that shouldn't be persisted on the contact itself.

## Locking active workflows

While any execution is active (`RUNNING` or `WAITING`), the workflow is **locked** against structural changes. Before making a breaking edit — adding or removing steps, changing the trigger event, switching trigger type — do one of the following:

* Turn the workflow off and let the in-flight executions finish, or
* Call `POST /workflows/:id/executions/cancel-all` to abort everything currently running.

Non-structural edits — renaming the workflow, changing the content of templates its steps reference — are allowed at any time.

## Common patterns

* **Welcome series** — trigger on the `signed_up` event, send the welcome email, wait 2 days, send a tips email, wait 5 more days, then nudge toward an upgrade.
* **Inbound auto-reply** — trigger on `email.received`, use a `CONDITION` checking `event.spamVerdict == "PASS"`, then `SEND_EMAIL` with the auto-reply template. See [Receiving emails](/guides/receiving-emails).
* **Re-engagement** — start from a segment exit (`segment.active-users.exit`), send a "we miss you" message, wait up to 7 days for an `email.opened` event, then branch on whether they engaged.
* **Webhook fan-out** — trigger on `purchase.completed`, hit your CRM with a `WEBHOOK` step, tag the contact via `UPDATE_CONTACT` (`{ tier: "customer" }`), and finish with a `SEND_EMAIL` receipt.

## API reference

Everything runs through the `/workflows` API, which covers the workflow itself plus its steps, transitions, and executions. The full route list is in the [API overview](/api-reference/overview#workflows).

## What's next

<Cards>
  <Card title="Templates" href="/concepts/templates">
    Write the email content that `SEND_EMAIL` steps deliver.
  </Card>

  <Card title="Receiving emails" href="/guides/receiving-emails">
    Start workflows from incoming mail via `email.received`.
  </Card>

  <Card title="Webhooks" href="/guides/webhooks">
    Reach your own services from a workflow with `WEBHOOK` steps.
  </Card>

  <Card title="Segments" href="/concepts/segments">
    Start workflows from segment entry / exit events.
  </Card>
</Cards>
