April 2026
6 min read
Share article

How to Pass Data Between n8n Nodes: The Complete Guide (2026)

How to pass data between n8n nodes

Most n8n debugging time goes into one question: why can my downstream node not see the data I thought I was passing to it? The answer is almost always a misunderstanding of how n8n structures items and how you reference them across nodes. This guide covers the full data model, the reference patterns you actually need, and the fixes for the most common data-flow problems.

The n8n Data Model

Every node in n8n outputs an array of items. Each item is an object with (typically) two properties: json (the data payload) and binary (attached files). When node A connects to node B, B receives A's output array as its input. B then runs once per item by default, transforming each into its own output.

Knowing this shape matters. If node A outputs 5 items, node B runs 5 times. If node A outputs 0 items, node B never runs at all. If the downstream node seems to "skip," check whether the upstream actually produced items.

Referencing the Current Item

Inside an expression on node B, $json refers to the current item being processed. If the incoming item has { name: "Alice" }, then $json.name returns "Alice". This is the most common reference pattern and covers 80 percent of expression use.

Within a Code node, use items[0].json.name to access the first item, or iterate: items.map(item => item.json.name). The Code node sees the full array, not just the current item.

Referencing a Previous Node's Output

When you need data from an earlier node that is not directly connected (or you need data even after a branch), use $node['Node Name']. For example, $node['Webhook'].json.userId reads the userId field from the Webhook node's output regardless of how many nodes are between them.

This works as long as the referenced node actually executed in this workflow run. If the Webhook node was skipped (e.g., it is behind an IF branch that was not taken), the reference returns undefined.

Referencing All Items From a Node

$items('Node Name') returns the full array of items from a specific node. Use this when you need to aggregate or look up across all items, not just the current one. $items('HTTP Request').length gives the count. $items('HTTP Request').filter(i => i.json.status === 'active') filters.

Reference Syntax by Use Case

Current item field: $json.field95% usage
Named node output: $node[Name].json.field78% usage
All items from node: $items(Name)42% usage
Input from immediately upstream: $input35% usage
Code node: items[i].json.field68% usage

Why Your Downstream Node Cannot See the Data

The most common cause: the field name does not match. Expressions are case-sensitive and typo-sensitive. $json.userID is different from $json.userId. Look at the actual output of the upstream node (click on the node, inspect the JSON) and copy the exact field name.

Second most common: the data is nested deeper than you think. API responses often wrap data in an envelope like { data: { user: { name } } }. To get the name, you need $json.data.user.name, not $json.name.

Third: the upstream node output zero items, so the downstream node never ran. Check the node's output count. If it is 0, the issue is upstream.

The Set Node: Your Data-Shaping Tool

The Set node lets you explicitly define the output item structure. Use it to clean up after API calls (flatten nested fields), compute derived fields, or standardize field names before a downstream node. If a field is hard to reference in later nodes, add a Set node right after the source and rename the field to something simple.

Set also has an option to keep only the fields you define (drop everything else). Useful when you want to pass a clean minimal payload to the next node instead of the full original object.

Merging Data From Multiple Branches

When two branches need to combine their outputs, use the Merge node. Merge has modes: append (stack both arrays), combine by position (index-based join), combine by matching field (SQL-style join on a shared key). Choose based on your shape. For joining by ID, use matching field mode and specify the key.

Common mistake: using merge in append mode when you meant to join. You end up with duplicate items instead of combined records. Check the merge output to verify.

Passing Data to Sub-Workflows

When you call a sub-workflow via Execute Workflow, the items you pass in become the input to the sub-workflow's trigger. Inside the sub-workflow, use $json just like you would for any other input. When the sub-workflow returns, its final output becomes the Execute Workflow node's output.

Pass only what the sub-workflow needs, not everything. Fewer fields means cleaner contracts and easier maintenance.

Loop vs Batch Behavior

If you use Split In Batches, the node outputs one batch of items per execution of the loop body. Downstream nodes see that batch as their input. At the end of the loop, Split In Batches outputs all batches combined. Knowing which stage you are in (inside loop vs after loop) tells you what data is available.

After a loop, to access data collected during the loop, reference the node inside the loop: $node['HTTP Request'].json still works if HTTP Request ran inside the loop.

Most Common Data-Flow Bugs

Typo or case mismatch in field name42%
Field nested deeper than expected24%
Upstream node output 0 items14%
Wrong merge mode (append vs join)10%
Referencing node that was not executed10%

Binary Data Handling

Files and images travel in the binary property of each item, not the json property. When passing a file from one node to the next, make sure the downstream node knows which binary property to read (often configurable; default is "data"). If the file disappears between nodes, check the binary property name mismatch.

Preserving Data Through Transforms

Many nodes output only their own response and drop the original input. If you need to pass original context through, add a Set node before the transforming node, save the fields you need into a "context" object, then merge that context back in after the transform. Or use the "Include Other Fields" option that some nodes expose.

Inspecting Node Output

Every n8n node has an output panel showing exactly what it produced. Use this obsessively during debugging. Click the upstream node and look at the JSON tab. The shape you see there is exactly what the downstream node receives as input. If downstream is failing, the bug is almost always visible in the upstream output.

Use the pin feature to lock sample data on a node so you can run downstream nodes without re-running upstream. This is especially useful when upstream nodes are slow or have side effects.

Passing Data to Webhook Responses

When a workflow is triggered by a Webhook and needs to return a response, use the Respond to Webhook node. Whatever data is at $json in that node becomes the HTTP response body. Build the response shape explicitly with a Set node just before Respond to Webhook so you know exactly what the caller will see.

Quick Debugging Checklist

When downstream data is missing: 1) inspect upstream node output, confirm the field exists with exact name, 2) check for nested structure, 3) verify upstream produced at least one item, 4) verify the node you are referencing actually executed in this run, 5) check for typos in $json.fieldName references. 90 percent of data-flow bugs are one of these five.

Community & Training

Join 215+ AI Agency Owners

Get free access to our all-in-one outreach platform, AI content templates, and a community of builders landing clients in days.

Access the Free Sprint
22 people joined this week