Passing Data Between Steps

Last updated 23 May 2026

The __data notepad in a workflow, written by earlier steps and read by later steps

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:

{{ __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.

STEPS Trigger fires Create record Store a value Webhook Send email (reads __data) __data (the notepad) incoming_email: { subject, text, … } __entity_inserted_id: 4218 case_ref: "C-4218" webhook: { ok: true, body: … } read by later steps

One rule that ties back to the 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

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