A triggered flow enrolls contacts when they match a trigger and moves them through a graph of steps — sending emails, waiting, branching, updating fields. See the Triggered Flows guide for the concepts; this page covers the API surface.Flow status is draft, active, paused, or archived. Structure (triggers, steps, reentry policy) is only editable while the flow is a draft or paused — pause an active flow before changing it, then reactivate.
Drafts may be incomplete. Step and trigger format is validated on every write, but completeness (a send_email step without an email, a trigger without its list) is only enforced when you activate. Activation failures return the missing pieces.
A flow can have up to 25 triggers; a contact entering via any of them joins the flow, subject to the reentry policy. Each trigger is an object discriminated on type:
Type
Configuration
added_to_static_list
list_id — fires when a contact is added to that static list
removed_from_static_list
list_id — fires when a contact is removed from that static list
contact_created
None — fires for every newly created contact
contact_updated
field (standard camelCase key or custom field api_name), optional operator + value to filter on the new value. changed fires on any change
date_based
field (createdAt or an active custom date field), mode (on_date, before_date + days_before, or after_date + days_after), optional include_anniversaries, trigger_time ("HH:00" or "HH:30", default "09:00"), timezone (IANA, default "UTC")
form_submitted
tent_id + form_id — fires when a contact submits that form
Steps form a graph. Every step needs a unique step_id (1-120 characters) and links to the next step through edge fields — next_step_id for linear steps, or true_step_id / false_step_id / default_step_id on condition steps. Omitting the edge ends the flow after that step. An optional label names the step in the UI and member history.
Type
Configuration
send_email
email_id (must be approved before activation), optional operational flag
wait
Exactly one mode: duration_amount + duration_unit (minutes, hours, days, weeks), until_date_field (a contact date field), or condition (a rule tree re-evaluated periodically)
update_contact
updates — an array of {field, value, write_mode?} assignments (overwrite or skip_if_filled), or a single field + value shorthand
add_to_list
list_id — must be a static list
remove_from_list
list_id — must be a static list
condition_check
condition — a rule tree; branches via true_step_id / false_step_id / default_step_id
create_tent
source (scratch or template + template_id), prompt, auto_publish, custom_page_alias, tent_name. prompt, alias, and name support {{contact.*}} merge tokens
send_webhook
endpoint_url (public http(s) only), method (post default, put, get), auth_type (none or jwt with token or secret_arn), custom_fields merged into the payload
drop_from_flow
None — terminal step that removes the member immediately
condition_check and condition-mode wait steps take the same rule tree as blast audiences, plus one extra source that is only legal inside flows: email_step, which tests engagement with an earlier send_email step.
PUT /v1/email-flows/{flowId}/steps # replace the full listPOST /v1/email-flows/{flowId}/steps # insert one stepPATCH /v1/email-flows/{flowId}/steps/{stepId} # merge a partial updateDELETE /v1/email-flows/{flowId}/steps/{stepId} # delete one step
PUT takes {"steps": [...]} and replaces the whole list. POST inserts one step, wrapped in a step key, plus at most one placement selector — position (start or end, default end), before_step_id, or after_step_id:
Inserting does not rewrite edges — set the *_step_id fields yourself to wire the step in. Duplicate step IDs return 409 Conflict with error_code: "campaign_flow_duplicate_step_id".PATCH also wraps the partial update in a step key; only the provided fields are merged onto the existing step, and step_id cannot be changed:
Only valid for send_email steps. Takes the same body as a blast’s Set the Email: {"source": "existing", "email_id": "..."} or an inline email seeded from a template_id, your own html, or a blank branded scaffold. Inline emails start as drafts and must be approved before the flow can activate — the created email_id appears on the step in the response.
POST /v1/email-flows/{flowId}/activatePOST /v1/email-flows/{flowId}/pausePOST /v1/email-flows/{flowId}/archive
Activation validates the whole flow. If anything is incomplete, the API returns 400 Bad Request with error_code: "campaign_flow_not_ready" and the missing fields:
Pausing stops new work without dropping members — they resume when you reactivate. Active flows must be paused before archiving or deleting. Triggers fire on new events only; contacts already on a list do not enter an added_to_static_list flow retroactively.
POST /v1/email-flows/{flowId}/contacts/{contactId}
Manually enrolls a contact into an active flow. Optionally pass per-member context that emails and webhooks in this run can reference as {{campaign.dynamic.<key>}} merge tokens:
{"dynamic_context": {"couponCode": "SAVE20"}}
Enrollment respects the flow’s rules: 409 Conflict if the contact is already in the flow (campaign_flow_already_in_flow) or blocked by no_reentry (campaign_flow_reentry_denied), and 400 Bad Request if disqualification rules match (campaign_flow_contact_disqualified).201 Created
GET /v1/email-flows/{flowId}/members/{membershipId}
Returns one member’s full run: status, current position, failure info, and a timeline of events (entered flow, step executed, email sent/delivered/opened/clicked, dropped) with timestamps.
Takes the same shape as listing blasts: status (draft, active, paused, archived, or any), archived, sort_by (updated_at, created_at, name), sort_order, page, limit, and search. Responses contain flows and a pagination object. Each flow includes aggregate details — total_entered, active_count, waiting_count, completed_count, dropped_count, and most_recent_entry_at.
PATCH /v1/email-flows/{flowId} patches name, description, reentry_policy, triggers, steps, disqualification_rules, or settings. triggers and steps are full replacements — use the step endpoints for incremental edits. Setting disqualification_rules on an active flow immediately drops current members that match.
POST /v1/email-flows/{flowId}/archive archives a non-active flow.
DELETE /v1/email-flows/{flowId} deletes it and returns {"flow_id": "...", "deleted": true}. Active flows must be paused first.