# 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](https://docs.agenticflow.ai/reference/nodes) - 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](https://docs.agenticflow.ai/reference/nodes) 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 [Utilities Nodes](https://github.com/PixelML/agenticflow-docs/blob/main/docs/11-reference/nodes/by-category/utilities/README.md) 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

***

## Related Documentation

* [Node Reference](https://docs.agenticflow.ai/reference/nodes) - Complete node type documentation
* [Utilities Nodes](https://github.com/PixelML/agenticflow-docs/blob/main/docs/11-reference/nodes/by-category/utilities/README.md) - Type conversion nodes
* [Troubleshooting Workflows](https://github.com/PixelML/agenticflow-docs/blob/main/docs/12-support/troubleshooting/workflows.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](https://docs.agenticflow.ai/reference/nodes) 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!

***
