Variables and data in flows
Every text box of a flow takes variables between double braces. They are filled in when the message goes out, for that contact:
Hi {{first_name|there}}! Your order #{{trigger.number}} of {{trigger.total:money}} is confirmed.
The shape of a variable
{{ source.path :formatting |default value }}
- the source says where the data comes from;
- the path goes into nested data;
- the formatting (optional, up to two) turns the data into something readable;
- the default value (optional) is what goes out when the data does not exist.
A {{…}} that is not a variable — {{ text with spaces }}, {{1}} — goes out as written.
Sources
| Variable | What it brings |
|---|---|
{{first_name}}, {{last_name}}, {{full_name}} | The contact's name (the first name falls back to the WhatsApp profile name) |
{{profile_name}}, {{email}}, {{phone}} | WhatsApp name, e-mail, phone with + |
{{field.city}} | A custom field of the contact |
{{flow.score}} | The answer a step kept, or the event of a Wait for event |
{{trigger.order.id}} | What the trigger brought: the webhook's data or the API's data |
{{$json.order.id}} | The same as trigger. — n8n's spelling |
{{now}} | The moment the message goes out |
Paths
A store's data is never flat. The path goes into it:
| Path | Reads |
|---|---|
trigger.billing.first_name | A field inside another |
trigger.line_items[0].name | The first item of a list |
trigger.line_items.0.name | The same, with a dot |
trigger.line_items[-1].name | The last item |
trigger["meta data"].gift | A key with a space or a special character |
A list of texts goes out comma-separated (vip, returning); a list of objects has no text of its
own — pick a field of an item, or count the items with :count.
Formatting
| Formatting | Example | Goes out |
|---|---|---|
:money | {{trigger.total:money}} | R$ 189,90 |
:money(USD) | {{trigger.total:money(USD)}} | US$ 189,90 |
:number, :number(2) | {{trigger.weight:number(2)}} | 1,50 |
:date | {{trigger.date_created:date}} | 22/09/2026 |
:datetime, :time | {{trigger.date_created:time}} | 11:03 (Brasília time; another zone: :time(America/New_York)) |
:title | {{trigger.billing.first_name:title}} | Maria da Silva |
:upper, :lower | {{trigger.coupon:upper}} | WELCOME10 |
:first_word | {{trigger.billing.first_name:first_word}} | Maria |
:digits | {{trigger.billing.phone:digits}} | 11987654321 |
:join, :join( / ) | {{trigger.tags:join( / )}} | vip / returning |
:count | {{trigger.line_items:count}} | 2 |
:truncate(20) | {{trigger.product:truncate(20)}} | Basic cotton t-shir… |
Numbers and dates come out the Brazilian way. The Portuguese names are accepted too (:moeda,
:data, :hora, :contar…). A number may come as 189.9, "189.90" or "1.234,56"; a date in
ISO, in seconds since 1970 or as dd/mm/yyyy. Data the formatting cannot read — a total of
"n/a" — counts as missing, and the default value goes out.
Default value
{{field.city|your city}} writes your city for whoever lacks the field. Without a default, a
missing value becomes empty text, and publishing warns about it.
In templates
A template step's variables have two more sources besides the contact:
- Trigger data — a path in the trigger's data, with optional formatting;
- Flow answers — what a step kept.
The default value is required, as in campaigns: Meta refuses an empty variable. A fixed text
takes variables too — Order {{trigger.number}} — to build a value with the data in the middle.
That is how an order confirmation goes out outside the 24-hour window, where only a template can be sent.
In links
The link of a URL button and the address of the outgoing webhook take variables too:
https://store.com/order/{{trigger.id}}?name={{first_name}}. Each value in the middle of the link
is encoded (a space or an & does not break the URL); a variable at the start is the base itself —
{{trigger.tracking_url}}. The link has to become an https:// address, or the run ends with the
reason invalid link.
Button titles — reply buttons, the list's button and the URL button — take variables too:
Pay {{trigger.total:money}}. WhatsApp caps a title at 20 characters, so what the data made of it
is cut with … at that cap; a title that would come out empty uses the fixed text written around
the variables, and two reply buttons with the same title get a number (Yes 2). A list's options
take variables in the title and the description: the title is cut at 24 characters and numbered when
it comes out the same as another option's; the description is cut at 72 and left out if the data
leaves it empty. A section's title takes variables too and is cut at 24; with more than one
section (when WhatsApp requires it) it never comes out empty.
In conditions and fields
- The Trigger data rule compares a path —
statusequal toprocessing,totalgreater than500. The comparisons are: equals, is not, contains, does not contain, starts with, is one of (a comma-separated list), is empty, is filled in, greater, greater or equal, less, less or equal. The compared value takes variables too:{{field.limit}}. - Set field can add and subtract (an order counter, the amount spent), add options to a list and remove them, write the current moment or clear the field.
- Delay can wait until a date in the data — one hour before
{{trigger.appointment.start}}, for example — and says what to do when the date has passed.
Validation
When publishing, the flow warns about formatting that does not exist (an error), an unknown source, a path that is not in the webhook's captured samples and a variable without a default (warnings). Under each box, the editor shows the message filled with the captured sample.