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