Flows
Orbit Flows is a visual workflow builder that lets you design complex communication automations without writing code. Connect triggers, conditions, channels, and AI agents into multi-step workflows using a drag-and-drop interface.What Are Flows?
Flows are directed graphs of nodes that execute in sequence or parallel based on conditions. Each node performs an action — sending a message, calling an API, running an AI agent, or evaluating a condition.Core Concepts
Use Cases
- Welcome series — onboard new users with a multi-step SMS + email sequence
- Appointment reminders — send reminders 24h and 1h before, handle confirmations
- Lead nurturing — qualify leads with AI, route hot leads to sales
- Support escalation — try AI agent first, escalate to human if unresolved
- Order updates — notify customers at each stage of fulfillment
- Survey collection — send surveys post-interaction, analyze responses with AI
Trigger Catalog
Every flow starts from exactly one trigger. The Flow Builder exposes nine trigger types; each maps to the APItrigger_type shown in the right column.
Two more
trigger_type values exist for programmatic starts: manual
(the Start Flow API or the dashboard’s
Run action) and workflow (started by another flow).
Trigger payloads
When a flow starts, everything the trigger supplies becomes the initial execution context. Keys mentioned below are the ones the runtime wires in itself; inbound routings may add more (for example a keyword hit appendskeyword).
Variables anywhere in the flow resolve against this context plus contact
fields, using
{{...}} placeholders — flat keys like {{first_name}}, not
{{contact.first_name}}. A placeholder whose key is absent renders as an
empty string.
Validate a Flow
GET /flows/:id/validate checks a saved flow’s definition for structural
correctness without running it. Run it before activation (and before
forecasting) so obvious graph problems surface at build time:
valid and an errors list. Checks include:
- The definition has at least one node, and node
id+typeare present - A
triggernode exists - No node is disconnected from every edge
abTest/experimentnodes define at least two variants, and any explicit weights sum to 100- No graph cycle that passes through no
delay/wait node (cyclic drip loops that include a wait node are allowed)
valid is true, errors is an empty array. The same checks also run
automatically on POST /flows and PUT /flows/:id.
Node Taxonomy
Action nodes
AI nodes
Logic nodes
Data nodes
An unrecognized node type is skipped at runtime: the executor logs it as
unhandled and takes the node’s default outgoing edge, so a node does
nothing unless its
type matches one the executor implements.
Visual builder vs API-only
Everything in the Trigger Catalog above and the Action / AI / Logic core is in the builder’s palette; the builder page lists them with their config panels in Flow Builder. Two categories are API-only today — define them in a flow’sdefinition.nodes over the API and the
runtime executes them identically:
- Messaging extras —
sendViber,sendLine,sendTelegram,sendInstagram,sendMessenger,sendPush,sendFax - Data + optimization nodes — Functions, Update Record, Send Survey, Send-Time Optimization, A/B Test
Edge Semantics
Each edge connects asource node to a target node and may carry a
sourceHandle that says which exit route it represents. The executor picks
the next node by handle, not by drawing order:
When no handle matches, the executor falls back to the node’s unlabeled
default edge. When that default is absent too, the run ends there. Every
handle-and-fallback decision lands in the step trace the
Executions API returns, so a mis-routed branch is
inspectable per node.
Where Flows Fire
A flow reaches live traffic through one of these entry points:- Inbound routing rules — match an incoming message by keyword or
sender, then start the flow. These rules are configured under the
inbound-routing surface (
sms_inbound_routesfamily); the transport split between inbound and outbound paths is covered in Transport architecture. - Webhooks — an external system POSTs the payload to the flow’s webhook URL.
- Schedule — a cron schedule you attach as
trigger_type: "schedule"withcron_exprand an optionalcron_tz. - Campaigns, contact/audience events — platform events and campaign
sends start
event-triggered flows. - API / dashboard —
manualflows start from the Start Flow call or the dashboard’s Run action.
Create a Flow via API
name, trigger_type (one of manual,
webhook, schedule, event, workflow), and definition — an object
holding the flow graph (nodes, edges, and any trigger detail). For
trigger_type: "schedule" also supply cron_expr (5-field cron) and an
optional cron_tz (IANA timezone, defaults to UTC).
Node shape
Each node has anid, a type, and a data object that carries the
node’s settings — the runtime reads node settings from data, not from
the node’s top level. Edges connect nodes with source and target
(the node ids), plus the optional sourceHandle routing described in
Edge Semantics.
Forecast a Flow Before Activation
POST /flows/:id/simulate dry-runs a flow against an audience before
you activate it. It projects the per-step drop-off funnel, the channel
mix, the predicted send cost, and a conversion estimate — without
enrolling a contact, emitting a send, or wiring a provider. The
projection is pure; it is a POST only because it carries a body. This
is the same call the builder’s Test/Forecast toolbar action uses, so
a what-if in the dashboard and an API forecast return the same shape.
The forecast math walks the flow’s nodes in traversal order and applies
a retention factor at each step: a
sendRetentionRate after send/call
nodes, a delayRetentionRate across delay/sto_wait nodes, and a
branchRetentionRate as the share continuing the primary path at
condition/split/abTest nodes. Defaults are deliberately
conservative (0.99 / 0.9 / 0.5); per-request assumptions
override them. When use_historical applies, a node’s real
step-conversion rate from the analytics funnel (below) overrides the
assumption for that node.
Response (excerpt):
step_retention_rate is the factor applied when leaving
the node, from_history marks whether it came from real history or an
assumption, and sends is the projected send/call count at the node.
Send costs are priced through the tenant’s own configured rates for
the given default_country, per channel (voice calls are multiplied by
avg_call_minutes before pricing). A missing rate yields a zero cost,
never an error. The forecast is a read-only estimate — no ledger row is
written.
Per-Flow Conversion Analytics
GET /flows/:id/analytics returns the post-activation read of a flow:
how many executions entered and completed, a per-node drop-off funnel in
traversal order, and how many contacts converted against the flow’s
goal.
The conversion goal is normally configured on the flow itself as
definition.conversionGoal (set it via PUT /flows/:id), either
{ "type": "event", "event": "order.completed", "windowDays": 7 } or
{ "type": "trait", "trait": "plan", "value": "pro", "windowDays": 7 }.
The query params above override it for an ad-hoc funnel. A contact
converts when it fires the goal event (or has the trait set) within the
window after its execution reaches a terminal state.
reached is the number of executions that traversed
the node, dropped_off the loss versus the previous step,
step_conversion_rate is versus the previous step (null at the entry
step), and overall_conversion_rate is versus the funnel entry. All
counting is a read-side aggregation over the recorded executions — the
executions log itself is untouched.
A/B Test Results
GET /flows/:id/ab-results rolls up every abTest (or experiment)
node in the flow: per-variant assigned, completed, and converted counts
with rates, plus the leading variant. It accepts the same
since/until/window_days/goal-override parameters as
/analytics.
winner_variant_id: null, so the dashboard never declares a false
winner.
How variant assignment works
EachabTest node pins its variant deterministically per contact: a
hash of the contact’s key and the node id picks the bucket, so a contact
that re-enters the flow re-derives the same variant — one sticky subject
per variant, no stored assignment state. Weights come from each
variant’s weight_pct (aliases weight / percentage are accepted);
when no positive weights are set, the split is even. The executor
records the assignment on the node’s own step output
({ ab_test: true, variant_id, variant_label } in the
Executions step trace), and the ab-results rollup
above reads assignments straight back off that trace — which matches
the abTest row in the Edge Semantics table, where
each variant id (a/b, or the variant’s handle id) routes over its
own sourceHandle edge.
Save-time validation (the GET /flows/:id/validate checks above) also
runs on create/update, so a flow with fewer than two variants, or with
explicit weights that do not sum to 100, is rejected before it ever
runs.
By default a variant is sticky for the contact. An abTest node with
data.bandit_mode: true instead routes adaptively: it asks the
real-time decisioning engine on each visit which variant currently
leads (fed by the same assigned/converted counts ab-results reports)
and routes toward that variant. Static validation of the variant shape
still applies.
Conversation Flows for AI Agents
Classic Flows route CONTACTS through channel automations. AI agents get a second, in-conversation map: a conversation flow. Attach one under an agent’sconfig.conversation_flow and every turn routes between NAMED
NODES (per-state instructions) instead of relying on a single static
system prompt — keyword or regex edge conditions against the customer’s
latest message decide which node’s instructions are active, validation
rejects malformed graphs at save, and the route replays deterministically
every turn over the conversation history.
The feature lives in the Agents doc family — see
Conversation flows for graph anatomy,
conditions and fallbacks, the save-validation contract, versioning and
A/B experiment pinning, deterministic replay, and worked billing/triage
and escalation examples.
Next Steps
- Flow Builder — the visual palette for triggers and core nodes
- Flow execution model — execution states, async waits, branching, and version binding at runtime
- Flow Executions — inspect per-node step traces and failures
- Transport architecture — inbound vs outbound transport split
- Webhooks — trigger flows from external events
- AI Agents — add AI agents as flow nodes
- Conversation flows — in-conversation routing for AI agents