branchstableBranch on variable
Route to different next steps based on a value the flow already has — for example, the caller's number at flow start, or a result returned by a sub-flow.…
What it does
A pure case dispatcher. Reads a value from somewhere in the run state
(vars.x, event.from, event.to, config.x, or a bare key treated as
vars.key), compares it against each case's expected value using the
selected operator, and jumps to the first case whose nodeId matches.
The cases live at config.cases as an array of
{ path, op, value, nodeId } — the canonical shape shared by every
case-supporting node (see invariant I-3). Each case is rendered as its
own outgoing edge in the canvas so you can wire targets by drag-and-drop.
When no case matches, the worker falls through to node.on.__next — that's
the right-edge out handle on the canvas. Always wire it to a default
target; an unmatched branch with no __next edge logs a warning and
leaves the flow stranded on the node. For "set a value, then immediately
route on it," use the Set / update variable node's built-in cases
instead — it does the write and the dispatch in one step. For digit
menus, use IVR menu which has the same case shape applied to gathered
DTMF.
When to use it
- Route VIP callers based on their inbound number — "calls from area code 212 go straight to the priority queue"
- Branch on a value collected by an upstream HTTP request or Integration call — e.g. send paid customers down one path and trial users down another
- Pick a language path based on
vars.preferredLanguageset by a customScript that read the contact record - Implement business hours by reading a flag a previous customScript
computed (
vars.isOpen→ "true" or "false") and routing accordingly - After a sub-flow returns, branch on whatever the sub-flow stamped
into vars (e.g.
vars.lookupResult) without writing the value again
Configuration
Route to different next steps based on a value the flow already has — for example, the caller's number at flow start, or a result returned by a sub-flow. For a digit menu, use the IVR menu node's built-in cases. For 'set a value, then route on it', use the Set / update variable node's cases. Only reach for this when neither of those fits.
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
cases | Cases (JSON) | json | Optional | {} | Array of { path, op, value, nodeId }. Supported ops: ==, !=, >, >=, <, <=, contains, startsWith, endsWith, matches (regex). |
Outgoing events: __next
Examples
Route VIPs by inbound area code
Inbound calls starting with +1212 (Manhattan) get the VIP path.
Anyone else falls through the right-edge out handle to the standard
queue.
{
"id": "vip-check",
"type": "branch",
"config": {
"cases": [
{ "path": "event.from", "op": "startsWith", "value": "+1212", "nodeId": "vip-queue" }
]
},
"on": { "__next": "standard-queue" }
}
Multi-case dispatch on a stored variable
A previous step wrote vars.tier. Each case targets a different
downstream node; the right-edge fallback catches anything not enumerated.
{
"id": "route-by-tier",
"type": "branch",
"config": {
"cases": [
{ "path": "vars.tier", "op": "==", "value": "gold", "nodeId": "gold-path" },
{ "path": "vars.tier", "op": "==", "value": "silver", "nodeId": "silver-path" },
{ "path": "vars.tier", "op": "matches", "value": "^(trial|free)$", "nodeId": "trial-path" }
]
},
"on": { "__next": "default-path" }
}
Gotchas
- No-match falls through to
on.__next, not to the first case. If you forget to wire the right-edgeouthandle, an unmatched branch logsbranch with no matching case and no fallback edgeand the flow stalls. Always wire a default. The literal'end'is a valid fallback when the right answer is "hang up." - All comparisons are stringified before the operator runs.
Both sides are coerced to strings via
String(value)(withnullbecoming''); numeric ops then coerce back viaNumber(). This meansvars.x === 0matchesvalue: "0"but alsovalue: 0, andnullis indistinguishable from"". For strict numeric comparisons stash the value as a string and use>,>=, etc. - Path supports four prefixes only.
event.from,event.to,vars.<dotted>,config.<dotted>, plus a bare key (treated asvars.key). Anything else returnsundefinedand the comparison silently uses the empty string. Typos likevars.foo.bar.bazon a non-object intermediate also return undefined — the path walker tolerates it instead of throwing. - Cases are evaluated in array order; first match wins. If two cases would both match a value, only the first one fires. Order your cases from most-specific to most-general (e.g. exact-match before regex).
- Invalid regex patterns in the
matchesop evaluate to non-match. This is defensive — a typo in a pattern (unterminated group, bad escape, etc.) won't hang or crash the flow; the case simply returnsfalseand routing falls through toon.__next. The worker now logs awarn-level entry containing the bad pattern, the node id, and the underlying compile error, so authoring mistakes surface in logs instead of failing silently. Grep worker logs forinvalid regex in matches opif amatchescase never fires. - The legacy
varPathfield is honored as an alias forpath. Generators MUST emitpath; saved flows from older versions of the editor may still usevarPathand continue to work. Don't mix them in a single case.
