Switch switch
Evaluate an expression and route workflow execution to the output port whose case value matches the result. The multi-way branching node for routing on enumerated values without chaining IfCondition nodes.
When to Use
- Route by order status: Direct orders to separate processing paths for
pending,processing,shipped,delivered, andcancelledstates each triggering different notifications and API calls. - Dispatch by webhook event type: Fan out incoming webhook payloads to specialised handlers:
order.created,order.updated,payment.received,refund.requestedeach going to its own processing branch. - Payment method routing: Apply different processing logic for
card,bank_transfer,wallet, andbuy_now_pay_laterpayment instruments — each method has distinct validation and settlement steps. - Locale-specific processing: Route users to region-specific document templates, tax calculations, and compliance checks based on
en-US,en-GB,de-DE, orfr-FRlocale values. - Tier-based feature gating: Gate workflow paths by subscription tier —
free,starter,professional,enterprise— applying different rate limits, quotas, and feature sets to each tier.
Configuration
| Field | Required | Description |
|---|---|---|
Expression |
Required | Template expression that resolves to the string value to match against cases. e.g. {{ $json.status }} or {{ $output.classify.category }}. The resolved value is compared as a string against each case key. |
Cases |
Required | Dictionary mapping match values (strings) to output port names. Each key is a possible resolved value of Expression; each value is the port name that fires when that key is matched. Port names can be any descriptive string — e.g. "handlePending", "sendTracking". |
DefaultPort |
Optional | Port name to fire when no case key matches the resolved expression. Defaults to "default". Always connect this port — unhandled values are a common source of silent data loss in production workflows. |
1 (integer) and "1" (string) both match a case key of "1". Case matching is case-sensitive: "Pending" does NOT match a case key of "pending".
Output Ports
| Port | Fires When |
|---|---|
| <case port name> | The resolved expression value matches this case's key. Exactly one case port fires per execution. |
default (or custom) | No configured case key matches the resolved expression. Always wire this port — it catches new or unexpected values from external systems. |
OutputData Fields
Fields available downstream via $output.<nodeKey>:
| Field | Type | Description |
|---|---|---|
SwitchCase | string | The matched case value — the key from the Cases dictionary that was selected. If the default port fires, this contains the raw unmatched expression result. |
Sample Configuration
Order status routing
{
"Expression": "{{ $json.status }}",
"Cases": {
"pending": "handlePending",
"shipped": "sendTrackingEmail",
"cancelled": "processRefund",
"delivered": "requestReview"
},
"DefaultPort": "unknownStatus"
}
Webhook event dispatcher
{
"Expression": "{{ $json.eventType }}",
"Cases": {
"order.created": "onOrderCreated",
"payment.received": "onPaymentReceived",
"refund.requested": "onRefundRequested"
},
"DefaultPort": "logUnknownEvent"
}
Payment method routing
{
"Expression": "{{ $json.paymentMethod }}",
"Cases": {
"card": "processCard",
"bank_transfer": "processBankTransfer",
"wallet": "processWallet"
},
"DefaultPort": "unsupportedPaymentMethod"
}
Sample Output
Matched Case Port
The input data routes unchanged to whichever case port matches. The _switch metadata indicates which case was matched.
{
"eventType": "payment.failed",
"orderId": "ORD-2025-00123",
"customerId": "cust_007",
"amount": 4500.00,
"currency": "USD",
"failureCode": "card_declined",
"_switch": {
"matchedCase": "payment.failed",
"caseIndex": 2,
"totalCases": 5
}
}
Default Port (no case matched)
{
"eventType": "order.updated",
"orderId": "ORD-2025-00124",
"_switch": {
"matchedCase": null,
"routedToDefault": true,
"totalCases": 5
}
}
Expression Reference
| Expression | Returns |
|---|---|
{{ $output.routeByStatus.SwitchCase }} | The matched case key string, e.g. "shipped". Available on all downstream branches. |
Validation Errors
| Error | Cause |
|---|---|
VAL_MISSING_EXPRESSION | The Expression field is empty or whitespace. |
VAL_MISSING_CASES | The Cases dictionary is null or contains no entries. |
Node Policies and GuardRails
- Always configure a DefaultPort: External systems introduce new enumeration values without warning. An unwired default port means new values silently drop execution. Route the default port to a logging or alerting node at minimum.
- Log unmatched cases: Connect the default branch to a VariableAssignment that captures the raw unmatched value, then send it to a monitoring or alerting node. This surfaces new values from external systems early.
- Use descriptive port names: Name your case ports for their action, not their value —
sendTrackingEmailrather thanshipped. The port name appears in workflow visual diagrams and makes intent obvious. - Prefer Switch over chained IfConditions: For 3 or more branches on a single variable, Switch produces a flat, readable workflow graph. Chained IfConditions create deep nesting that is error-prone to extend.
- Case values are strings: Ensure your expression resolves to the exact string form of your case keys. Numeric values from JSON will be coerced to strings before matching.
Pattern Examples
Pattern 1 — Order Status Fan-Out
A central order processing workflow receives orders in various states. Switch fans each to its dedicated handler, keeping each handler focused on a single state transition.
WebhookTrigger (order event received)
└─► Switch [key: "routeByStatus"]
Expression: {{ $json.orderStatus }}
Cases: { "pending": "notifyWarehouse", "shipped": "sendTracking",
"cancelled": "initiateRefund", "delivered": "requestReview" }
DefaultPort: "logUnknown"
├─► notifyWarehouse ──► SlackNode (alert warehouse team)
├─► sendTracking ──► EmailSmtp (email customer tracking link)
├─► initiateRefund ──► HttpRequest (POST /refunds)
├─► requestReview ──► EmailSmtp (ask for product review)
└─► logUnknown ──► VariableAssignment (capture raw status)
└─► SlackNode (alert engineering)
Pattern 2 — Tier-Based Feature Gate
Gate workflow capabilities by subscription tier, applying different processing rules and quotas to each tier.
FormTrigger (export request submitted)
└─► Switch [key: "routeByTier"]
Expression: {{ $var.userTier }}
Cases: { "free": "limitedExport", "starter": "standardExport",
"professional": "fullExport", "enterprise": "fullExport" }
DefaultPort: "unknownTier"
├─► limitedExport ──► [export max 100 rows, add watermark]
├─► standardExport ──► [export max 10,000 rows]
├─► fullExport ──► [unlimited export, scheduled delivery]
└─► unknownTier ──► StopWorkflow
Pattern 3 — Locale-Specific Document Generation
Generate region-appropriate documents by routing to locale-specific template and tax calculation nodes.
FormTrigger (invoice generation request)
└─► Switch [key: "routeByLocale"]
Expression: {{ $json.locale }}
Cases: { "en-US": "usInvoice", "en-GB": "gbInvoice",
"de-DE": "deInvoice", "fr-FR": "frInvoice" }
DefaultPort: "defaultInvoice"
├─► usInvoice ──► [USD, US tax rules, Letter format]
├─► gbInvoice ──► [GBP, UK VAT, A4 format]
├─► deInvoice ──► [EUR, German MwSt, A4 format]
├─► frInvoice ──► [EUR, French TVA, A4 format]
└─► defaultInvoice ──► [USD, no tax, standard template]