---
title: "Passing Data Between Steps"
url: https://flexie.io/resources/workflows/passing-data-between-steps
description: "How a step uses what the trigger and earlier steps produced. The __data namespace, the workflow's shared notepad, plus how listeners, the webhook action, the AI step, and Store a value all write into it."
---

# Passing Data Between Steps

Last updated 23 May 2026

![The __data notepad in a workflow, written by earlier steps and read by later steps](https://flexie.io/image/resources/workflows-passing-data-between-steps.png)

As a workflow runs, it gathers information: the data the trigger carried, values earlier steps computed, the ids of records it created, replies from webhooks. All of it is collected in one place, a shared namespace called **`__data`**, and any **later** step can read it with [Flexie Scripting](https://flexie.io/resources/flexie-scripting/overview):

```
{{ __data.incoming_email.subject }}
{{ __data.__entity_inserted_id }}
{{ __data.my_stored_value }}

```

Think of `__data` as a notepad that every step in the run can read, and that specific steps write to. It is scoped to **this workflow, for this record**. There is no leakage between records or between workflows, and within a run there is no expiry: anything written stays available to everything downstream.

> **One rule that ties back to the [tree](https://flexie.io/resources/workflows/runtime-parallel-and-tree):** a step can only rely on data that was produced **above it** in the tree. If step B needs what step A produced, B must be a **descendant** of A, not a sibling running alongside it.

## What writes into `__data`

### 1\. The trigger or listener signal

When a workflow starts from a **listener**, the signal's data is placed into `__data` before the first step runs. So the very first action can already use it. The common signals and what they provide:

| Trigger signal                        | Read it as                                                                           | Useful fields                                                                         |
| ------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| An email arrives                      | \_\_data.incoming\_email                                                             | from\_email, from\_name, subject, text, html, cc\_emails, date\_received, attachments |
| An SMS or WhatsApp is received        | \_\_data.incoming\_sms                                                               | sender\_number, to\_number, text, date\_received                                      |
| A message is delivered                | \_\_data.delivered\_sms                                                              | text, date\_delivered                                                                 |
| A task is created                     | \_\_data.last\_task                                                                  | task, due\_date, category, owner, created\_by                                         |
| A task is updated                     | \_\_data.updated\_task                                                               | (same shape as a new task)                                                            |
| A note is added                       | \_\_data.last\_note                                                                  | note, note\_text, category, author                                                    |
| A mention in a note                   | \_\_data.mention                                                                     | note, note\_text, author, mentioned\_user                                             |
| A comment is added                    | \_\_data.last\_comment                                                               | note, comment, comment\_text, author                                                  |
| A page is visited or link hit         | \_\_data.page\_visit                                                                 | url, utm\_\*, external\_referer, date\_hit, ip                                        |
| A web request hits a dynamic endpoint | \_\_data.<body\_field> for each body field, plus \_\_data.\_\_headers.\* for headers | see [Dynamic Endpoints, Receiving data](https://flexie.io/resources/dynamic-endpoints/receiving-data)  |

### 2\. The "Store a value" action

The most direct way to put something on the notepad yourself. You give it a **name** and a **value** (computed with Flexie Scripting), and from then on it is available as `{{ __data.<name> }}`. Use it to compute something once and reuse it across many later steps.

### 3\. Steps that create records

When a step **creates a record**, the new record's id is placed in `__data.__entity_inserted_id` (and its type in `__data.__entity_type`). This is how a later step references the exact record an earlier step just created, for example, record a payment against the invoice this run just generated.

### 4\. The Webhook action

A **Webhook** action stores the other system's reply under the name you choose (default `webhook`), so a later step can branch on the response or use values from it: `{{ __data.webhook }}`, and the status code as a convenient companion value.

### 5\. The AI step

An **AI step** stores the model's output (default name `__ai_response`) so later steps can use what the AI produced, for example, send the AI-drafted reply, or branch on the AI's verdict.

## Reading values

Reach into the data with dots, exactly like record fields:

```
Hi {{ __data.incoming_email.from_name | default("there") }},

We received your message:
"{{ __data.incoming_email.subject }}"

```

Combine it with the [Flexie Scripting toolkit](https://flexie.io/resources/flexie-scripting/function-reference) to reshape or guard it:

```
{# A received payload may be JSON text, decode then read it #}
{% set order = __data.webhook | json_decode %}
Order {{ order.id }} total: {{ jsonPath(order, "$.totals.grand") | number_format(2) }}

```

Always guard values that might be absent with `default(...)` or an `{% if %}`. A signal field can be empty, and a webhook might not return what you expect.

## A worked example

> **Trigger (listener):** an email arrives in the support mailbox. **Decision (virtual condition):** does `{{ __data.incoming_email.subject }}` contain "invoice"? **Yes:** **Create a case**, subject = `{{ __data.incoming_email.subject }}`, description = `{{ __data.incoming_email.text }}`. Then **Store a value** `case_ref` \= the new case id (`{{ __data.__entity_inserted_id }}`). Then **Send an email** acknowledging case `{{ __data.case_ref }}`.

Each step uses what the ones above it produced, that is `__data` doing its job.

## Sharing across workflows (briefly)

`__data` is private to one run. If you genuinely need a value to outlive a run and be read elsewhere, the **Set shared memory** action writes to a short-lived shared store that other workflows (and templates) can read with `memoryGet("key")`. Use it sparingly, it is for cross-workflow signals, not general data passing.

## Next steps

* [Runtime, parallel execution & the tree](https://flexie.io/resources/workflows/runtime-parallel-and-tree): why data only flows downward.
* [Dynamic Endpoints, Receiving data](https://flexie.io/resources/dynamic-endpoints/receiving-data): how an incoming web request lands in `__data`.
