Blocks

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

The switch step in the Inspector 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 like case_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 first mode 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 boolean true matches "true", 5 matches "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.