Router Node
The Router node inspects event payloads and directs them to different webhooks based on configurable rules. Build conditional branching logic without code — route payment events to one service and subscription events to another, all from a single pipeline.
Overview
A Router evaluates an ordered list of rules against the incoming event payload. Each rule specifies a field path, an operator, a comparison value, and a target webhook. The first matching rule wins: the event is forwarded to that rule's outputWebhookId. If no rule matches, the event goes to the defaultWebhookId (if configured) or is dropped.
On the canvas, Router nodes appear as blue rectangles. They fire during the onEventCreated phase, before primary delivery.
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | — | Friendly label for the router |
| routes | Route[] | [] | Ordered list of routing rules (see below) |
| defaultWebhookId | string | null | Fallback webhook when no route matches |
| inputNodes | { nodeType, nodeId }[] | [] | Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …) |
| isActive | boolean | true | Whether the router is enabled |
Route object
| Field | Type | Description |
|---|---|---|
| field | string | Dot-notation path into the payload (e.g. data.type) |
| operator | string | Comparison operator (see operators table) |
| value | string | Value to compare against |
| outputWebhookId | string | Webhook ID to route matching events to |
Supported operators
| Operator | Description | Example value |
|---|---|---|
| eq | Exact match (string comparison) | payment_intent.succeeded |
| neq | Does not equal | draft |
| contains | String contains substring | error |
| gt | Greater than (numeric) | 1000 |
| lt | Less than (numeric) | 50 |
| exists | Field is present and not null | (no value needed) |
| not_exists | Field is absent or null | (no value needed) |
| in | Value is in comma-separated list | active,pending,processing |
| not_in | Value is not in comma-separated list | cancelled,refunded |
Create a Router
curl -X POST /api/routers \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment Event Router",
"routes": [
{
"field": "type",
"operator": "equals",
"value": "payment_intent.succeeded",
"outputWebhookId": "ep_payments_success"
},
{
"field": "type",
"operator": "equals",
"value": "payment_intent.payment_failed",
"outputWebhookId": "ep_payments_failed"
},
{
"field": "type",
"operator": "contains",
"value": "subscription",
"outputWebhookId": "ep_subscriptions"
}
],
"defaultWebhookId": "ep_catch_all",
"inputNodes": [{ "nodeType": "webhook", "nodeId": "6654a1b2c3d4e5f6a7b8c9d0" }],
"isActive": true
}'Response
{
"_id": "6654d4e5f6a7b8c9d0e1f2a3",
"name": "Payment Event Router",
"routes": [
{
"field": "type",
"operator": "equals",
"value": "payment_intent.succeeded",
"outputWebhookId": "ep_payments_success"
},
{
"field": "type",
"operator": "equals",
"value": "payment_intent.payment_failed",
"outputWebhookId": "ep_payments_failed"
},
{
"field": "type",
"operator": "contains",
"value": "subscription",
"outputWebhookId": "ep_subscriptions"
}
],
"defaultWebhookId": "ep_catch_all",
"inputNodes": [{ "nodeType": "webhook", "nodeId": "6654a1b2c3d4e5f6a7b8c9d0" }],
"isActive": true,
"ownerId": "org_abc123",
"createdAt": "2025-05-01T12:00:00.000Z",
"updatedAt": "2025-05-01T12:00:00.000Z"
}Update a Router
curl -X PATCH /api/routers/6654d4e5f6a7b8c9d0e1f2a3 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"routes": [
{
"field": "type",
"operator": "equals",
"value": "payment_intent.succeeded",
"outputWebhookId": "ep_payments_success"
},
{
"field": "data.object.amount",
"operator": "gt",
"value": "10000",
"outputWebhookId": "ep_high_value"
}
]
}'routes replaces the entire array. Always include all routes you want to keep — omitted routes are removed.Canvas Integration
The Router uses canvas type router and renders in blue. Each route creates an outgoing edge to its target webhook. The default route (if set) creates an additional edge styled with a dashed line.
{
"id": "router-6654d4e5f6a7b8c9d0e1f2a3",
"type": "router",
"position": { "x": 400, "y": 200 },
"data": {
"label": "Payment Event Router",
"routerId": "6654d4e5f6a7b8c9d0e1f2a3",
"routeCount": 3,
"hasDefault": true,
"isActive": true
}
}Payload Examples
Incoming event — matches first route
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_abc123",
"amount": 5000,
"currency": "usd"
}
}
}Result: Event is routed to ep_payments_success because the type field equals payment_intent.succeeded.
Incoming event — no match, uses default
{
"type": "invoice.finalized",
"data": {
"object": {
"id": "inv_xyz789",
"total": 3000
}
}
}Result: No route matches. Event is routed to ep_catch_all (the default webhook).
Use Cases
- Event type routing — Route Stripe events by type to dedicated processing services.
- Priority routing — Send high-value transactions (amount > $100) to a priority queue and low-value ones to standard processing.
- Regional routing — Route events by
data.regionto region-specific webhooks. - A/B testing — Use
regexorinoperators to split traffic across different processing versions. - Error isolation — Route error events to a dedicated error-handling service while normal events go to the main pipeline.
API Reference
| Method | Webhook | Description |
|---|---|---|
| GET | /api/routers | List all routers |
| POST | /api/routers | Create a new router |
| GET | /api/routers/:id | Get a specific router |
| PATCH | /api/routers/:id | Update a router |
| DELETE | /api/routers/:id | Delete a router |
payment_intent.succeeded before a broader contains: payment rule.