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

FieldTypeDefaultDescription
namestringFriendly label for the merge node
modestringwait_all'wait_all' (wait for all branches) or 'first' (proceed on first arrival)
expectedBranchCountnumber2Number of branches expected before merging (for wait_all mode)
timeoutSecondsnumber300Seconds to wait before timing out missing branches
isActivebooleantrueWhether the merge is enabled
inputNodes{ nodeType, nodeId }[][]Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …)
In 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

bashCreate 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

json201 Created
{
  "_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

bashUpdate Merge Node
curl -X PATCH /api/merge-nodes/6655d0e1f2a3b4c5d6e7f8a9 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "first",
    "timeoutSeconds": 60
  }'
Switching from 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.

jsonCanvas node data
{
  "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:

jsonMerged payload (wait_all)
{
  "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

jsonMerged payload (first)
{
  "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

jsonTimeout payload
{
  "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 first mode 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

MethodWebhookDescription
GET/api/merge-nodesList all merge nodes
POST/api/merge-nodesCreate a new merge node
GET/api/merge-nodes/:idGet a specific merge node
PATCH/api/merge-nodes/:idUpdate a merge node
DELETE/api/merge-nodes/:idDelete a merge node
Set 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.