Skip to main content

Map Step

The Map step transforms data structures, iterates over arrays, filters collections, and reshapes objects. Use it to prepare data for agents, format API responses, extract specific fields, or process lists of items.
When to use Map vs. Agents: Use Map for structural transformations (reformatting, filtering, extracting). Use Agents when transformation requires intelligence or interpretation. Map is for predictable data manipulation; agents are for smart decisions.

How Map Works

Map takes input data and transforms it according to rules you define:
HTTP Request: Returns user data

Map: Extract relevant fields
  Input: ${http.body}
  Output: {name, email, tier}

Agent: Use cleaned data

Configuration

Transform Type

transformType
enum
required
Type of transformation to performOptions:
  • extract - Pull specific fields from object
  • iterate - Process each item in array
  • filter - Keep only items matching condition
  • restructure - Reshape entire data structure
  • merge - Combine multiple objects
  • custom - Use JavaScript for complex transforms

Transform Types

Extract Fields

Pull specific fields from an object, discarding the rest.
fields
array
required
Fields to extractExample:
{
  "fields": ["name", "email", "tier", "address.city"]
}
Input:
{
  "id": 123,
  "name": "John Doe",
  "email": "[email protected]",
  "tier": "gold",
  "internal_notes": "...",
  "address": {
    "street": "123 Main St",
    "city": "Boston"
  }
}
Output:
{
  "name": "John Doe",
  "email": "[email protected]",
  "tier": "gold",
  "address": {
    "city": "Boston"
  }
}
Use dot notation for nested fields: "address.city" extracts city from address object.

Iterate Over Array

Process each item in an array, transforming or filtering items.
array
string
required
Path to array to iterate overExample: ${http_request.body.orders}
itemTransform
object
required
How to transform each itemExample:
{
  "id": "${item.order_id}",
  "customer": "${item.customer_name}",
  "total": "${item.amount}",
  "status": "${item.order_status}",
  "date": "${item.created_at}"
}
Reference current item with ${item.field_name}
filterCondition
string
Optional filter condition (keep only matching items)Examples:
${item.status} == "active"
${item.amount} > 100
${item.tier} == "gold" || ${item.tier} == "platinum"
Complete Example: Input:
{
  "orders": [
    {"order_id": 1, "customer_name": "John", "amount": 150, "order_status": "paid"},
    {"order_id": 2, "customer_name": "Jane", "amount": 50, "order_status": "pending"},
    {"order_id": 3, "customer_name": "Bob", "amount": 200, "order_status": "paid"}
  ]
}
Configuration:
{
  "array": "${http.body.orders}",
  "filterCondition": "${item.order_status} == 'paid'",
  "itemTransform": {
    "id": "${item.order_id}",
    "customer": "${item.customer_name}",
    "total": "${item.amount}"
  }
}
Output:
[
  {"id": 1, "customer": "John", "total": 150},
  {"id": 3, "customer": "Bob", "total": 200}
]

Filter Array

Keep only items that match a condition (without transforming them).
array
string
required
Path to array
condition
string
required
Filter conditionExamples:
// Keep high-value orders
${item.amount} >= 1000

// Keep active customers
${item.status} == "active" && ${item.last_login} != null

// Keep specific tiers
["gold", "platinum"].includes(${item.tier})

// Keep recent items
${item.created_at} > "${seven_days_ago}"

Restructure Data

Completely reshape data structure.
template
object
required
New structure template using variablesExample - Flatten nested structure:Input:
{
  "user": {
    "profile": {
      "name": "John Doe",
      "contact": {
        "email": "[email protected]"
      }
    }
  }
}
Template:
{
  "name": "${input.user.profile.name}",
  "email": "${input.user.profile.contact.email}"
}
Output:
{
  "name": "John Doe",
  "email": "[email protected]"
}
Example - Create nested structure: Input:
{
  "first_name": "John",
  "last_name": "Doe",
  "street": "123 Main St",
  "city": "Boston",
  "state": "MA"
}
Template:
{
  "name": {
    "first": "${input.first_name}",
    "last": "${input.last_name}",
    "full": "${input.first_name} ${input.last_name}"
  },
  "address": {
    "street": "${input.street}",
    "city": "${input.city}",
    "state": "${input.state}"
  }
}
Output:
{
  "name": {
    "first": "John",
    "last": "Doe",
    "full": "John Doe"
  },
  "address": {
    "street": "123 Main St",
    "city": "Boston",
    "state": "MA"
  }
}

Merge Objects

Combine multiple objects into one.
sources
array
required
Objects to mergeExample:
{
  "sources": [
    "${customer_data}",
    "${order_data}",
    {"source": "quiva"}
  ]
}
Later sources override earlier ones on conflicting keys.
Example: Input:
{
  "customer": {"name": "John", "email": "[email protected]"},
  "order": {"id": 123, "total": 99.99},
  "metadata": {"processed": true}
}
Configuration:
{
  "sources": [
    "${input.customer}",
    "${input.order}",
    "${input.metadata}"
  ]
}
Output:
{
  "name": "John",
  "email": "[email protected]",
  "id": 123,
  "total": 99.99,
  "processed": true
}

Custom JavaScript

Use JavaScript for complex transformations that don’t fit other types.
code
string
required
JavaScript code to transform dataAvailable variables:
  • input - Input data
  • context - Flow context and previous step outputs
Must return transformed dataExample:
// Calculate statistics from array
const orders = input.orders;
const total = orders.reduce((sum, order) => sum + order.amount, 0);
const average = total / orders.length;

return {
  total_orders: orders.length,
  total_revenue: total,
  average_order_value: average,
  highest_order: Math.max(...orders.map(o => o.amount))
};
Custom JavaScript has performance implications. Use built-in transform types when possible.

Common Patterns

Extract only fields agent needs from API response
HTTP Request: Get customer data

Map: Extract relevant fields
  Type: Extract
  Fields: ["name", "email", "tier", "orders"]

Agent: Personalize response
  Context: Cleaned customer data
Why: Reduces token usage, removes noise, faster agent processing
Transform each item in a list
Database: Get pending orders
  Returns: [{order_id, customer_id, items, ...}, ...]

Map: Transform each order
  Type: Iterate
  Transform: {
    id: ${item.order_id},
    customer: ${item.customer_id},
    total: ${item.items.length},
    status: "processing"
  }

Agent: Process each transformed order
Use when: Need to process lists of items
Keep only items meeting criteria
HTTP Request: Get all leads

Map: Filter high-value leads
  Type: Filter
  Condition: ${item.score} >= 7 && ${item.budget} >= 10000

Agent: Qualify filtered leads
Use when: Only processing subset of data
Reshape data to match API requirements
Agent: Generate customer data
  Output: {name: "John Doe", email: "...", ...}

Map: Format for CRM API
  Type: Restructure
  Template: {
    "contact": {
      "first_name": "${input.name.split(' ')[0]}",
      "last_name": "${input.name.split(' ')[1]}",
      "email": "${input.email}"
    },
    "source": "quiva"
  }

HTTP Request: POST to CRM
Use when: External API expects specific format
Merge data from different sources
HTTP Request 1: Get customer profile

HTTP Request 2: Get customer orders

Map: Merge data
  Type: Merge
  Sources: [${http_1.body}, ${http_2.body}]

Agent: Analyze complete customer data
Use when: Need combined view of data
Flatten deeply nested structures
HTTP Request: Complex nested response

Map: Flatten
  Type: Restructure
  Template: {
    "id": "${input.data.user.profile.id}",
    "name": "${input.data.user.profile.personal.name}",
    "email": "${input.data.user.profile.contact.email}"
  }

Simplified flat structure
Use when: Working with complex API responses
Compute statistics from arrays
Database: Get order history

Map: Calculate statistics
  Type: Custom JavaScript
  Code: Calculate total, average, max, min

Agent: Provide insights on order history
Use when: Need derived metrics
Transform data for user-friendly display
Database: Raw transaction data

Map: Format for display
  Transform each item:
    - Format currency
    - Format dates
    - Add display labels
    - Calculate derived fields

Agent: Present formatted data to user
Use when: Preparing data for end users

Real-World Examples

Example 1: E-commerce Order Processing

Scenario: Process orders, filter by status, enrich with customer data
HTTP Request: GET /orders
  Returns: [{order_id, customer_id, status, items, total}, ...]

Map 1: Filter paid orders
  Type: Filter
  Array: ${http.body.orders}
  Condition: ${item.status} == "paid"

Map 2: Enrich with customer data
  Type: Iterate
  Transform: {
    "order_id": "${item.order_id}",
    "total": "${item.total}",
    "customer": {
      "id": "${item.customer_id}",
      "name": "${customers[item.customer_id].name}",
      "email": "${customers[item.customer_id].email}"
    },
    "item_count": "${item.items.length}"
  }

Agent: Process enriched orders

Example 2: Lead Scoring

Scenario: Score leads based on multiple criteria
HTTP Request: GET /leads

Map: Calculate lead scores
  Type: Iterate
  Transform: {
    "id": "${item.id}",
    "company": "${item.company}",
    "score": 0,
    "score_factors": {}
  }

Map: Add scoring (Custom JS)
  Code:
    const lead = input;
    let score = 0;
    
    // Company size
    if (lead.employees >= 1000) score += 30;
    else if (lead.employees >= 100) score += 20;
    else score += 10;
    
    // Budget
    if (lead.budget >= 100000) score += 30;
    else if (lead.budget >= 50000) score += 20;
    else score += 10;
    
    // Engagement
    if (lead.website_visits >= 10) score += 20;
    if (lead.email_opens >= 5) score += 10;
    
    return { ...lead, score };

Map: Filter qualified leads
  Type: Filter
  Condition: ${item.score} >= 60

Agent: Personalize outreach for qualified leads

Example 3: Customer Data Enrichment

Scenario: Combine data from multiple APIs
Trigger: New customer signup

HTTP Request 1: GET customer profile from CRM

HTTP Request 2: GET customer orders from e-commerce

HTTP Request 3: GET customer support tickets

Map: Merge all data
  Type: Merge
  Sources: [
    "${http_1.body}",
    {"orders": "${http_2.body.orders}"},
    {"tickets": "${http_3.body.tickets}"}
  ]

Map: Calculate customer metrics
  Type: Custom JavaScript
  Code:
    return {
      ...input,
      lifetime_value: input.orders.reduce((sum, o) => sum + o.total, 0),
      order_count: input.orders.length,
      support_interactions: input.tickets.length,
      last_purchase: input.orders[0]?.date,
      customer_health_score: calculateHealthScore(input)
    }

Agent: Generate personalized onboarding email
  Context: Complete customer profile

Example 4: Report Generation

Scenario: Transform raw data into report format
Database: Query monthly transactions

Map 1: Group by category (Custom JS)
  Code:
    const grouped = {};
    input.transactions.forEach(t => {
      if (!grouped[t.category]) grouped[t.category] = [];
      grouped[t.category].push(t);
    });
    return grouped;

Map 2: Calculate category totals
  Type: Custom JavaScript
  Code:
    const categories = Object.keys(input);
    return categories.map(cat => ({
      category: cat,
      count: input[cat].length,
      total: input[cat].reduce((sum, t) => sum + t.amount, 0),
      average: input[cat].reduce((sum, t) => sum + t.amount, 0) / input[cat].length
    }));

Agent: Generate executive summary report
  Context: Category statistics

Example 5: Form Data Normalization

Scenario: Normalize inconsistent form submissions
Trigger: Form submission
  Data: Various formats, optional fields, inconsistent casing

Map: Normalize data
  Type: Restructure
  Template: {
    "contact": {
      "firstName": "${input.first_name || input.firstName || input.fname}",
      "lastName": "${input.last_name || input.lastName || input.lname}",
      "email": "${(input.email || input.email_address).toLowerCase().trim()}",
      "phone": "${normalizePhone(input.phone || input.tel)}"
    },
    "company": {
      "name": "${input.company_name || input.company || ''}",
      "size": "${input.company_size || input.employees || 'unknown'}"
    },
    "metadata": {
      "source": "web_form",
      "submitted_at": "${now}",
      "ip": "${trigger.ip}"
    }
  }

Agent: Process normalized data

Variable Mapping

Map step heavily uses variable syntax to reference data. Learn more about variable mapping:

Variable Mapping Guide

Complete guide to referencing data from triggers, steps, and context
Common variable patterns in Map:
// Reference trigger data
${trigger.body.email}

// Reference previous step
${http_request.body.data}

// Reference array item (in iterate)
${item.field_name}

// Nested access
${agent.output.decision.confidence}

// Array element
${orders[0].total}

// Multiple sources
${step1.name} ${step2.email}

Best Practices

Use Right Transform Type

Choose the appropriate transform type. Extract for simple field selection, Iterate for arrays, Custom JS for complex logic.

Keep Transforms Simple

Break complex transformations into multiple Map steps. Easier to debug and maintain.

Filter Early

Filter arrays before processing to reduce computation. Process only what you need.

Test with Real Data

Test Maps with actual data structures from your APIs/databases. Edge cases matter.

Document Complex Logic

Add descriptions to Map steps explaining what transformation does and why.

Avoid Over-Transformation

Let agents handle interpretation. Use Map only for structural changes, not business logic.

Check for Null Values

Always handle null/undefined values. Use ${field || 'default'} for safety.

Validate Output

Verify Map output has expected structure before using in agents or APIs.

Troubleshooting

Causes:
  • Wrong variable path
  • Source data doesn’t exist
  • Filter condition too strict
Solutions:
  • Check execution logs for actual input data
  • Verify variable references: ${http.body.data} not ${data}
  • Test filter condition separately
  • Add null checks: ${field} != null
Causes:
  • Incorrect dot notation
  • Field doesn’t exist
  • Array needs index
Solutions:
  • Verify exact path from logs: ${input.user.profile.name}
  • Check for arrays: Use [0] for first element
  • Handle optional fields: ${input.field || 'default'}
Causes:
  • Wrong comparison operator
  • Data type mismatch
  • Variable reference incorrect
Solutions:
  • Use == for equality, not =
  • Check types: "100" vs 100
  • Log items to see actual values
  • Test condition in Eval step first
Causes:
  • Wrong array reference
  • Transform template incorrect
  • Missing fields in items
Solutions:
  • Verify array path in logs
  • Check each item has required fields
  • Use ${item.field || 'default'} for optional fields
  • Test with small sample array first
Causes:
  • Syntax error
  • Undefined variable
  • Missing return statement
Solutions:
  • Check JavaScript syntax
  • Verify all variables exist: input, context
  • Always return a value
  • Use console.log for debugging (appears in logs)
  • Test JS in Eval step first

When to Use Map vs. Other Steps

Use Map WhenUse Alternative When
Extracting specific fieldsNeed all data (no transform needed)
Reformatting data structureAgent can work with existing structure
Filtering arraysCondition on single value (use Condition)
Merging objectsComplex merge logic (use Eval)
Simple calculationsComplex business rules (use Rules)
Structural changes onlyNeed intelligence (use Agent)
Examples: Use Map: Extract name and email from API response
Don’t need Map: Agent can read full API response
Use Map: Filter array to items > 100UseConditioninstead:Checkifsinglevalue>100 ❌ **Use Condition instead**: Check if single value > 100 Use Map: Flatten nested object structure
Use Agent instead: Interpret and summarize nested data

Performance Tips

Reduce array size before complex transformationsGood:
Map 1: Filter (keep 100 items)

Map 2: Complex transform (process 100)
Bad:
Map 1: Complex transform (process 1000)

Map 2: Filter (keep 100)
Built-in transforms (Extract, Iterate, Filter) are faster than Custom JavaScriptFast: Extract, Iterate, Filter, Restructure
Slower: Custom JavaScript (but more flexible)
Processing large arrays can be slow. Consider:
  • Paginating API calls
  • Filtering at source (database query, API parameters)
  • Processing in batches
Don’t iterate arrays within iterationsBad:
Map: For each customer
  Map: For each order (nested)
Better:
Map: Flatten to customer-order pairs
Map: Process flattened array

Next Steps