Data Flow & Type Handling

Learn how data flows between nodes in workflows and handle data types correctly to avoid runtime errors.

Overview

In workflows, data flows from one node to the next by referencing outputs from previous nodes or workflow inputs. You control this data flow using {{...}} reference syntax. Each field has a specific data type, and type compatibility is critical for successful workflow execution.

Key Concepts:

  • Workflow inputs and node outputs have defined types

  • Node inputs expect specific types

  • Type mismatches cause workflow failures at runtime

  • Type conversion nodes help transform data between incompatible types


How Data Flows in Workflows

Understanding how data moves through your workflow is essential for building reliable automations.

Workflow Execution Flow

Workflows execute linearly from top to bottom, with data passing from one node to the next:

1. User provides workflow inputs

2. Node 1 executes → produces outputs

3. Node 2 executes (can reference Node 1 outputs) → produces outputs

4. Node 3 executes (can reference Node 1 & 2 outputs) → produces outputs

5. Workflow returns final output

Available Data at Each Node

Each node can access:

  • Workflow inputs: Data provided when the workflow starts

  • Previous node outputs: Results from any node that executed before it

  • Cannot access: Future node outputs (nodes that haven't executed yet)

Example:

Node 1: fetch_user
  Can access: workflow inputs only

Node 2: analyze_data
  Can access: workflow inputs + fetch_user outputs

Node 3: send_email
  Can access: workflow inputs + fetch_user outputs + analyze_data outputs

Controlling Data Flow

You control what data flows between nodes by:

  1. Referencing specific fields - Choose exactly which data to pass

  2. Transforming data - Use conversion techniques to match types

  3. Combining data - Merge multiple sources into one field


How to Reference Data

Use double curly braces {{...}} to reference data in node configurations and text fields:

Reference Workflow Inputs

{{input_field_name}}

Example:

{
  "url": "{{website_url}}",
  "email": "{{user_email}}"
}

Reference Node Outputs

{{node_name.output_field}}

Example:

{
  "content": "{{web_scraper.html}}",
  "summary": "{{llm_analyzer.result}}"
}

Use in Text Strings

You can embed references inside text strings - they will always be converted to string type:

{
  "message": "Hello {{user_name}}, your order #{{order_id}} is ready!",
  "subject": "Analysis complete: {{analyzer.status}}",
  "body": "Found {{search_results.count}} results for {{query}}"
}

Important: When used in strings, all values are automatically converted to text:

  • Numbers become strings: {{count}}"42"

  • Objects become JSON strings: {{user}}'{"name":"John"}'

  • Arrays become JSON strings: {{items}}'["a","b","c"]'

Nested Field Access

Access nested objects and arrays:

{{node_name.data.users[0].name}}
{{api_response.result.items[2].id}}

Understanding Data Types

Every field in workflow inputs, node inputs, and node outputs has a specific data type defined in its schema.

Common Data Types

Type
Description
Example Values

string

Text data

number

Numeric values (int or float)

42, 3.14, -10

boolean

True/false values

true, false

object

JSON objects/dictionaries

{"name": "John", "age": 30}

array

Lists of items

[1, 2, 3], ["a", "b", "c"]

How to Check Field Types

1. Workflow Input Types When defining workflow inputs, you specify the type:

{
  "user_email": {
    "type": "string",
    "title": "User Email"
  },
  "max_results": {
    "type": "number",
    "title": "Maximum Results"
  }
}

2. Node Input and Output Types Each node defines its input and output types in its schema. Check the node's detail page to see:

  • What input types each field expects

  • What output types each field produces

Where to find node type information:

  • Node Reference Documentation - Browse all 193+ nodes by category

  • Individual node detail pages - Shows complete input/output schemas

  • Visual builder node panel - Displays field types when configuring nodes


Type Compatibility Rules

✅ Compatible Substitution

You can substitute a field if the types match exactly:

// Workflow input: user_email (string)
// Node 1 output: email_address (string)
// Node 2 input: recipient (string)

{
  "recipient": "{{user_email}}"  // ✅ string → string
}

{
  "recipient": "{{node_1.email_address}}"  // ✅ string → string
}

❌ Incompatible Substitution

Type mismatches will cause runtime errors:

// Node 1 output: user_data (object)
// Node 2 input: email (string)

{
  "email": "{{node_1.user_data}}"  // ❌ object → string
}

Error at runtime:

Type mismatch: Expected string, got object

Example: Type Mismatch Scenario

Workflow Setup:

Node 1: api_call
  Output: response (object) = {"users": [{"email": "[email protected]"}]}

Node 2: send_email
  Input: recipient (string)

Incorrect Configuration:

{
  "recipient": "{{api_call.response}}"  // ❌ Fails!
}

Error: Cannot assign object to string field.

Correct Configuration:

{
  "recipient": "{{api_call.response.users[0].email}}"  // ✅ Works!
}

Access the nested string field directly.


Type Conversion Techniques

When you need to connect fields with different types, use these techniques:

1. String to JSON (Object/Array)

Use Case: Convert JSON string to object or array

Node: string_to_json

Example:

Node 1: api_call
  Output: json_string (string) = '{"name": "John", "age": 30}'

Node 2: string_to_json
  Input: string_to_convert (string) = "{{api_call.json_string}}"
  Output: json_output (object) = {"name": "John", "age": 30}

Node 3: process_user
  Input: user_object (object) = "{{string_to_json.json_output}}"

2. Object/Array to String

Use Case: Convert object or array to string

Technique: Use string interpolation (automatic conversion)

{
  "text_field": "User data: {{object_node.data}}",
  "message": "Items: {{array_node.items}}"
}

When embedded in strings, objects and arrays are automatically converted to JSON string format.

3. Number to String

Use Case: Convert numeric values to text

Technique: Use string interpolation (automatic conversion)

{
  "message": "Count: {{counter_node.value}}",
  "id": "ID-{{order_id}}"
}

Numbers are automatically converted to strings when embedded in text.

4. Extract Array Item

Use Case: Get a single item from an array

Technique: Use array indexing

{
  "first_user": "{{users_node.list[0]}}",
  "last_item": "{{items[2]}}"
}

5. Extract Object Field

Use Case: Get a specific field from an object

Technique: Use dot notation

{
  "user_name": "{{user_object.name}}",
  "user_age": "{{user_object.age}}",
  "nested_value": "{{data.results.items[0].id}}"
}

6. Custom Transformations with Code

Use Case: Complex type conversions and data transformations

Available Nodes:

  • run_javascript - Execute JavaScript code for transformations

  • run_python_code - Execute Python code for transformations

Example (JavaScript):

// Input: data_array (array of objects)
// Output: CSV string

const headers = Object.keys(data_array[0]).join(',');
const rows = data_array.map(obj =>
  Object.values(obj).join(',')
).join('\n');

return headers + '\n' + rows;

Example (Python):

# Input: items (array)
# Output: formatted_list (string)

formatted = '\n'.join([f"- {item}" for item in items])
return formatted

Common Type Mismatch Scenarios

Scenario 1: API Response to Email Body

Problem:

// API returns: {"status": "success", "message": "Done"}
// Email expects: string

{
  "email_body": "{{api_call.response}}"  // ⚠️ May not be what you want
}

This will work (objects are auto-converted to JSON strings), but you'll get:

{"status":"success","message":"Done"}

Better Solutions:

Solution A: Access the specific field you want

{
  "email_body": "{{api_call.response.message}}"  // ✅ Returns: "Done"
}

Solution B: Format it nicely in a string

{
  "email_body": "Status: {{api_call.response.status}}, Message: {{api_call.response.message}}"
  // ✅ Returns: "Status: success, Message: Done"
}

Solution C: Use the whole object if you want JSON

{
  "email_body": "API Response: {{api_call.response}}"
  // ✅ Returns: 'API Response: {"status":"success","message":"Done"}'
}

Scenario 2: String ID to Number ID

Problem:

// Node 1 returns: user_id (string) = "12345"
// Node 2 expects: id (number)

{
  "id": "{{node_1.user_id}}"  // ❌ Type error: string → number
}

Solution: Use run_javascript or run_python_code to convert

// JavaScript
return parseInt(user_id);  // Converts "12345" → 12345
# Python
return int(user_id)  # Converts "12345" → 12345

Scenario 3: Multiple Values to Array

Problem:

// Need array of emails
// Have: email1 (string), email2 (string)

{
  "recipients": "{{email1}}, {{email2}}"  // ❌ string, not array
}

Solution: Use array construction

{
  "recipients": ["{{email1}}", "{{email2}}"]  // ✅
}

Type Validation and Error Messages

Runtime Type Errors

When types don't match, workflows fail with clear error messages:

Example Error:

Node: send_email (step 3)
Field: recipient
Error: Type validation failed
Expected: string
Received: object
Value: {"email": "[email protected]", "name": "John"}

How to Fix Type Errors

  1. Check the error message - identifies the node, field, expected type, and actual type

  2. Review node documentation - confirm expected input types

  3. Inspect previous node outputs - verify what type is being produced

  4. Add conversion node - insert appropriate type conversion between nodes

  5. Adjust substitution - use dot notation or array indexing to access correct type


Best Practices

1. Verify Types Before Substituting

Always check:

  • What type does the source field produce?

  • What type does the target field expect?

  • Do they match?

2. Use Node Documentation

Refer to the Node Reference to understand:

  • Input schemas (expected types)

  • Output schemas (produced types)

3. Test Incrementally

Build workflows step-by-step:

  1. Add a node

  2. Configure inputs with substitution

  3. Run and verify output types

  4. Continue to next node

4. Add Type Conversion Early

If you know types don't match:

  • Insert conversion nodes immediately

  • Don't wait for runtime errors

5. Use Explicit Field Access

Instead of:

{"data": "{{api_node.response}}"}  // May be wrong type

Use:

{"data": "{{api_node.response.data.result}}"}  // Explicit field

6. Handle Arrays Carefully

Remember:

  • {{node.items}} → entire array

  • {{node.items[0]}} → first item

  • {{node.items[0].name}} → field from first item


Type Conversion Node Reference

Available nodes and techniques for type conversion:

Conversion
Method
Example

String → Object/Array

string_to_json node

Parse '{"a":1}'{a: 1}

Object/Array → String

String interpolation

"Data: {{obj}}"'Data: {"a":1}'

Number → String

String interpolation

"Count: {{num}}""Count: 42"

String → Number

run_javascript / run_python_code

parseInt("42")42

Array → String

String interpolation

"Items: {{arr}}"'Items: [1,2,3]'

Extract from Array

Array indexing

{{arr[0]}} → first item

Extract from Object

Dot notation

{{obj.field}} → field value

Custom Transform

run_javascript / run_python_code

Any complex conversion

Key Nodes:

  • string_to_json - Parse JSON strings to objects/arrays

  • run_javascript - Execute JavaScript for custom transformations

  • run_python_code - Execute Python for custom transformations

  • get_value_by_key - Extract value from object by key name

See Utilities Nodes for complete list.


Troubleshooting Guide

Issue: "Type mismatch" error at runtime

Solution:

  1. Check error message for expected vs. actual type

  2. Add conversion node between incompatible nodes

  3. Use dot notation to access nested fields of correct type

Issue: "Cannot read property of undefined"

Solution:

  1. Verify previous node executed successfully

  2. Check field name spelling: {{node.field}} must match exactly

  3. Ensure previous node actually outputs that field

Issue: "Invalid JSON" error

Solution:

  1. Verify string is valid JSON before using string_to_json

  2. Check for escaped quotes or formatting issues

  3. Test JSON string in external validator

Issue: Array indexing fails

Solution:

  1. Verify array is not empty: check previous node output

  2. Use valid index: [0] for first item, not [1]

  3. Handle potential empty arrays in workflow logic



Complete Example: Step-by-Step Workflow Execution

Let's walk through a real workflow execution to see exactly what data is available at each step and how the workflow state changes.

Workflow Setup

Workflow Definition:

{
  "name": "API Analysis Workflow",
  "input_schema": {
    "type": "object",
    "properties": {
      "api_url": {
        "type": "string",
        "title": "API URL"
      },
      "recipient_email": {
        "type": "string",
        "title": "Recipient Email"
      }
    }
  }
}

📊 STEP 1: User Starts Workflow

User provides inputs:

{
  "api_url": "https://api.example.com/users/123",
  "recipient_email": "[email protected]"
}

Workflow State:

{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "[email protected]"
  },
  "nodes": {}
}

What you can reference:

  • {{api_url}}"https://api.example.com/users/123"

  • {{recipient_email}}"[email protected]"

  • ❌ No node outputs available yet


📊 STEP 2: Node 1 (fetch_data) Executes

Node Configuration:

{
  "name": "fetch_data",
  "node_type_name": "api_call",
  "input_config": {
    "url": "{{api_url}}",
    "method": "GET"
  }
}

After {{...}} substitution, node receives:

{
  "url": "https://api.example.com/users/123",
  "method": "GET"
}

Node executes and produces output:

{
  "status_code": 200,
  "response_body": {
    "id": 123,
    "name": "John Doe",
    "email": "[email protected]",
    "status": "active"
  }
}

Workflow State after Node 1:

{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "[email protected]"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "[email protected]",
        "status": "active"
      }
    }
  }
}

What you can now reference:

  • {{api_url}}"https://api.example.com/users/123"

  • {{recipient_email}}"[email protected]"

  • {{fetch_data.status_code}}200

  • {{fetch_data.response_body}}{"id": 123, "name": "John Doe", ...}

  • {{fetch_data.response_body.name}}"John Doe"

  • {{fetch_data.response_body.status}}"active"

  • ❌ Cannot reference nodes that haven't executed yet


📊 STEP 3: Node 2 (analyze_user) Executes

Node Configuration:

{
  "name": "analyze_user",
  "node_type_name": "claude_ask",
  "input_config": {
    "prompt": "Analyze this user data: Name is {{fetch_data.response_body.name}}, status is {{fetch_data.response_body.status}}",
    "model": "claude-3-5-sonnet-latest",
    "max_tokens": 500
  }
}

After {{...}} substitution, node receives:

{
  "prompt": "Analyze this user data: Name is John Doe, status is active",
  "model": "claude-3-5-sonnet-latest",
  "max_tokens": 500
}

Node executes and produces output:

{
  "content": "This user John Doe appears to be an active account holder in good standing."
}

Workflow State after Node 2:

{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "[email protected]"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "[email protected]",
        "status": "active"
      }
    },
    "analyze_user": {
      "content": "This user John Doe appears to be an active account holder in good standing."
    }
  }
}

What you can now reference:

  • ✅ All workflow inputs

  • ✅ All fetch_data outputs

  • {{analyze_user.content}}"This user John Doe appears to be..."

  • ❌ Cannot reference nodes that haven't executed yet


📊 STEP 4: Node 3 (send_report) Executes

Node Configuration:

{
  "name": "send_report",
  "node_type_name": "send_email",
  "input_config": {
    "recipient_emails": ["{{recipient_email}}"],
    "subject": "User Analysis Report",
    "body": "Analysis for {{fetch_data.response_body.name}}:\n\n{{analyze_user.content}}"
  }
}

After {{...}} substitution, node receives:

{
  "recipient_emails": ["[email protected]"],
  "subject": "User Analysis Report",
  "body": "Analysis for John Doe:\n\nThis user John Doe appears to be an active account holder in good standing."
}

Node executes and produces output:

{
  "message": "Email sent successfully",
  "email_id": "msg_abc123xyz"
}

Final Workflow State after Node 3:

{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "[email protected]"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "[email protected]",
        "status": "active"
      }
    },
    "analyze_user": {
      "content": "This user John Doe appears to be an active account holder in good standing."
    },
    "send_report": {
      "message": "Email sent successfully",
      "email_id": "msg_abc123xyz"
    }
  }
}

What you can now reference:

  • ✅ All workflow inputs

  • ✅ All outputs from all executed nodes

  • {{send_report.message}}"Email sent successfully"

  • {{send_report.email_id}}"msg_abc123xyz"


📊 STEP 5: Workflow Completes

If no output mapping is defined:

The workflow returns the last node's output:

{
  "message": "Email sent successfully",
  "email_id": "msg_abc123xyz"
}

If output mapping is defined:

{
  "output_mapping": {
    "user_name": "{{fetch_data.response_body.name}}",
    "analysis": "{{analyze_user.content}}",
    "email_status": "{{send_report.message}}"
  }
}

The workflow returns:

{
  "user_name": "John Doe",
  "analysis": "This user John Doe appears to be an active account holder in good standing.",
  "email_status": "Email sent successfully"
}

Key Observations from This Example

1. Data Accumulates as You Go:

After Node 1: Can reference → inputs + fetch_data
After Node 2: Can reference → inputs + fetch_data + analyze_user
After Node 3: Can reference → inputs + fetch_data + analyze_user + send_report

2. Cannot Reference Future Nodes:

  • At Node 1, you CANNOT use {{analyze_user.content}} (hasn't run yet)

  • At Node 2, you CANNOT use {{send_report.message}} (hasn't run yet)

3. String Interpolation:

  • "Name is {{fetch_data.response_body.name}}""Name is John Doe"

  • Numbers become strings: If id was in the string, "ID: {{fetch_data.response_body.id}}""ID: 123"

  • Objects become JSON: "Data: {{fetch_data.response_body}}""Data: {\"id\":123,\"name\":\"John Doe\"...}"

4. Exact Field Names Matter:

  • {{fetch_data.response_body}} (correct - matches ApiCallNodeOutput)

  • {{fetch_data.response}} (wrong - field doesn't exist)

  • {{analyze_user.content}} (correct - matches AskClaudeOutput)

  • {{analyze_user.response}} (wrong - field doesn't exist)

5. Check Node Documentation:

  • Each node type has specific input and output field names

  • Always refer to Node Reference for exact field names


Summary

Key Takeaways:

Data Flow:

  1. ✅ Workflows execute top-to-bottom, data flows linearly between nodes

  2. ✅ Each node can access workflow inputs and outputs from previous nodes

  3. ✅ Use {{...}} syntax to reference data from inputs and previous nodes

Type Handling: 4. ✅ All fields in workflow inputs, node inputs, and node outputs have defined types 5. ✅ Types must match when passing values between nodes 6. ✅ Type mismatches cause runtime errors, not design-time warnings 7. ✅ Check node detail pages to see input/output types

Automatic Conversions: 8. ✅ When embedded in strings, all values auto-convert to strings 9. ✅ Objects and arrays become JSON strings: "Data: {{obj}}" works 10. ✅ Numbers become text: "Count: {{num}}" works

Manual Conversions: 11. ✅ Use string_to_json to parse JSON strings to objects/arrays 12. ✅ Use run_javascript or run_python_code for complex transformations 13. ✅ Use dot notation and array indexing to extract specific values

Best Practices: 14. ✅ Test workflows incrementally to catch type errors early 15. ✅ Use explicit field access: {{api.response.data.id}} vs {{api.response}}

Remember: When in doubt, check the node's detail page for exact input/output types!


Last updated

Was this helpful?