Merge Node
The Merge node combines events from parallel branches back into a single flow. Configure it to wait for all branches to complete or proceed as soon as the first one arrives — essential for parallel processing workflows.
Overview
When your pipeline splits into multiple parallel branches (via Router or multiple chains), the Merge node brings them back together. It collects events from its connected branches and, depending on the mode, either waits for all branches to report in (wait_all) or proceeds as soon as the first branch completes (first).
The merged output is a combined payload containing results from all (or the first) branch(es). A configurable timeout ensures the Merge node does not wait indefinitely for missing branches. On the canvas, Merge nodes appear as teal rectangles.
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | — | Friendly label for the merge node |
| mode | string | wait_all | 'wait_all' (wait for all branches) or 'first' (proceed on first arrival) |
| expectedBranchCount | number | 2 | Number of branches expected before merging (for wait_all mode) |
| timeoutSeconds | number | 300 | Seconds to wait before timing out missing branches |
| isActive | boolean | true | Whether the merge is enabled |
| inputNodes | { nodeType, nodeId }[] | [] | Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …) |
wait_all mode, the merge waits until expectedBranchCount events arrive or the timeout expires. In first mode, the merge fires immediately when the first branch event arrives and discards subsequent ones.Create a Merge Node
curl -X POST /api/merge-nodes \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment + Inventory Join",
"mode": "wait_all",
"expectedBranchCount": 2,
"timeoutSeconds": 120,
"isActive": true,
"inputNodes": [{ "nodeType": "webhook", "nodeId": "ep_payment_result" }, { "nodeType": "webhook", "nodeId": "ep_inventory_result" }]
}'Response
{
"_id": "6655d0e1f2a3b4c5d6e7f8a9",
"name": "Payment + Inventory Join",
"mode": "wait_all",
"expectedBranchCount": 2,
"timeoutSeconds": 120,
"isActive": true,
"inputNodes": [{ "nodeType": "webhook", "nodeId": "ep_payment_result" }, { "nodeType": "webhook", "nodeId": "ep_inventory_result" }],
"ownerId": "org_abc123",
"createdAt": "2025-05-01T12:00:00.000Z",
"updatedAt": "2025-05-01T12:00:00.000Z"
}Update a Merge Node
curl -X PATCH /api/merge-nodes/6655d0e1f2a3b4c5d6e7f8a9 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"mode": "first",
"timeoutSeconds": 60
}'wait_all to first mode while events are pending may cause in-flight merges to resolve immediately. Ensure no critical branches are still in progress.Canvas Integration
The Merge node uses canvas type merge-node and renders in teal. It has multiple incoming edges (one per branch) and a single outgoing edge to the downstream node that receives the merged payload.
{
"id": "merge-6655d0e1f2a3b4c5d6e7f8a9",
"type": "merge-node",
"position": { "x": 700, "y": 300 },
"data": {
"label": "Payment + Inventory Join",
"mergeNodeId": "6655d0e1f2a3b4c5d6e7f8a9",
"mode": "wait_all",
"expectedBranchCount": 2,
"isActive": true
}
}Payload Examples
wait_all — merged output
When all branches complete, the Merge node produces a combined payload:
{
"mergeNodeId": "6655d0e1f2a3b4c5d6e7f8a9",
"mode": "wait_all",
"completedAt": "2025-05-01T12:01:30.000Z",
"branches": [
{
"branchIndex": 0,
"webhookId": "ep_payment_result",
"payload": {
"paymentId": "pay_abc123",
"status": "captured",
"amount": 5000
},
"receivedAt": "2025-05-01T12:00:45.000Z"
},
{
"branchIndex": 1,
"webhookId": "ep_inventory_result",
"payload": {
"sku": "WIDGET-001",
"reserved": true,
"warehouse": "us-east-1"
},
"receivedAt": "2025-05-01T12:01:30.000Z"
}
]
}first — immediate output
{
"mergeNodeId": "6655d0e1f2a3b4c5d6e7f8a9",
"mode": "first",
"completedAt": "2025-05-01T12:00:45.000Z",
"branches": [
{
"branchIndex": 0,
"webhookId": "ep_payment_result",
"payload": {
"paymentId": "pay_abc123",
"status": "captured",
"amount": 5000
},
"receivedAt": "2025-05-01T12:00:45.000Z"
}
]
}Timeout scenario
{
"mergeNodeId": "6655d0e1f2a3b4c5d6e7f8a9",
"mode": "wait_all",
"completedAt": "2025-05-01T12:02:00.000Z",
"timedOut": true,
"branches": [
{
"branchIndex": 0,
"webhookId": "ep_payment_result",
"payload": { "paymentId": "pay_abc123", "status": "captured" },
"receivedAt": "2025-05-01T12:00:45.000Z"
}
],
"missingBranches": [
{ "branchIndex": 1, "webhookId": "ep_inventory_result" }
]
}Use Cases
- Split/join pattern — Split an order event into payment and inventory checks, then merge results before confirming the order.
- Fastest response — Query multiple providers in parallel and use
firstmode to proceed with whichever responds first. - Data enrichment — Send to multiple enrichment services in parallel, merge their results, and deliver the enriched payload to the final destination.
- Saga orchestration — Coordinate multi-step transactions where all steps must complete before finalizing.
- Graceful degradation — Use timeouts to proceed with partial results when some branches are slow or unavailable.
API Reference
| Method | Webhook | Description |
|---|---|---|
| GET | /api/merge-nodes | List all merge nodes |
| POST | /api/merge-nodes | Create a new merge node |
| GET | /api/merge-nodes/:id | Get a specific merge node |
| PATCH | /api/merge-nodes/:id | Update a merge node |
| DELETE | /api/merge-nodes/:id | Delete a merge node |
timeoutSeconds to a value that accounts for the slowest expected branch. If a branch is unreliable, consider using first mode with a fallback strategy instead.