Solaris AISolaris AI FlowDocs
Node ReferenceUtility

Merge

Combine outputs from incoming workflow branches into one payload.

View as Markdown

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

FieldTypeRequiredDescription
Node LabelstringNoDisplay name shown on the canvas
Response NamestringRecommendedVariable name used by downstream nodes. Defaults to mergeResponse
ModeselectRecommendedHow incoming branches are combined. Defaults to Keep each branch separate if unset
Match FieldstringOnly for Match list items by fieldField path used to join list items, for example address or data.mint
Per-Item CombineselectFor list matching modesWhether matched items are flattened together or kept under each branch name

Downstream nodes read the merged value under .result:

{mergeResponse.result}

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

{mergedPrices.result}

Modes

Mode in the editorBest forWhat it does
Keep each branch separateSafest default; multiple API responsesStores each branch under its response name
Combine matching fieldsObject outputs with different field namesSpreads all incoming object fields into one object. Later branches overwrite earlier branches on key conflicts
Stack lists togetherMultiple arraysConcatenates arrays end-to-end
Match list items by positionArrays already in the same orderCombines item 1 with item 1, item 2 with item 2, and so on. Output length is the shortest input list
Match list items by fieldArrays that share an ID fieldInner-joins items where every branch has the same value at the match field. Output follows the first branch's order

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

Example workflow:

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

Set the Birdeye nodes' response names to something readable:

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

With Keep each branch separate, Merge outputs:

{
  "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:

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 requires every incoming branch to output an object. It spreads the fields into one object.

Example inputs:

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

Merged value inside result:

{
  "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 requires every incoming branch to output an array. It appends all items into one list.

Example inputs:

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

Merged value inside result:

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

Reference the first item downstream with:

{mergeResponse.result[0].symbol}

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 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 requires every incoming branch to output an array. It combines items with the same index.

Example inputs:

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

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

[
  { "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 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:

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

Example inputs:

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

Merged value inside result:

[
  { "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:

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

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

OptionOutput shapeUse 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

A successful Merge node returns:

{
  "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:

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

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

FieldModesWhat it tells you
inputsAllOne entry per incoming branch: { responseName, type, length? }
outputLengthStack lists, Match by position, Match by fieldNumber of items in the merged list
lengthMismatchMatch list items by positiontrue when input arrays were not all the same length
autoNamespacedIndicesMatch list items by positionRow indices that auto-fell-back to nested shape because a row had a non-object item
droppedFromInputsMatch list items by fieldOne count per branch - items that did not survive to the merged output (missing key, no match, or a sibling branch lacked the key)
duplicateKeysPerInputMatch list items by fieldOne count per branch - items skipped because their join key was already seen in that branch (first-write-wins)
keyMatch list items by fieldEcho of the resolved match field
unwrappedFromEnvelopeStack lists, Match by position, Match by fieldOne 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

  • 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

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

  • Transform - reshape branch outputs before merging
  • Code - write custom combine logic
  • Condition - route branches before they merge

On this page