Skip to main content

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:

ScenarioLoop Over
Send order confirmationEach item in order
Process uploaded filesEach file in batch
Update multiple contactsEach contact in list
Generate reportsEach 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:

  1. Gets the current item from the array
  2. Executes all connected downstream nodes
  3. Moves to the next item
  4. Repeats until all items processed

Configuring the Loop Node

Basic Configuration

FieldDescriptionExample
ArrayThe array to iterate over{{data.items}}
Item AliasName for current itemcurrent_item
Index AliasName for current indexindex
Max IterationsSafety limit100

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


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:

  1. ORD-001, Apple
  2. ORD-001, Banana
  3. 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:

ScenarioRecommended Max
Email sending100-500
API calls50-100
Record creation100-500
Heavy processing10-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:

  1. Data Transformation - Process data in loops
  2. Error Handling - Handle loop failures
  3. Testing Workflows - Test loop behavior