# 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:**

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

### Reference Node Outputs

```
{{node_name.output_field}}
```

**Example:**

```json
{
  "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**:

```json
{
  "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                     | `"hello"`, `"user@example.com"` |
| `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:

```json
{
  "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](/reference/nodes.md) - 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**:

```json
// 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**:

```json
// 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": "test@example.com"}]}

Node 2: send_email
  Input: recipient (string)
```

**Incorrect Configuration:**

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

Error: Cannot assign object to string field.

**Correct Configuration:**

```json
{
  "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)

```json
{
  "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)

```json
{
  "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

```json
{
  "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

```json
{
  "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):**

```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):**

```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:**

```json
// 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

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

**Solution B:** Format it nicely in a string

```json
{
  "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

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

### Scenario 2: String ID to Number ID

**Problem:**

```json
// 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
// JavaScript
return parseInt(user_id);  // Converts "12345" → 12345
```

```python
# Python
return int(user_id)  # Converts "12345" → 12345
```

### Scenario 3: Multiple Values to Array

**Problem:**

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

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

**Solution:** Use array construction

```json
{
  "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": "user@example.com", "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](/reference/nodes.md) 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:

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

Use:

```json
{"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 the [Node Reference](/reference/nodes.md) for available utility nodes.

***

## 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

***

## Related Documentation

* [Node Reference](/reference/nodes.md) - Complete node type documentation
* [Node Reference](/reference/nodes.md) - Type conversion nodes
* [Troubleshooting Guide](https://github.com/PixelML/agenticflow-docs/blob/main/docs/12-support/troubleshooting/troubleshooting-guide.md) - Common workflow errors

***

## 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:**

```json
{
  "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:**

```json
{
  "api_url": "https://api.example.com/users/123",
  "recipient_email": "admin@company.com"
}
```

**Workflow State:**

```json
{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "admin@company.com"
  },
  "nodes": {}
}
```

**What you can reference:**

* ✅ `{{api_url}}` → `"https://api.example.com/users/123"`
* ✅ `{{recipient_email}}` → `"admin@company.com"`
* ❌ No node outputs available yet

***

### 📊 STEP 2: Node 1 (fetch\_data) Executes

**Node Configuration:**

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

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

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

**Node executes and produces output:**

```json
{
  "status_code": 200,
  "response_body": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "status": "active"
  }
}
```

**Workflow State after Node 1:**

```json
{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "admin@company.com"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com",
        "status": "active"
      }
    }
  }
}
```

**What you can now reference:**

* ✅ `{{api_url}}` → `"https://api.example.com/users/123"`
* ✅ `{{recipient_email}}` → `"admin@company.com"`
* ✅ `{{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:**

```json
{
  "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:**

```json
{
  "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:**

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

**Workflow State after Node 2:**

```json
{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "admin@company.com"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com",
        "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:**

```json
{
  "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:**

```json
{
  "recipient_emails": ["admin@company.com"],
  "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:**

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

**Final Workflow State after Node 3:**

```json
{
  "inputs": {
    "api_url": "https://api.example.com/users/123",
    "recipient_email": "admin@company.com"
  },
  "nodes": {
    "fetch_data": {
      "status_code": 200,
      "response_body": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com",
        "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:

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

**If output mapping is defined:**

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

The workflow returns:

```json
{
  "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](/reference/nodes.md) 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!

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.agenticflow.ai/workflows/data-flow-and-types.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
