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
namestring—Friendly label for the merge node
modestringall'all' (wait for every wired branch) or 'n_of_m' (proceed once requiredCount branches arrived)
requiredCountnumber—For n_of_m: how many branches are enough
timeoutMinutesnumber30Minutes to wait for the missing branches, counted from the FIRST arrival
autoActionOnTimeoutstringfail'fail' (the run is marked failed) or 'continue_partial' (go on with what arrived)
mergeStrategystringreturn_allHow the branches become one payload: 'return_all', 'array' or 'keyed' — see Merge strategy below
returnInputnumber—For return_all: forward only this branch (0 = first to arrive) instead of all of them
correlationKeyFieldstring—Top-level payload field; branches with the same value wait for each other. With 'keyed' it also joins items — see below
isActivebooleantrueWhether the merge is enabled
inputNodes{ nodeType, nodeId }[][]Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …)
ℹIn all mode the merge waits until every wired branch has arrived or the timeout expires. In n_of_m mode it fires as soon as requiredCount branches are in.

Merge strategy

Three ways to turn the branches into the one payload that goes downstream. Pick by what the next node needs, not by the name:

  • Return all as one — the branches side by side (branch_0, branch_1, …), or just one of them when returnInput picks it.
  • Array (flat concat) — every item from every branch in one flat items list. Three rows from one branch and one from another give four items.
  • Keyed (join by key field) — items that share the value of the correlation key field become one object (an inventory row and a price row with the same sku, say); items with no match are kept as they are. When both sides carry a field, the branch that arrived later wins. Without a key field, Keyed falls back to pairing by position: item 0 of every branch becomes one object, item 1 the next — with rows 2, 3, 4 on one side and row 8 on the other you get row 2 + row 8, then rows 3 and 4 untouched.

Correlation key field

A top-level field of the payload, such as orderId. It does two separate things:

  • Which branches wait for each other — with any strategy. Events whose payload carries the same value are merged together. Without the field, branches from the same run (a webhook fanned out by a Split or a Conditional) are grouped by that run, and branches from independent sources — two webhooks wired to the same Merge — pair up in arrival order: each arrival joins the oldest open wait that has not yet heard from its source, so one event per source completes the merge.
  • How items are joined — only with Keyed. Items that share the value are combined into one object; the rest are appended as they are. Array is always the flat list and Return all keeps the branches apart, so neither uses the field for items.
⚠Use a plain field name — no {{ }} and no dots. The branch grouping reads the field literally, so a template or a dotted path never matches and the branches fall back to grouping by run — and with a key configured, independent sources do not pair up by arrival: the key decides what goes together.

Create a Merge Node

bashCreate Merge Node
curl -X POST /api/merge-nodes \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": "<your-workspace-id>",
    "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.