Skip to main content

Flows

Flows are SendSeven's visual automation engine. You design a journey on a canvas — a graph of nodes connected by edges — and the engine executes it for each contact you start the flow on. A single flow can send a message, wait for a reply, branch on conditions, update tags, hand off to a human agent, call a webhook, or chain into another flow.

Flows are useful for things like:

  • Onboarding sequences — welcome message, wait 1 day, send a tutorial link, branch on whether the link was clicked.
  • Drip campaigns — send 5 educational emails over 2 weeks with branching based on engagement.
  • Bot-style conversations — collect a contact's name and preferences via Telegram/WhatsApp, then route them to the right team.
  • Reactivation flows — for contacts who haven't replied in 30 days, send a discount code and tag the engaged ones.
  • Birthday / anniversary greetings — use the schedule trigger, which supports cron-style and birthday-anchored rules.

Flows execute in the same multi-channel pipeline as the rest of the platform. Sends produce real messages rows, count toward usage-based billing, fire webhooks, and are visible in conversations once a contact replies.

Required scopes

ScopePurpose
flows:readList flows, view definition, view runs and run steps
flows:writeCreate, update, archive flows; start runs from the API
flows:publishMove a flow from draft to published

Flow lifecycle

StatusDescription
draftEditable. Cannot start runs from this status.
publishedActive. New runs can be started; in-flight runs continue executing.
pausedNew runs are blocked. In-flight runs continue running until they finish or are stopped.
archivedHidden from default lists. Cannot start runs.

A flow's definition (nodes + edges + trigger config) is mutable on draft and published flows. In-flight runs use a snapshot of the definition taken when they started, so editing a published flow does not break contacts who are already partway through it. Only new runs see the latest version.

Run lifecycle

StatusDescription
runningEngine is actively executing nodes for this contact.
waitingRun is parked on a delay, a wait-for-reply, a wait-for-click, or a condition recheck.
completedRun reached a terminal state (no outgoing edges from the last node).
failedA node returned an unrecoverable error. The full traceback is stored on flow_runs.error_text.
canceledStopped manually via API, the stop-URL token, or the STOP keyword.

Anatomy of a flow

A flow definition is a JSON document with three top-level keys:

{
"schema_version": 1,
"trigger": {
"kind": "manual",
"event_name": null
},
"entry_node_id": "node_welcome",
"nodes": [
{ "id": "node_welcome", "type": "send", "config": { ... } },
{ "id": "node_wait", "type": "delay", "config": { "duration_seconds": 60 } },
{ "id": "node_followup", "type": "send", "config": { ... } }
],
"edges": [
{ "from": "trigger", "to": "node_welcome", "branch": "default" },
{ "from": "node_welcome", "to": "node_wait", "branch": "default" },
{ "from": "node_wait", "to": "node_followup", "branch": "default" }
]
}

Trigger: how runs are started. See Triggers and the API.

Nodes: the work each step does. See Node reference.

Edges: directed connections between nodes. The branch key specifies which branch of a conditional node the edge belongs to (default, yes, no, or a named branch).

Creating a flow

In the app, Flows → New flow opens a four-step wizard:

  1. Start — begin with a blank canvas or one of the built-in templates.
  2. Basics — name the flow.
  3. Channels — pick the flow's default channels (only channels you have connected are offered).
  4. Trigger — select and configure what starts the flow (see Triggers and the API).

Via the API, the same happens in one call — POST /api/v1/flows with name, definition, and optionally default_channel_chain and default_language.

Default channel chain

Every flow carries a flow-level default_channel_chain — an ordered list of channel types (e.g. ["whatsapp", "telegram", "sms"]) that every Send, Ask-with-Buttons, and Open Conversation node falls back to when it has no chain of its own. The engine walks the chain and delivers on the first channel the contact is reachable on (see Channel persistence).

Choose your channels deliberately. Each channel supports a different feature set (buttons, media, templates, message length). A flow that targets many channels is limited to the intersection of their capabilities, so only include channels you definitely want to support — fewer channels means more rich features are available everywhere.

API behavior on POST /flows:

  • email is never a valid default-chain entry and is silently stripped — email sends are configured per-node.
  • If you omit default_channel_chain and the definition contains interactive nodes, the chain is auto-seeded with all non-email channel types.
  • If you supply a chain that is empty (or email-only) for a flow with interactive nodes, the request is rejected with 400 FLOW_CHANNEL_CHAIN_REQUIRED.

Re-entry policy

When a flow is triggered for a contact who already has an active run, the engine consults the flow's re_entry_policy:

PolicyBehavior
block (default)The new trigger is silently ignored. The existing run continues.
restartThe existing run is canceled. A fresh run starts from the beginning.
parallelA second run is started alongside the existing one. Use sparingly — the engine doesn't deduplicate side effects across parallel runs.

How a run advances

For each contact, the engine processes one node at a time:

  1. Load the run, the snapshotted definition, and the contact.
  2. Execute the current node. The node resolves to one of:
    • continue — advance to the next node immediately.
    • wait — pause on a timer, reply, click, or condition recheck.
    • terminal — mark the run completed.
    • error — mark the run failed and emit flow.run.failed.
  3. Record an audit entry for the step.
  4. If continue, loop back to step 1 with the next node. If wait, the run pauses and resumes automatically when the wait fires.

Each step runs exactly once and every side effect is idempotent, so a run can never double-execute a node even if processing is retried.

Webhooks

Flows emit three webhook events through the standard webhook bus:

  • flow.run.started
  • flow.run.completed
  • flow.run.failed

See Webhooks for the full event schema.