> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ingestly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Merge

> Combine data from multiple pipeline branches into a single output.

The merge node combines data from multiple upstream branches into a single output. Use it to bring together results from parallel processing paths, for example merging an extraction result with data fetched by an HTTP action.

## When to use merge

* Your graph **fans out into parallel branches** (one node feeding several) and you need to combine them before delivery. Use **Key match** when each branch produces records that share an identifier, **Concat** when you just want the lists end-to-end, **Zip** when items pair by position.
* Your graph takes **either one branch or another** (a [classify](/nodes/classify) sends the document down one path) and you need to continue on a single node afterwards. Turn off **Require all sources** so the merge runs on the branch that arrived.
* You **cut a document up** with [split](/nodes/split) and need to reassemble the per-group outputs. Split's groups become child runs rather than branches in this run, so gather them with **Collect**, not Key match, Concat or Zip.
* You enriched data via an [HTTP action](/nodes/http-action) and need to merge the response back with the original payload. Use **Key match**.
* Skip merge when only one branch reaches the output node; merge expects multiple sources.

## Require all sources

**Key match**, **Concat** and **Zip** show a **Require all sources** switch, on by default. It controls what happens when a connected branch does not deliver.

* **On:** the merge waits for every connected source and runs only once all of them arrive. A branch that was skipped counts as not arrived, and so does a branch that ran and failed. Either way the merge is skipped, and every node downstream of it is skipped with it. Read the merge step's skip reason in the run detail: it names the branches whose input did not arrive. The terminal status differs between the two cases. If the missing branch was skipped, nothing failed, so the run still finishes as completed even though the outputs downstream of the merge never ran. If the branch ran and failed, and no failure edge handles that failure, the run finishes as failed.
* **Off:** the merge runs on whatever arrived, and sources whose branch was skipped or failed are left out of the result. This is the setting that makes either/or joins work, for example a classify sending a document to branch A or branch B and a merge joining the two back into one path. The merge is still skipped when nothing arrived at all.

Turning the switch off changes only this node. Every other node keeps waiting for all of its inputs.

## Merge modes

Select a merge mode to control how data from multiple sources is combined.

### Key match

Match items across sources by a shared property and merge their fields into combined objects.

For example, if source A produces `[{ lineNum: 1, text: "Hello" }]` and source B produces `[{ lineNum: 1, confidence: 0.95 }]`, key matching on `lineNum` produces `[{ lineNum: 1, text: "Hello", confidence: 0.95 }]`.

Add one entry per pair of arrays under **Merge Sources**.

**Configuration:**

| Field            | Type       | Required | Description                                                                                                                                            |
| ---------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Source Array** | expression | Yes      | The array to merge into, for example `{{extract1.payload.lines}}`                                                                                      |
| **Merge Array**  | expression | Yes      | The array from another node to merge in                                                                                                                |
| **Key**          | string     | Yes      | Property present in both arrays. Items with the same key value merge into one object                                                                   |
| **Target Path**  | string     | No       | Where the merged array lands in the output. Defaults to the last segment of **Source Array**, so `{{extract1.payload.lines}}` lands at `payload.lines` |

Two entries that resolve to the same target path are rejected when you save the pipeline. Name at least one of them explicitly.

### Concat

Append outputs from multiple sources end-to-end into a single list.

For example, if source A produces `[{ line: 1 }, { line: 2 }]` and source B produces `[{ line: 3 }]`, concat produces `[{ line: 1 }, { line: 2 }, { line: 3 }]`.

**Configuration:**

| Field               | Type            | Required | Description                                               |
| ------------------- | --------------- | -------- | --------------------------------------------------------- |
| **Sources**         | expression list | Yes      | The arrays or values to concatenate, in order             |
| **Target Path**     | string          | No       | Where the concatenated array lands in the output          |
| **Deduplicate**     | switch          | No       | Remove duplicate items from the result                    |
| **Deduplicate Key** | string          | No       | Deduplicate by this property instead of by the whole item |

### Zip

Pair items by index position from multiple sources into combined objects.

For example, if source A produces `[{ page: 1 }, { page: 2 }]` and source B produces `[{ text: "Hi" }, { text: "Bye" }]`, zip produces `[{ page: 1, text: "Hi" }, { page: 2, text: "Bye" }]`.

**Configuration:**

| Field              | Type            | Required | Description                                                                                                        |
| ------------------ | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| **Sources**        | expression list | Yes      | The arrays to pair by index position (at least two)                                                                |
| **Unequal Length** | select          | Yes      | What to do when the sources hold different numbers of items: **Truncate to Shortest**, **Include All** or **Fail** |
| **Target Path**    | string          | No       | Where the zipped array lands in the output                                                                         |

**Include All** keeps every index, and a source that ran out of items contributes no properties at those positions. It does not pad them with nulls.

<Warning>Zip works on arrays of objects. A source whose items are scalars (strings, numbers, booleans) fails the step with an error naming the source and the index. Earlier releases accepted such a source and emitted empty objects like `[{}, {}]`. Wrap the scalars in objects first, for example with a [transform](/nodes/transform) node.</Warning>

### Collect

Gather outputs from child runs (created by a split or classify node) into one array. Use this when a split node cuts a document into multiple child runs and you need to reassemble the results.

Connect the [split](/nodes/split) or [classify](/nodes/classify) node directly to the merge node on the canvas. The edge is saved as a post-children join automatically, so the merge runs once in the parent run after every child run has finished. There is no edge setting to pick. Two rules apply:

* The merge must be in **Collect** mode. In any other mode the connection is rejected, because the merge resolves its sources from expressions and the edge would only order the nodes.
* The upstream node must actually create child runs. A classify using the AI engine answers inline and creates none, so that connection is rejected.

Set **Source Node Name** to the name of a node inside the child pipeline, and each child run's output for that node is collected in the chosen sort order.

Collect can also gather a node's per-item outputs from inside a [loop](/nodes/loop). Set **Source Node Name** to the name of a node within the loop body, and merge concatenates that node's per-item results, in item order, into a single array. This runs after the loop completes and complements the split child run case.

**Configuration:**

| Field                | Type   | Required | Description                                                                                                                                                                                                     |
| -------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Source Node Name** | string | Yes      | The node inside the child pipeline or the loop body whose outputs to collect                                                                                                                                    |
| **Source Path**      | string | No       | Dot-separated path within that node's payload. For a payload of `{ data: { lines: [...] } }`, use `data.lines` to collect just the lines                                                                        |
| **Sort Order**       | select | No       | How the collected outputs are ordered: **Chunk Index** follows the order the split produced the child runs, **Chunk Label** sorts by each child's label. Ignored for a loop body, which is always in item order |
| **Target Path**      | string | No       | Where the collected array lands in the output                                                                                                                                                                   |

## Inputs and outputs

**Allowed inputs:** Extract, HTTP action, Loop, Variable, Store, Prompt, Validate, Transform, Review, and another Merge. Split and Classify can connect only to a merge in **Collect** mode. Supports multiple input connections.

**Output:** Combined data object from all configured sources.

## Related

<CardGroup cols={2}>
  <Card title="Split action" icon="scissors" href="/nodes/split">
    Split data into branches before merging
  </Card>

  <Card title="Extract action" icon="sparkles" href="/nodes/extract">
    Extract data before merging
  </Card>

  <Card title="Transform action" icon="arrows-rotate" href="/nodes/transform">
    Transform merged data into a specific format
  </Card>

  <Card title="Review action" icon="user-check" href="/nodes/review">
    Review merged data before delivery
  </Card>
</CardGroup>
