Combine
The Combine step takes two lists and returns one. It either places the second list after the first, or matches records from each on a shared field.
- Use it to
- Append two lists together or join/diff records from two branches on a shared field
- Find it
- Tools → Data → Lists → Combine
- Cost
- 1 Step credit per execution
- Limits
- Requires 2 incoming branches (AND input) · Up to 10,000 records per input list and 100,000 combined
- Output
- {{ N.results }} (combined list, plus $diff when using Changed matches only), {{ N.resultCount }}
- Instead
- Summarize (when grouping or aggregating a single list)
Use it when data is split across two sources, such as contacts in one system and orders in another, or tickets and their service levels.
Setting it up
Connect two branches to it
Combine takes two incoming connections from different steps, one per list. It waits for both branches before running, shown by the AND chip on its input. The canvas refuses a third connection or two connections from the same step. See Merging Parallel Branches.
Choose how to combine
Append lists places the second list’s items after the first list’s. Match fields pairs records that share a value.
Pick the two lists
First list and Second list each read from one connected branch. The branch chosen for the first list is unavailable for the second. A nested path ({{ 2.results.lineItems }}) or piped reference ({{ 2.orders | unnest:lineItems }}) reads that field across every record as one list. You can also paste a JSON array.
Name the field to match on, if you are matching
Field in first list and Field in second list name the match key in each list. The names can differ, such as customer.id and account.externalId.
Deciding what happens to matched and unmatched records
In Match fields mode, Records to keep (unmatched) controls whether Combine merges matches, keeps unmatched records, or compares the two lists for differences.
Two lists sharing a field id:
First list [{"id": 1, "name": "Lovelace", "total": 100}, {"id": 2, "name": "Hopper", "total": 300}, {"id": 3, "name": "Turing", "total": 50}]
Second list [{"id": 1, "total": 100}, {"id": 2, "total": 250}, {"id": 9, "total": 7}]Records 1 and 2 exist in both (record 1 has the same total; record 2 changed from 250 to 300). Record 3 is only in the first list, record 9 only in the second.
| Records to keep | Value | IDs Returned | What comes back |
|---|---|---|---|
| Matches only | inner | 1, 2 | The two matched records, merged. Nothing else. |
| Matches, plus unmatched from both lists | all | 1, 2, 3, 9 | The two merged matches, then {"id": 3, "name": "Turing", "total": 50} and {"id": 9, "total": 7}. |
| Matches, plus unmatched from first list | first | 1, 2, 3 | The two merged matches, then {"id": 3, "name": "Turing", "total": 50}. |
| Matches, plus unmatched from second list | second | 1, 2, 9 | The two merged matches, then {"id": 9, "total": 7}. |
| Only in first list | onlyFirst | 3 | Only first-list records with no match in the second list (put the newer list first to find new records). |
| Only in second list | onlySecond | 9 | Only second-list records with no match in the first list (put the newer list first to find removed records). |
| Changed matches only | changed | 2 | Only matched records whose shared top-level fields differ, plus a $diff field ({"total": {"first": 300, "second": 250}}, read as {{ 5.results.0.$diff.total.second }}). |
| Unchanged matches only | unchanged | 1 | Only matched records whose shared top-level fields are all deeply equal. |
Choose by what a missing or changed partner means. A customer with no orders is still a customer; Matches, plus unmatched from first list keeps them. When comparing two snapshots, put the newer list first. Choose Only in first list for new records or Only in second list for removed records. Choose Changed matches only to inspect field changes in $diff (top-level match fields are excluded).
Field collisions, case, and multiple matches
| Behavior | How Combine handles it |
|---|---|
| When field names collide | Second list wins (default) keeps the second list’s value when both records share a field name; First list wins keeps the first’s. Match fields with different names both stay in the merged record. |
| Ignore letter case | Off by default (ACME and acme do not match). Turn it on when joining codes or emails exported with different casing. |
| One-to-many matches | When one record matches several in the other list, every match is returned as its own merged row (for example, one customer matched against two orders produces two rows, so results can be longer than either input). |
What it passes on
Combine returns an object with two fields: results holds the combined list and resultCount holds its size.
{{ 5.results }}A step after it can iterate over that list, or read a single entry:
{{ 5.results.0.name }}{{ 5.resultCount }} gives the number of records without counting them yourself.
Limits
Each input list can hold up to 10,000 records, and the combined result up to 100,000. Exceeding either ceiling fails the step and names the list. Narrow each branch with Filter before combining, or split the work across runs.
Examples to copy
Attach service levels to incidents
Incidents arrive from a ticketing tool, and client service levels live in a Custom Variables step.
- How to combine: Match fields.
- Field in first list:
clientId. - Field in second list:
clientId. - Records to keep: Matches, plus unmatched from first list.
- Ignore letter case: on.
Matches, plus unmatched from first list keeps incidents whose client has no recorded service level. Ignoring case matches client IDs written with different capitalization.
Put two exports one after the other
Two branches each fetch a page of results, and you want them as one list.
- How to combine: Append lists.
The first list’s items come first, then the second list’s. No field names are involved.
- Run
- Connect two branches producing lists into Combine, select Append lists or Match fields, and click Run step in Test & Debug
- You should see
- The Output tab shows the merged or appended records in results and the total count in resultCount.
What’s Next?
- Filter: split one list into what you keep and what you discard.
- Custom Variables: hold a lookup table for matching against.
- Variable Reference Syntax: read a list entry by position.