Loops & Iteration
When you need to process multiple items - like sending emails to a list of contacts or processing order items - you use loops. This guide shows you how to iterate over data in your workflows.
Time to complete: 15 minutes
When to Use Loops
Use loops when you need to:
- Process each item in an array
- Send messages to multiple recipients
- Create multiple records
- Transform each item in a list
- Aggregate data from multiple sources
Example scenarios:
| Scenario | Loop Over |
|---|---|
| Send order confirmation | Each item in order |
| Process uploaded files | Each file in batch |
| Update multiple contacts | Each contact in list |
| Generate reports | Each department |
The Loop Node
The Loop node iterates over an array, executing connected nodes for each item.
How It Works
┌─────────────┐
│ Loop │
│ (5 items) │
└──────┬──────┘
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
Run 1 Run 2 ... Run 5
(item[0]) (item[1]) (item[4])
Each iteration:
- Gets the current item from the array
- Executes all connected downstream nodes
- Moves to the next item
- Repeats until all items processed
Configuring the Loop Node
Basic Configuration
| Field | Description | Example |
|---|---|---|
| Array | The array to iterate over | {{data.items}} |
| Item Alias | Name for current item | current_item |
| Index Alias | Name for current index | index |
| Max Iterations | Safety limit | 100 |
Example Setup
Array to process:
{
"contacts": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" },
{ "name": "Carol", "email": "carol@example.com" }
]
}
Loop configuration:
Array: {{webhooktrigger_1.payload.contacts}}
Item Alias: contact
Index Alias: i
Max Iterations: 100
Inside the loop, access:
{{loop_1.contact.name}} → "Alice" (first iteration)
{{loop_1.contact.email}} → "alice@example.com"
{{loop_1.i}} → 0 (zero-based index)
Building a Loop Workflow
Example: Send Email to Each Contact
Goal: Send personalized emails to a list of contacts
[Webhook] → [Loop] → [Email Send]
↓
(runs for each contact)
Step 1: Set up the trigger
Webhook receives:
{
"campaign": "Welcome Series",
"contacts": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" }
]
}
Step 2: Configure the Loop
Array: {{webhooktrigger_1.payload.contacts}}
Item Alias: contact
Step 3: Configure Email Send (inside loop)
To: {{loop_1.contact.email}}
Subject: Welcome, {{loop_1.contact.name}}!
Body: Hello {{loop_1.contact.name}}, welcome to our platform...
Step 4: The result
- Email 1 → alice@example.com, "Welcome, Alice!"
- Email 2 → bob@example.com, "Welcome, Bob!"
Accessing Loop Variables
Current Item
{{loop_1.item}} → Full current item
{{loop_1.item.property}} → Item property
Or with custom alias:
{{loop_1.contact}} → Full contact object
{{loop_1.contact.email}} → Contact's email
Current Index
{{loop_1.index}} → 0, 1, 2, ... (zero-based)
{{loop_1.i}} → With custom alias
Iteration Count
{{loop_1.iteration}} → 1, 2, 3, ... (one-based count)
Array Length
{{loop_1.total}} → Total number of items
Is First/Last
{{loop_1.is_first}} → true for first item
{{loop_1.is_last}} → true for last item
Loop Patterns
Pattern 1: Simple Iteration
Process each item independently.
[Get Data] → [Loop] → [Process Item]
Use for:
- Sending notifications
- Creating records
- Making API calls
Pattern 2: Transform and Collect
Transform each item and collect results.
[Get Data] → [Loop] → [Transform Item] → [Collect Results]
Inside loop (Transform):
const item = input.loop_1.item;
return {
processed: {
id: item.id,
formatted_name: item.name.toUpperCase(),
processed_at: new Date().toISOString()
}
};
Pattern 3: Conditional Processing
Only process items that meet criteria.
[Loop] → [Condition] → True → [Process]
→ False → [Skip]
Condition configuration:
Variable: {{loop_1.contact.is_active}}
Operator: equals
Value: true
Pattern 4: Sequential with Delay
Add delays between iterations (rate limiting).
[Loop] → [API Call] → [Delay 1s]
Use for:
- Avoiding rate limits
- Spreading load
- Timing-sensitive operations
Nested Loops
When you need to loop within a loop.
Example: Orders with Multiple Items
Data structure:
{
"orders": [
{
"id": "ORD-001",
"items": ["Apple", "Banana"]
},
{
"id": "ORD-002",
"items": ["Cherry"]
}
]
}
Workflow:
[Get Orders] → [Loop Orders] → [Loop Items] → [Process Item]
Access in inner loop:
{{loop_1.order.id}} → Current order ID
{{loop_2.item}} → Current item in that order
Iterations:
- ORD-001, Apple
- ORD-001, Banana
- ORD-002, Cherry
Aggregating Loop Results
Collecting in Transform Node
After the loop, aggregate results:
// Transform node after loop
const results = input.loop_1.all_results || [];
return {
total_processed: results.length,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
summary: results.map(r => r.id).join(', ')
};
Building Arrays
Inside loop, build an array progressively:
const existingResults = input.accumulated_results || [];
const currentResult = {
id: input.loop_1.item.id,
processed: true
};
return {
accumulated_results: [...existingResults, currentResult]
};
Error Handling in Loops
Continue on Error
Configure the loop or nodes inside to continue on failure:
Loop Settings:
☑ Continue on item error
This ensures one failed item doesn't stop the entire loop.
Track Failures
// Inside loop transform
try {
// Process item
return { success: true, item: input.loop_1.item };
} catch (error) {
return { success: false, item: input.loop_1.item, error: error.message };
}
Summary After Loop
// After loop completes
const results = input.all_loop_results;
const failures = results.filter(r => !r.success);
if (failures.length > 0) {
// Handle failures - maybe send alert
return {
status: "completed_with_errors",
failed_items: failures
};
}
return { status: "success" };
Performance Considerations
Batch Processing
For large datasets, consider batching:
// Instead of 1000 individual API calls
// Batch into groups of 50
const items = input.data.items;
const batchSize = 50;
const batches = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
return { batches };
Then loop over batches instead of individual items.
Max Iterations
Always set a reasonable limit:
| Scenario | Recommended Max |
|---|---|
| Email sending | 100-500 |
| API calls | 50-100 |
| Record creation | 100-500 |
| Heavy processing | 10-50 |
Parallel vs Sequential
Loops execute sequentially by default. For parallel processing:
- Use batch API calls
- Process arrays in Transform node
- Consider multiple workflows
Common Issues
Empty Array
Problem: Loop doesn't execute
Solution: Check array has items
// Before loop
const items = input.data.items || [];
if (items.length === 0) {
return { skip: true, message: "No items to process" };
}
return { items };
Wrong Array Reference
Problem: "Cannot iterate over undefined"
Solution: Verify the array path
❌ {{data.contacts}} → Missing trigger prefix
✅ {{webhooktrigger_1.payload.contacts}}
Item Access Inside Loop
Problem: Can't access item properties
Solution: Use correct loop alias
❌ {{item.name}} → No node reference
✅ {{loop_1.item.name}} → Correct reference
Loop Never Ends
Problem: Workflow times out
Solution: Check max iterations and array length
Max Iterations: 100 ← Set a reasonable limit
Best Practices
1. Always Set Max Iterations
Prevent runaway loops:
Max Iterations: 100
2. Use Meaningful Aliases
Item Alias: contact ← Clear
Index Alias: contact_index ← Descriptive
3. Handle Empty Arrays
Add a condition before the loop:
[Check Array] → Has items → [Loop]
→ Empty → [Handle Empty Case]
4. Add Delays for External APIs
[Loop] → [API Call] → [Delay 500ms]
5. Log Progress
For long loops, track progress:
console.log(`Processing ${input.loop_1.index + 1} of ${input.loop_1.total}`);
Quick Reference
Loop Variables
{{loop_1.item}} Current item
{{loop_1.index}} Current index (0-based)
{{loop_1.iteration}} Current iteration (1-based)
{{loop_1.total}} Total items
{{loop_1.is_first}} Is first item
{{loop_1.is_last}} Is last item
Configuration
Array → Variable containing array
Item Alias → Name for current item
Index Alias → Name for current index
Max Iterations → Safety limit
Common Patterns
Simple: [Loop] → [Action]
Transform: [Loop] → [Transform] → [Collect]
Conditional: [Loop] → [Condition] → [Action]
With Delay: [Loop] → [Action] → [Delay]
Next Steps
Continue with:
- Data Transformation - Process data in loops
- Error Handling - Handle loop failures
- Testing Workflows - Test loop behavior