How to Pass Data Between n8n Nodes: The Complete Guide (2026)
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
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
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.
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.