{{variablePath}}{
"id": "sendEmail",
"parameters": {
"to": "{{getUserProfile.item.email}}",
"subject": "Welcome {{getUserProfile.item.name}}!"
}
}{{getUserProfile.item.email}} is replaced with the actual email from the getUserProfile node's output.{{variablePath}}{
"message": "{{previousNode.item.message}}",
"count": "{{previousNode.all.length}}",
"mixed": "Hello {{user.name}}, you have {{notifications.count}} new messages"
}{
"parameters": {
"email": "{{user.item.email}}", // Text field
"count": "{{users.count()}}", // Number field
"data": "{{previousNode.item}}", // Object/Array field
"url": "https://api.com/{{user.item.id}}" // Part of a string
}
}input to access data from the node that just executed before the current node.{{input.item}} - Current Item// Previous node returned: [
// { "id": 1, "name": "Alice" },
// { "id": 2, "name": "Bob" }
// ]
{
"id": "sendEmail",
"parameters": {
"to": "{{input.item.email}}", // Gets email of current item
"name": "{{input.item.name}}" // Gets name of current item
}
}
// First run: to = "alice@example.com", name = "Alice"
// Second run: to = "bob@example.com", name = "Bob"{{input.all}} - All Items// Previous node returned: [
// { "score": 85 },
// { "score": 92 }
// ]
{
"id": "aggregate",
"parameters": {
"allScores": "{{input.all}}" // Gets entire array
}
}
// allScores = [{ "score": 85 }, { "score": 92 }]{{nodeId.item}} - Latest Item from Specific Node// Workflow: fetchUser → calculateTotal → sendEmail
{
"id": "sendEmail",
"parameters": {
"to": "{{fetchUser.item.email}}", // From fetchUser node
"amount": "{{calculateTotal.item.total}}" // From calculateTotal node
}
}{{nodeId.all}} - All Items from Specific Node{
"id": "mergeData",
"parameters": {
"users": "{{fetchUsers.all}}", // All users from fetchUsers
"orders": "{{fetchOrders.all}}" // All orders from fetchOrders
}
}// API returned: {
// "user": {
// "profile": {
// "firstName": "Jane",
// "lastName": "Doe"
// },
// "addresses": [
// { "street": "123 Main St", "city": "Boston" }
// ]
// }
// }
{
"parameters": {
"firstName": "{{apiCall.item.user.profile.firstName}}", // "Jane"
"street": "{{apiCall.item.user.addresses.0.street}}", // "123 Main St"
"city": "{{apiCall.item.user.addresses.0.city}}" // "Boston"
}
}"{{nodeId.item.items.0}}" // First item (index 0)
"{{nodeId.item.items.1}}" // Second item (index 1)
"{{nodeId.item.items.2}}" // Third item (index 2){{input.item...}} as the default syntax in node parameters.| Context | Preferred Syntax | Use It For | Example |
|---|---|---|---|
| Previous node (current item) | {{input.item...}} | Most node fields | {{input.item.email}} |
| Previous node (all items) | {{input.all}} | Batch operations, aggregate input | {{input.all}} |
| Specific node (current item) | {{nodeId.item...}} | Cross-branch or non-adjacent node reference | {{fetchUser.item.name}} |
| Specific node (all items) | {{nodeId.all}} | Full dataset from another node | {{fetchOrders.all}} |
| Loop context (inside loop path) | {{$loop...}} | Iteration metadata + current iterated item | {{$loop.index}}, {{$loop.item.id}} |
| Workflow constants | {{$vars...}} | Reusable global config values | {{$vars.base_url}} |
| Environment variables | {{$env...}} | Secrets/config from environment | {{$env.API_BASE_URL}} |
| Direct node handle (advanced) | {{$node["Node Name"]...}} | Dynamic cross-node lookups when needed | {{$node["HTTP Request"].item.result}} |
| Code node script | $input (JavaScript) | Programmatic transformations in code | const item = $input.all()[0].json; |
$loop, $vars, $node, $env?$loop (Loop Over Items context){
"currentIndex": "{{$loop.index}}",
"totalItems": "{{$loop.count}}",
"currentItemId": "{{$loop.item.id}}"
}$loop when you need iteration metadata. Use {{input.item...}} for standard field access in most non-loop nodes.$vars (Workflow-scoped variables){
"url": "{{$vars.api_base_url}}/customers/{{input.item.customer_id}}",
"workspace": "{{$vars.workspace_uuid}}"
}$vars for non-changing values you do not want to duplicate across nodes.$node (Read output from a named node){
"customerEmail": "{{$node[\"fetchCustomer\"].item.email}}",
"orderTotal": "{{$node[\"calculateTotal\"].item.total}}"
}{{nodeId.item...}} first. Use $node[...] only when you specifically need name-based dynamic lookups.$env (Environment-level values){
"base_url": "{{$env.APP_URL}}",
"api_key": "{{$env.EXTERNAL_API_KEY}}"
}$env for deployment/runtime config and secrets. Do not hardcode secrets in workflow JSON.| Node Type | Recommended Access Pattern |
|---|---|
| HTTP Request, Edit Fields, Filter, If, Switch, Table nodes | {{input.item...}} |
| Aggregate, Merge, batch operations | {{input.all}} or {{nodeId.all}} |
| Loop Over Items branch | {{input.item...}} + {{$loop...}} when iteration metadata is needed |
| Code node | Use $input in JavaScript, return clean object shape |
input is enough{{input.item.email}}{{nodeId.item.email}} when reading from a non-previous node.{{$loop.index}} works in loop path, not outside it.{{input.item.count}} keeps number type."Count: {{input.item.count}}" becomes string.{{input.item}} -> {{input.item.user}} -> {{input.item.user.email}}.{{$env.SECRET_NAME}} instead of raw secret strings.$('nodeId') syntax:| Method | Description | Example |
|---|---|---|
first() | Get first item | {{$('users').first()}} |
last() | Get last item | {{$('users').last()}} |
count() | Count items | {{$('users').count()}} |
pluck('field') | Extract field from all items | {{$('users').pluck('email')}} |
sum() | Sum numeric values | {{$('orders').pluck('total').sum()}} |
unique() | Remove duplicates | {{$('tags').unique()}} |
take(n) | Get first n items | {{$('users').take(5)}} |
sortBy('field') | Sort by field | {{$('users').sortBy('name')}} |
// users = [
// { "name": "Alice", "email": "alice@example.com", "age": 30 },
// { "name": "Bob", "email": "bob@example.com", "age": 25 }
// ]
{
"parameters": {
"emails": "{{$('users').pluck('email')}}", // ["alice@...", "bob@..."]
"count": "{{$('users').count()}}", // 2
"firstEmail": "{{$('users').pluck('email').first()}}", // "alice@example.com"
"avgAge": "{{$('users').pluck('age').avg()}}" // 27.5
}
}{
"parameters": {
"greeting": "Hello {{user.item.name}}!",
"message": "You have {{notifications.count}} new messages",
"url": "https://api.com/users/{{user.item.id}}/profile"
}
}
// Result:
// greeting = "Hello Alice!"
// message = "You have 5 new messages"
// url = "https://api.com/users/123/profile"{
"parameters": {
"count": "{{users.count()}}", // Number: 42
"isActive": "{{user.item.active}}", // Boolean: true
"tags": "{{post.item.tags}}", // Array: ["tech", "ai"]
"message": "Count: {{users.count()}}" // String: "Count: 42"
}
}staticData:{
"parameters": {
"apiKey": "{{staticData.apiKey}}",
"baseUrl": "{{staticData.apiUrl}}"
}
}{
"parameters": {
"approved": "{{humanInTheLoop.item.approved}}",
"comments": "{{humanInTheLoop.item.comments}}"
}
}{
"parameters": {
"imageData": "$toBinary({{imageUrl.item.url}})"
}
}// Workflow: Fetch User → Send Email
// Node 1: Fetch User
{
"id": "fetchUser",
"type": "http",
"parameters": {
"url": "https://api.example.com/users/123"
}
}
// Output: { "name": "Alice", "email": "alice@example.com" }
// Node 2: Send Email
{
"id": "sendEmail",
"type": "email",
"parameters": {
"to": "{{fetchUser.item.email}}", // "alice@example.com"
"subject": "Hello {{fetchUser.item.name}}" // "Hello Alice"
}
}// Workflow: Get Table Rows → Process Each Row
// Node 1: Get Table Rows
{
"id": "getTableRows",
"type": "getTableRows",
"parameters": {
"table_uuid": "abc-123"
}
}
// Output: [
// { "id": 1, "name": "Item 1" },
// { "id": 2, "name": "Item 2" }
// ]
// Node 2: Process Each Row (runs twice, once per item)
{
"id": "processRow",
"type": "http",
"parameters": {
"url": "https://api.example.com/items/{{input.item.id}}",
"body": {
"name": "{{input.item.name}}"
}
}
}
// First run: url = ".../items/1", body = { "name": "Item 1" }
// Second run: url = ".../items/2", body = { "name": "Item 2" }// Workflow: Fetch User → Fetch Orders → Send Summary
// Node 1: Fetch User
{
"id": "fetchUser",
"parameters": { "userId": "123" }
}
// Output: { "name": "Alice", "email": "alice@example.com" }
// Node 2: Fetch Orders
{
"id": "fetchOrders",
"parameters": { "userId": "123" }
}
// Output: [
// { "id": "order1", "total": 50 },
// { "id": "order2", "total": 75 }
// ]
// Node 3: Send Summary
{
"id": "sendSummary",
"parameters": {
"to": "{{fetchUser.item.email}}",
"name": "{{fetchUser.item.name}}",
"orderCount": "{{$('fetchOrders').count()}}",
"totalSpent": "{{$('fetchOrders').pluck('total').sum()}}"
}
}
// to = "alice@example.com"
// name = "Alice"
// orderCount = 2
// totalSpent = 125// Workflow: Fetch User → Check Age → Send Appropriate Message
// Node 1: Fetch User
{
"id": "fetchUser"
}
// Output: { "name": "Bob", "age": 17 }
// Node 2: Check Age (If Node)
{
"id": "checkAge",
"type": "if",
"parameters": {
"condition": "{{fetchUser.item.age}} >= 18"
}
}
// Output (partitioned):
// {
// "true": [], // Age >= 18
// "false": [...] // Age < 18
// }
// Node 3a: Adult Message (connected to "true" port)
{
"id": "adultMessage",
"parameters": {
"message": "Welcome {{fetchUser.item.name}}! You have full access."
}
}
// Node 3b: Minor Message (connected to "false" port)
{
"id": "minorMessage",
"parameters": {
"message": "Sorry {{fetchUser.item.name}}, you must be 18 or older."
}
}// ❌ Bad: Generic IDs
{ "id": "node1" }
{ "id": "node2" }
// ✅ Good: Descriptive IDs
{ "id": "fetchUserProfile" }
{ "id": "calculateOrderTotal" }input for Simple Sequential Workflows// ✅ Good for simple chains
"{{input.item.result}}"
// ✅ Good when combining multiple sources
"{{fetchUser.item.email}}" and "{{calculateTotal.item.amount}}"// Step 1: Test the node output
"{{apiCall.item}}"
// Step 2: Navigate to nested object
"{{apiCall.item.data}}"
// Step 3: Access specific property
"{{apiCall.item.data.user}}"
// Step 4: Get final value
"{{apiCall.item.data.user.email}}"// ❌ More Complex
{
"type": "code",
"parameters": {
"code": "return items.map(item => item.email)"
}
}
// ✅ Simpler
{
"parameters": {
"emails": "{{$('users').pluck('email')}}"
}
}// ✅ Preserves type
"count": "{{users.count()}}" // Results in number: 42
// ❌ Converts to string
"count": "Total: {{users.count()}}" // Results in string: "Total: 42"// ❌ Too deep (hard to debug)
"{{node1.item.data.user.profile.settings.preferences.theme}}"
// ✅ Reasonable depth
"{{node1.item.user.theme}}"// Add comments to explain complex variable paths
{
"parameters": {
// Gets the email of the user who created the order
"creatorEmail": "{{order.item.metadata.createdBy.user.email}}"
}
}{{nodeId.item}}{{nodeId.item.data}}// ❌ Wrong: Returns "Count: 42" (string)
"count": "Count: {{users.count()}}"
// ✅ Right: Returns 42 (number)
"count": "{{users.count()}}"items.5 but array only has 3 items// ❌ May return null
"{{data.item.items.10}}"
// ✅ Safer
"{{$('data').pluck('items').flatten().take(10)}}"sourceHandle property{
"source": "ifNode",
"sourceHandle": "true", // or "false"
"target": "nextNode"
}// 1. Test basic access
"{{nodeId.item}}"
// 2. Add one level
"{{nodeId.item.data}}"
// 3. Add specific property
"{{nodeId.item.data.email}}"{
"type": "code",
"parameters": {
"code": "const item = $input.all()[0].json; console.log(item); return item;"
}
}| # | Failure Pattern | What You See | Why It Happens | Fast Fix |
|---|---|---|---|---|
| 1 | Wrong variable path depth | Empty/null values | Path assumes fields that do not exist at runtime | Validate path incrementally: {{input.item}} -> nested levels |
| 2 | Referencing wrong source node | Correct shape, wrong data | Using {{nodeId.item...}} from stale/non-adjacent branch | Prefer {{input.item...}} when sequential; use explicit node ID only when needed |
| 3 | Loop context misuse | Index/item undefined | {{$loop...}} used outside loop branch | Use {{$loop...}} only inside loop execution path |
| 4 | Array index out of bounds | Null at deep array path | Hardcoded index (for example .10) does not exist | Check size first or use safer collection operations |
| 5 | Type coercion surprises | Numeric comparisons fail | Value turned into string via interpolation in text | Keep value standalone: {{input.item.count}} (not "Count: {{...}}") |
| 6 | Hidden branch mismatch | Workflow "works" but outputs unexpected fields | If/Switch route differs from expected branch | Verify branch condition and connected sourceHandle |
| 7 | Generic error masking root cause | Same error for many issues | Node reports one generic failure message | Add context-specific checks and logging before critical node |
| 8 | Upstream node partial failure | Downstream fails inconsistently | Prior node output shape changes on error/partial results | Normalize payload with Edit Fields before critical references |
| 9 | Environment/config mismatch | Works locally, fails in staging/prod | Missing or different {{$env...}} values | Validate required environment variables per environment |
| 10 | Missing execution observability | "Intermittent" failure with no clues | No intermediate inspection/logging | Add temporary debug nodes and structured logs during investigation |
{{input.item.requiredField}} is empty -> route to error handling{{input.item}} in a safe field/output.{{input.item.data}} -> {{input.item.data.user}} -> {{input.item.data.user.email}}{{$loop.index}} and {{$loop.item}}input vs explicit node reference{{input.item.id}} fails, try {{sourceNode.item.id}} to verify source mismatch.{
"type": "code",
"parameters": {
"code": "const item = $input.all()[0].json; console.log('DEBUG_ITEM', item); return item;"
}
}| What You Want | Pattern | Example |
|---|---|---|
| Data from previous node (current item) | {{input.item}} | {{input.item.email}} |
| All data from previous node | {{input.all}} | {{input.all}} |
| Data from specific node (latest item) | {{nodeId.item}} | {{fetchUser.item.name}} |
| All data from specific node | {{nodeId.all}} | {{fetchUsers.all}} |
| Nested property | {{node.item.path.to.value}} | {{user.item.profile.age}} |
| Array element by index | {{node.item.array.0}} | {{data.item.items.2.name}} |
| Count items | {{$('nodeId').count()}} | {{$('users').count()}} |
| Extract property from all items | {{$('nodeId').pluck('field')}} | {{$('orders').pluck('total')}} |
| Sum values | {{$('nodeId').pluck('field').sum()}} | {{$('sales').pluck('amount').sum()}} |
| Get first item | {{$('nodeId').first()}} | {{$('users').first()}} |
| Workflow constant | {{staticData.key}} | {{staticData.apiKey}} |
| Human approval response | {{humanInTheLoop.item.field}} | {{humanInTheLoop.item.approved}} |
| Convert URL to base64 | $toBinary(url) | $toBinary({{img.item.url}}) |
input for previous node, use nodeId for any specific node.item for single/current item, use .all for entire array$('nodeId').method() to transform arraysobject.property.subPropertyarray.0, array.1, array.2docs/workflow-nodes/ for what each node outputs{{nodeId.item}}const item = $input.all()[0].json; console.log(item) to inspect data structure