Workflows
Build automated, multi-step contact journeys triggered by events, segments, schedules, or manual entry
Workflows are graph-based automations that move contacts through a sequence of steps — sending emails, waiting for activity, branching on conditions, calling webhooks, updating contact data, or exiting. Each contact that enters a workflow runs through it independently as a workflow execution.
Triggers
A workflow has a single trigger that decides how contacts enter:
| Trigger type | When it fires |
|---|---|
EVENT | Any time a matching event is tracked on a contact (custom event via /v1/track, system events such as email.received or contact.subscribed, or segment entry/exit events). |
MANUAL | Only when you explicitly start an execution via POST /workflows/:id/executions. |
SCHEDULE | On a recurring schedule defined by triggerConfig (e.g. cron expression). |
When you create a workflow via the API, the trigger defaults to EVENT with the event name you provide. To use MANUAL or SCHEDULE, change triggerType and triggerConfig with PATCH /workflows/:id after creation.
A workflow's trigger event name cannot be changed after the first execution — pick it carefully. Other trigger configuration (delays, conditions inside steps) remains editable.
Lifecycle and the enabled flag
Workflows are created with enabled: false. No executions start until you flip enabled to true — this is the most common reason "my workflow isn't firing."
The allowReentry flag (default false) controls whether a contact can enter the same workflow more than once. Leave it off for "first-touch only" sequences (welcome emails, onboarding) and turn it on for re-engageable journeys (re-prompts, repeated nudges).
Step types
A workflow always begins with a single auto-created TRIGGER step. You build the rest of the graph by adding steps and transitions (edges) between them.
| Step type | Purpose | Required config |
|---|---|---|
TRIGGER | Auto-created entry point. Holds the trigger configuration. | eventName (for EVENT trigger) |
SEND_EMAIL | Sends an email to the contact using a template. Template variables resolve from contact data + execution context. | templateId, optional from override |
DELAY | Pauses the execution for a fixed duration before continuing. | amount, unit (minutes / hours / days) |
WAIT_FOR_EVENT | Pauses until a specified event is tracked on the contact, with a timeout fallback. | eventName, timeout (seconds) |
CONDITION | Branches the execution based on contact data or event data. Each CONDITION step has two outgoing transitions tagged yes / no. | A filter expression (same shape as segment filters) |
WEBHOOK | Calls an external HTTPS endpoint with contact + execution context as the JSON body. url, header values, and body support {{variables}}. | url, optional method, headers, body |
UPDATE_CONTACT | Patches contact data — useful for tagging contacts as they progress ({ stage: "activated" }). | data object |
EXIT | Terminates the execution. Optionally records an exitReason for analytics. | optional reason |
Transitions
Transitions are the edges of the workflow graph. For most step types a transition is a simple "next" pointer. For CONDITION steps, each transition carries a branch discriminator ("yes" or "no") so the engine knows which path to take when the condition evaluates.
When deleting a step in the middle of a chain, pass ?splice=true on DELETE /workflows/:id/steps/:stepId to automatically reconnect the surrounding transitions; otherwise the deletion leaves the graph disconnected.
Executions
Each contact entering the workflow creates a WorkflowExecution. Executions move through these states:
| Status | Meaning |
|---|---|
RUNNING | Currently processing a step (or about to). |
WAITING | Paused inside a DELAY or WAIT_FOR_EVENT step. |
COMPLETED | Reached the end of the graph successfully. |
EXITED | Hit an EXIT step. exitReason records why. |
FAILED | An unrecoverable error occurred (e.g. webhook returned non-2xx after retries, template not found). |
CANCELLED | Cancelled manually via the API or dashboard. |
Each execution carries a context JSON object that's merged with the contact's data when rendering templates and evaluating conditions. When you start an execution manually with POST /workflows/:id/executions, you can pass an initial context — this is how you parameterise per-execution variables (a coupon code, a referrer name) without storing them on the contact.
Locking active workflows
A workflow is locked for structural edits while it has active (RUNNING or WAITING) executions. To make breaking changes — adding/removing steps, changing the trigger event, switching trigger type — first either:
- Disable the workflow and wait for active executions to complete, or
POST /workflows/:id/executions/cancel-allto cancel everything in flight.
Cosmetic changes (renaming, editing template content referenced by steps) don't require this.
Common patterns
- Welcome series —
EVENTtrigger onsigned_up, send welcome email, delay 2 days, send tips email, delay 5 days, send upgrade nudge. - Inbound auto-reply —
EVENTtrigger onemail.received,CONDITIONonevent.spamVerdict == "PASS", thenSEND_EMAILwith an auto-reply template. See Receiving emails. - Re-engagement — Triggered by a segment exit (
segment.active-users.exit), send a "we miss you" email, wait 7 days for anyemail.openedevent, branch on whether the contact engaged. - Webhook fan-out —
EVENTtrigger onpurchase.completed,WEBHOOKstep to your CRM,UPDATE_CONTACTto tag{ tier: "customer" },SEND_EMAILwith the receipt.
API reference
Workflows are managed through the /workflows API. See the API overview for the full route list — the API exposes endpoints for the workflow itself, its steps, its transitions, and its executions.