> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anyreach.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Data flow

> How data moves through a workflow's steps.

A workflow execution carries a single accumulating object — the **context** (`ctx`) — that grows as each step runs. Understanding `ctx` is the key to writing expressions that reference data from any earlier step.

## The context object

When a workflow starts, `ctx` contains the trigger data and any input provided:

```json theme={null}
{
  "input": { ... trigger event data or manual input data ... }
}
```

After each step runs and produces output, `ctx` gets a new key named after that step ID:

```json theme={null}
{
  "n1": { "customer_email": "alice@example.com" },
  "n2": {
    "status_code": 200,
    "headers": { ... },
    "body": { "id": "c_123", "first_name": "Alice", "tier": "gold" }
  }
}
```

By the time the workflow reaches an Output step, `ctx` contains every step's output.

## Referencing data with expressions

Suppose your workflow has executed two steps so far:

* **n1**: a manual **Input** step
* **n2**: an **Action** step

The context (`ctx`) at this point might look like:

```json theme={null}
{
  "n1": { "customer_email": "alice@example.com" },
  "n2": {
    "status_code": 200,
    "headers": { ... },
    "body": { "id": "c_123", "first_name": "Alice", "tier": "gold" }
  }
}
```

To read the `customer_email` value produced by the Input step `n1`, use this expression:

```
{{n1.customer_email}}
```

In this expression, `n1` refers to the step ID, and `customer_email` is the specific key from that step's output.

See [Expressions overview](/workflows/expressions/overview) for the full syntax reference.

## What each step contributes to ctx

| Step          | What it adds to `ctx`                                                          |
| ------------- | ------------------------------------------------------------------------------ |
| **Trigger**   | The trigger payload (conversation data, timer metadata, or external app event) |
| **Action**    | Whatever the action returned from the native or Pipedream operation            |
| **Condition** | The label of the matched branch (or `null` for default)                        |
| **Code**      | Whatever the Python function returned                                          |
| **HTTP API**  | `{ status_code, headers, body }`                                               |
| **Output**    | The defined output fields — also ends the execution path                       |
| **Wait**      | `{ waited_seconds }`                                                           |
| **Input**     | The user-defined input fields provided at run time                             |

## Type preservation

Expressions preserve types when the entire field value is an expression. When mixed with text, they coerce to string.

```jsonc theme={null}
// Whole-field expression — preserves the original type
"account_id": "{{ Lookup.body.id }}"        → 12345   (integer)

// Interpolated expression — always a string
"message": "Account {{ Lookup.body.id }}"   → "Account 12345"  (string)
```

This matters when downstream steps (HTTP request bodies, Output schemas) expect specific types.

## Working with lists

There is no built-in loop step. To iterate over a list, use a Code step:

```python theme={null}
def run(ctx):
    contacts = ctx["Lookup"]["body"]["contacts"]
    results = []
    for contact in contacts:
        r = requests.post("https://api.example.com/notify", json={"id": contact["id"]})
        results.append(r.json())
    return {"results": results}
```

For long-running loops where each iteration might fail independently, consider invoking sub-workflows asynchronously from the Code step — see [Sync vs async](/workflows/execution/sync-vs-async).

## Referencing Upstream Step Data in the Current Step

Each step’s configuration form includes smart input components that allow you to easily select properties from upstream steps. This makes it simple and error-free to insert expressions referencing outputs from previous steps. When you choose a variable using the variable selector, the correct expression is automatically inserted into your configuration field.

<Frame caption="Variable selector pops up when you click in an input field that accepts expressions">
  <img src="https://mintcdn.com/anyreach/pzUOM56fFP3HcHci/images/workflows/variable.png?fit=max&auto=format&n=pzUOM56fFP3HcHci&q=85&s=b0a25f8e48e5be4931ff390733c644d1" alt="Variable selector open in builder" width="1536" height="1182" data-path="images/workflows/variable.png" />
</Frame>

<Frame caption="After selecting a variable, the builder inserts the corresponding expression into your configuration field.">
  <img src="https://mintcdn.com/anyreach/pzUOM56fFP3HcHci/images/workflows/variable-selected.png?fit=max&auto=format&n=pzUOM56fFP3HcHci&q=85&s=d788f0d4d079972e3c3aadebba2417f3" alt="A variable inserted into field using variable selector" width="1490" height="1092" data-path="images/workflows/variable-selected.png" />
</Frame>

Here clicking on the "name" property of the "Input" step node automatically builds the expression `{{n1.name}}` where `n1` is the id of the `Input` step.
