Switch
The N-way branch: a list of cases, each an expression and a label, first match wins, and a default port catches the rest. Classify routing and case order.
The N-way branch: a list of cases, each an expression plus a label; the first match routes down that label's port, and the default port catches everything else.
What it does
Switch walks its cases top to bottom, evaluating each expression
against the immediate upstream step's output. The first one that
comes back truthy (anything but nil or false) wins, and the
payload leaves through the port named by that case's label. If no
case matches, the payload leaves through default. Like
if, switch is a transparent router: the payload
is never modified, only directed.
Every case expression is validated when the step is saved, using
the same sandbox as if, so a parse error or a disallowed call
shows on the step before you activate. An expression that fails
at run time (for example comparing a missing field with >)
fails the step with the case index and the expression in the
error message.
Config
One row per case: when this expression is true, route to this label.
The Cases list shows one compact row per case; clicking a row opens the case editor. Each case has:
- When: an expression over
payload(same sandboxed dialect as if):payload["kind"] == "refund". - Route to: a free-form label (
refund,vip,spam) that becomes one of this step's output ports. New rows get a placeholder label likecase_3; rename it to something a reader of the canvas understands. The editor marks a label as wired once an edge leaves that port.
Cases evaluate in order; first match wins. No match → the
default port. You never write a case for default; it's always
there.
Under the hood cases is stored as a JSON array of
{ "expr": …, "label": … } objects, which is also what the
AI builder and the MCP tools write.
Output
Like if, switch passes the payload through unchanged on the port that fired. With these cases
[
{ "expr": "payload[\"kind\"] == \"refund\"", "label": "refund" },
{ "expr": "payload[\"amount\"] > 500", "label": "large" }
]
the payload { "kind": "order", "amount": 900 } leaves through
large, { "kind": "refund", "amount": 900 } leaves through
refund (first match), and { "kind": "order", "amount": 20 }
leaves through default. Downstream steps on every port keep
reading {{ payload.kind }} and {{ payload.amount }} as if the
switch weren't there.
Patterns
- After a classify: an AI classify
picks a label, and the switch routes on
payload["data"]["label"] == "refund", one case per label. - Tiering: score ranges to different handling lanes, ordered highest first (first match wins does the range logic for you).
- Event fan-out: a webhook in app-event
mode receives every Slack event; a switch on
payload["event_type"]sends mentions, reactions and DMs down their own lanes. - Rejoin: each lane does its own work, then a
merge in
firstmode brings the survivor back to a shared tail.
Gotchas
- Wire every case port and the default. A case without an outgoing edge silently drops the payloads that match it, and the default is where typos and unexpected values land, so give it at least a notification.
- Order matters: put the most specific case first. A broad first case shadows everything below it.
- Labels are port ids. Renaming a label after wiring leaves the old edge pointing at a port that no longer exists; reconnect it.
==is loose (a booleantruematches"true",5matches"5"); use===when you need strict equality.
Related
- If for a plain yes/no.
- Merge to bring the lanes back together.
- AI classify for a label a switch can route on.