# Merge (/docs/nodes/utility/merge)

Combine outputs from incoming workflow branches into one payload.



The Merge node combines the outputs from incoming workflow branches and passes one merged payload downstream. It is most useful with two or more branches; a single incoming branch is allowed, but the output still uses the selected Merge mode and the `{ success, mode, inputCount, result, meta }` envelope. Use it when parallel nodes produce related data and the next node needs to read everything from one place.

Common uses:

* Keep multiple API responses together without overwriting fields
* Combine several lists into one list
* Pair list items by position
* Join list items by a shared field such as `address`, `mint`, or `symbol`

Merge is **edge-driven**: it reads the nodes connected directly into it. You do not write template expressions in the Merge node itself.

Configuration [#configuration]

| Field            | Type   | Required                               | Description                                                                            |
| ---------------- | ------ | -------------------------------------- | -------------------------------------------------------------------------------------- |
| Node Label       | string | No                                     | Display name shown on the canvas                                                       |
| Response Name    | string | Recommended                            | Variable name used by downstream nodes. Defaults to `mergeResponse`                    |
| Mode             | select | Recommended                            | How incoming branches are combined. Defaults to **Keep each branch separate** if unset |
| Match Field      | string | Only for **Match list items by field** | Field path used to join list items, for example `address` or `data.mint`               |
| Per-Item Combine | select | For list matching modes                | Whether matched items are flattened together or kept under each branch name            |

Downstream nodes read the merged value under `.result`:

```text
{mergeResponse.result}
```

If you change **Response Name** to `mergedPrices`, downstream nodes use:

```text
{mergedPrices.result}
```

Modes [#modes]

| Mode in the editor               | Best for                                  | What it does                                                                                                        |
| -------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Keep each branch separate**    | Safest default; multiple API responses    | Stores each branch under its response name                                                                          |
| **Combine matching fields**      | Object outputs with different field names | Spreads all incoming object fields into one object. Later branches overwrite earlier branches on key conflicts      |
| **Stack lists together**         | Multiple arrays                           | Concatenates arrays end-to-end                                                                                      |
| **Match list items by position** | Arrays already in the same order          | Combines item 1 with item 1, item 2 with item 2, and so on. Output length is the shortest input list                |
| **Match list items by field**    | Arrays that share an ID field             | Inner-joins items where every branch has the same value at the match field. Output follows the first branch's order |

Recommended default: keep each branch separate [#recommended-default-keep-each-branch-separate]

For most users, especially when connecting integration nodes like Birdeye, use **Keep each branch separate**.

Example workflow:

```text
Manual Trigger → Birdeye SOL price ┐
                                   ├→ Merge → Telegram
Manual Trigger → Birdeye BONK price┘
```

Set the Birdeye nodes' response names to something readable:

```text
Birdeye SOL price  → solPrice
Birdeye BONK price → bonkPrice
Merge              → mergeResponse
```

With **Keep each branch separate**, Merge outputs:

```json
{
  "success": true,
  "mode": "namespacedObject",
  "inputCount": 2,
  "result": {
    "solPrice": {
      "success": true,
      "operation": "getPrice",
      "address": "So111...",
      "data": { "value": 142.31 }
    },
    "bonkPrice": {
      "success": true,
      "operation": "getPrice",
      "address": "DezX...",
      "data": { "value": 0.000018 }
    }
  },
  "meta": {
    "inputs": [
      { "responseName": "solPrice", "type": "object" },
      { "responseName": "bonkPrice", "type": "object" }
    ]
  }
}
```

A downstream Telegram, Discord, AI, Transform, Code, or Condition node can reference:

```text
SOL: {mergeResponse.result.solPrice.data.value}
BONK: {mergeResponse.result.bonkPrice.data.value}
```

If two upstream nodes have the same response name, Merge keeps both by adding suffixes such as `birdeyeResponse` and `birdeyeResponse_2`. That works, but readable response names such as `solPrice` and `bonkPrice` are easier to use.

Combine matching fields [#combine-matching-fields]

**Combine matching fields** requires every incoming branch to output an object. It spreads the fields into one object.

Example inputs:

```json
{ "symbol": "SOL", "price": 142.31 }
{ "volume24h": 1000000, "liquidity": 500000 }
```

Merged value inside `result`:

```json
{
  "symbol": "SOL",
  "price": 142.31,
  "volume24h": 1000000,
  "liquidity": 500000
}
```

Be careful with raw integration outputs. Many integration nodes return similar wrapper fields such as `success`, `operation`, and `data`. If two branches both have `data`, the later branch overwrites the earlier branch's `data`. Use **Keep each branch separate** when you need to preserve both full responses.

Stack lists together [#stack-lists-together]

**Stack lists together** requires every incoming branch to output an array. It appends all items into one list.

Example inputs:

```json
[
  { "symbol": "SOL" },
  { "symbol": "BONK" }
]
```

```json
[
  { "symbol": "JUP" }
]
```

Merged value inside `result`:

```json
[
  { "symbol": "SOL" },
  { "symbol": "BONK" },
  { "symbol": "JUP" }
]
```

Reference the first item downstream with:

```text
{mergeResponse.result[0].symbol}
```

Envelope auto-unwrap [#envelope-auto-unwrap]

The three list modes - **Stack lists together**, **Match list items by position**, and **Match list items by field** - accept the standard executor envelopes around an array directly:

* `{ success: true, data: [...] }` (Transform, Code, AI nodes, most integrations)
* `{ success: true, result: [...] }` (For Each End, Merge itself)

Merge sees through the wrapper to the inner array, so a Transform or Code node in between is no longer required. The `meta.unwrappedFromEnvelope` field on Merge's output records which incoming branches were auto-unwrapped, so the behavior stays visible.

You still need a [Transform](/docs/nodes/utility/transform) step when the array is nested deeper, for example to pull `data.tokens` out of `{ success, data: { tokens: [...] } }`. Auto-unwrap is intentionally one level - anything further is a real reshape that belongs in Transform.

Match list items by position [#match-list-items-by-position]

**Match list items by position** requires every incoming branch to output an array. It combines items with the same index.

Example inputs:

```json
[
  { "address": "A", "symbol": "AAA" },
  { "address": "B", "symbol": "BBB" }
]
```

```json
[
  { "price": 1.25 },
  { "price": 2.5 }
]
```

Merged value inside `result` with **Per-Item Combine** set to **Combine fields**:

```json
[
  { "address": "A", "symbol": "AAA", "price": 1.25 },
  { "address": "B", "symbol": "BBB", "price": 2.5 }
]
```

The output length is the shortest input list. If one list has 10 items and another has 8, the merged result has 8 items.

Use this only when the lists are already in the same order.

Match list items by field [#match-list-items-by-field]

**Match list items by field** requires every incoming branch to output an array of objects. It joins items where all branches share the same value at the configured **Match Field**.

Example configuration:

```text
Mode: Match list items by field
Match Field: address
Per-Item Combine: Combine fields
```

Example inputs:

```json
[
  { "address": "A", "symbol": "AAA" },
  { "address": "B", "symbol": "BBB" }
]
```

```json
[
  { "address": "B", "price": 2.5 },
  { "address": "A", "price": 1.25 }
]
```

Merged value inside `result`:

```json
[
  { "address": "A", "symbol": "AAA", "price": 1.25 },
  { "address": "B", "symbol": "BBB", "price": 2.5 }
]
```

The output follows the order of the first incoming branch. Items without a match in every branch are dropped and counted in `meta.droppedFromInputs` (one count per incoming branch). Duplicate join keys are counted in `meta.duplicateKeysPerInput` so you can debug unexpected drops.

Match fields can use dotted paths:

```text
address
data.mint
token.id
```

Field paths cannot be empty, start or end with `.`, contain `..`, or use reserved segments such as `__proto__`, `constructor`, or `prototype`.

Per-item combine [#per-item-combine]

For **Match list items by position** and **Match list items by field**, choose how matched items become one row:

| Option             | Output shape                               | Use when                                                                           |
| ------------------ | ------------------------------------------ | ---------------------------------------------------------------------------------- |
| **Combine fields** | `{ "symbol": "SOL", "price": 142 }`        | Fields do not conflict, or you are okay with later fields overwriting earlier ones |
| **Keep separate**  | `{ "tokens": { ... }, "prices": { ... } }` | You need to preserve each branch exactly                                           |

If you choose **Keep separate**, each matched item is nested under the upstream branch's response name. If **Match list items by position** is set to **Combine fields** but a row contains a non-object item, that row automatically falls back to the same nested shape so the run does not silently drop the value. The fallback row indices are recorded in `meta.autoNamespacedIndices`.

**Match list items by field** does not fall back: if any array element is not an object, the node fails with a clear error so you can fix the upstream shape.

Output [#output]

A successful Merge node returns:

```json
{
  "success": true,
  "mode": "namespacedObject",
  "inputCount": 2,
  "result": {},
  "meta": {
    "inputs": [
      { "responseName": "branchA", "type": "object" },
      { "responseName": "branchB", "type": "array", "length": 12 }
    ]
  }
}
```

Downstream nodes normally read from `.result`:

```text
{mergeResponse.result}
{mergeResponse.result.solPrice.data.value}
{mergeResponse.result[0].address}
```

The `meta` object is for debugging. The exact fields depend on the mode:

| Field                   | Modes                                          | What it tells you                                                                                                                                                          |
| ----------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inputs`                | All                                            | One entry per incoming branch: `{ responseName, type, length? }`                                                                                                           |
| `outputLength`          | Stack lists, Match by position, Match by field | Number of items in the merged list                                                                                                                                         |
| `lengthMismatch`        | Match list items by position                   | `true` when input arrays were not all the same length                                                                                                                      |
| `autoNamespacedIndices` | Match list items by position                   | Row indices that auto-fell-back to nested shape because a row had a non-object item                                                                                        |
| `droppedFromInputs`     | Match list items by field                      | One count per branch - items that did not survive to the merged output (missing key, no match, or a sibling branch lacked the key)                                         |
| `duplicateKeysPerInput` | Match list items by field                      | One count per branch - items skipped because their join key was already seen in that branch (first-write-wins)                                                             |
| `key`                   | Match list items by field                      | Echo of the resolved match field                                                                                                                                           |
| `unwrappedFromEnvelope` | Stack lists, Match by position, Match by field | One entry per branch whose `{ success, data \| result: [...] }` envelope was auto-unwrapped to the inner array - `{ responseName, from }`. Omitted when no unwrap occurred |

Limits and safety [#limits-and-safety]

* Merge needs at least one incoming branch. One branch is valid, but two or more branches are recommended for real merging.
* Duplicate edges from the same source node count as one input.
* Routing-skipped branches are not merged.
* Mode-specific type checks are strict. Array modes fail if an incoming branch is neither an array nor a `{ success: true, data | result: [...] }` envelope around one. Unknown values for **Mode** or **Per-Item Combine** fail the run rather than falling back silently.
* **Keep each branch separate** and namespaced per-item modes require safe response names: start with a letter or digit and contain only letters, digits, `_`, or `-`.
* Avoid response names that start with `_` or `$`, contain `.`, or equal `__proto__`, `constructor`, or `prototype`.
* Merge errors are treated as failed node runs, not soft-success responses.

When to use Merge vs Transform vs Code [#when-to-use-merge-vs-transform-vs-code]

Use **Merge** when you have incoming branches and want one downstream node to read their outputs together. It is designed for two or more branches; with one branch, the output still follows the selected Merge mode.

Use **Transform** before Merge when each branch needs to extract or reshape data first, for example turning `{ success, data: { tokens: [...] } }` into a bare token array.

Use **Code** when the combine logic needs custom JavaScript, complex grouping, custom scoring, or behavior that does not fit the Merge modes.

Next steps [#next-steps]

* [Transform](/docs/nodes/utility/transform) - reshape branch outputs before merging
* [Code](/docs/nodes/utility/code) - write custom combine logic
* [Condition](/docs/nodes/utility/condition) - route branches before they merge
