Skip to content
Expedify
Your First AI Agent

Module · Building the Workflow

Passing data between nodes

Lesson 4 of 8 · 9 min

Everything so far has been about one node at a time. This lesson is about the wire between them — and it is the lesson that decides whether you can build anything at all. Four correctly configured nodes that don't share data are four separate jobs. What turns them into a workflow is that a node can reach back and use what an earlier node produced.

There is exactly one way to do that, and it looks like this:

{{ alias.field }}alias names the node you want. field names the piece of its output you want.

The alias is the node's name

You already met the alias in lesson 3: the smaller string printed under a node's label. In lesson 1's workflow the four nodes are aliased new_contact, welcome, qualify and record. Those are not decoration — they are the namespace. Rename a node and every reference to it has to change too, which is the main reason to pick a name you can live with early.

Three references, one workflow

Every value below starting with {{ is a live reference. Read each one as “go to that node, take that field”.

Scroll for all 4 steps →

Read the two on record. entity_id is {{new_contact.new_data.id}} — which record to update, taken from the trigger. And buried inside json_data is {{qualify.output}} — the word the AI agent decided on. That single reference is the entire reason the workflow is worth building: without it, the CRM write would have to be a guess.

The trap that catches everybody

Look closely at the trigger reference. It is not {{new_contact.id}}. It is {{new_contact.new_data.id}}.

A Database Change Trigger nests the row under new_data. It has to, because it also hands you old_data — the row as it was before the change — and operation, telling you whether this was an INSERT, an UPDATE or a DELETE. Writing {{new_contact.email}} gets you nothing, silently. This is the single most common broken reference in Expedify.

It is also, once you see it, a gift: {{new_contact.old_data.lead_status}} and {{new_contact.new_data.lead_status}} together let a workflow ask “what actually changed?” — which is how you build “when a deal becomes Won”, rather than “whenever a deal is touched”.

What each node hands you

You cannot reference a field a node does not produce. Here are the outputs of the nodes this course has used so far — these are the product's own field names, not a summary:

Reference shapes for the nodes used in this path. Every node's panel lists its own.

Database Change Trigger

Reference it with
{{alias.new_data.<column>}}
What you get
The changed row. Also .old_data.<column> and .operation.

Scheduler

Reference it with
{{alias.triggered_at}}
What you get
The time it ran. Nothing about any record.

User Message Trigger

Reference it with
{{alias.message}}
What you get
What the person typed. Also .chat_history.

Webhook Trigger

Reference it with
{{alias.payload.<field>}}
What you get
The JSON that was POSTed. Also .headers and .params.

Core Agent

Reference it with
{{alias.output}}
What you get
The agent's answer, as text.

CRM Manager

Reference it with
{{alias.results}}
What you get
A list from a search. A single record is .entity.id.

Database Query

Reference it with
{{alias.data}}
What you get
The rows, as a list of objects.

Loop

Reference it with
{{alias.current_item.<field>}}
What you get
The one item this pass is on.

Campaign Send

Reference it with
{{alias.message_id}}
What you get
Proof it sent. Also .send_result.

You do not have to memorise this. The builder has a variables panel that lists what is actually in scope at the node you have open — and it is generated from the same schema this table is. When you are unsure, look there rather than guessing: a guessed field name is indistinguishable from a correct one until the workflow runs.

A reference to a list is not a reference to a thing

Lesson 2's Version B is the clearest example in the course. The Database Query hands back {{find.data}} — a list of contacts. You cannot email a list. So the Loop takes that list and, one pass at a time, offers up {{each.current_item.id}} — a single contact.

List in, item out

The Loop's whole job is turning “many” into “one at a time”, so the node after it can work the way every other node does.

Loop Body

Scroll for all 4 steps →

Notice that welcome is the same node type doing the same job as in Version A. Only its reference changed — {{each.current_item.id}} instead of {{new_contact.new_data.id}}. That is the shape of most real editing work: not new nodes, just a different source for the same field.

What a broken reference looks like

This is the failure you will hit most, so it is worth recognising on sight. A reference that names a node or a field that isn't there does not usually crash. It resolves to nothing, and the node carries on with a hole where the value should be.

  • An email that arrives addressed to nobody, or a template variable rendered as an empty string.
  • A CRM write that “succeeds” and changes nothing, because the value it was told to write was empty.
  • A {{...}} printed literally in the output — the surest sign of the three, and the easiest to miss in a long message.

Three things to check, in this order. Is the alias spelled exactly as the node prints it? Is the field one the node actually produces (the table above)? And — if it is a Database Change Trigger — did you remember new_data? That third question resolves more broken workflows than the other two combined.

Key takeaways

  1. {{ alias.field }} is the only way data moves between nodes. There is no second mechanism.
  2. The alias is the name printed under the node's label — rename the node and the references must follow.
  3. A Database Change Trigger nests its row under new_data. {{trigger.email}} is empty; {{trigger.new_data.email}} is the value.
  4. You can only reference what a node actually outputs — check its panel rather than guessing.
  5. A list (results, data) needs a Loop before a node can act on one item.
  6. Broken references fail quietly. Empty values and a literal {{...}} in the output are what they look like.

Try it yourself

Go back to Version B above and trace one contact's id all the way through: it starts as a column in the database, becomes a row in {{find.data}}, becomes {{each.current_item.id}} on one pass of the loop, and ends up as the recipient of an email. Four names for the same value. Being able to follow that chain — and say which node renamed it — is the skill this whole path is building toward.

Next: Branching — what happens when the workflow should stop going in a straight line.