Expressions
What you can write between {{ and }}, and what you get back.
Any field can take an expression. GoRunner fills it in when the step runs. To insert one without typing, see Use data from earlier steps.
Where data comes from
{{trigger.body.email}}- The trigger’s data, such as a form’s field.
{{steps.trigger.body.email}}is the same. {{steps.step_1.id}}- An earlier step’s output, by the step’s name.
{{steps.step_2.result}}- What a Code step returned.
{{steps.step_3.item}}- Inside a loop: the current item.
.indexis its place from 0,.totalthe count. {{steps.step_4.resumeData}}- What was sent to a held step’s resume link, such as an approval’s answer.
{{steps.step_1.error}}- In an error step: why the step failed.
{{$vars.API_URL}}- A workflow variable, plain or sensitive.
{{$execution.id}}- This run’s ID. Also
$execution.modeand$execution.startedAt. {{$workflow.id}}- This workflow’s ID.
{{$workspace.id}}is its workspace’s.
Where do I find a step’s name?
Insert a value with the data picker, then point at it: the full path shows, such as {{steps.step_1.id}}.
Paths
Dots go into an object; a number in brackets picks from a list: {{steps.step_1.body.items[0].name}}. A path that leads nowhere gives nothing, and the field is empty.
What you get back
- Only an expression
- It keeps its type.
{{steps.step_1.body}}passes the object itself, a number stays a number. - Mixed with text
- Each expression becomes text: a string as it is, anything else as JSON.
Order {{trigger.body.id}} received.
Warning In a JSON body you write yourself, put an object as the whole value, unquoted: {"order": {{trigger.body}}}. Inside quotes, the first quote in the data breaks the JSON.
JavaScript
Anything that isn’t a plain path runs as JavaScript, with the same data:
{{ steps.step_1.body.items.length > 3 ? "big" : "small" }}
{{ $json.body.email.split("@")[1] }}
{{ $ifEmpty(trigger.body.name, "friend") }}$json- The trigger’s data.
$triggeris the same, and$stepsissteps. $('step_1')- A step’s output, by name.
$if(test, a, b)awhen the test is true, elseb.$ifEmpty(value, other)otherwhen the value is missing or empty.$now- Now, as a date.
$todayis the same.
An expression may run for 100 ms and return up to 16 MB. One that throws gives nothing. One that runs too long or uses too much memory fails the step, and says which expression.
Numbers and dates for people
Intl.NumberFormat, Intl.DateTimeFormat and toLocaleString work in expressions and Code steps:
{{ new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" }).format(1234.5) }}
// 1.234,50 €They know about 1,100 locales and write them as Node does, with each locale’s own digits and calendar. A locale GoRunner doesn’t have falls back as in Node, to its language, then to en-US: sv-XX is written as sv, and xx as en-US. en-CA is one it has, so it gives 2026-10-26:
{{ new Intl.DateTimeFormat("en-CA", { timeZone: "Europe/Berlin", year: "numeric", month: "2-digit", day: "2-digit" }).format(new Date("2026-10-26T02:00:00Z")) }}
// 2026-10-26From v2026.10.18 to v2026.10.29, only 11 locales were known, and others wrote like their language or en-US: en-CA gave 10/26/2026.
Test data while you build
Before anything has run, the data picker offers each trigger’s and step’s sample, so the names are real. After a test, it offers what the test returned. You can pin a step’s output so test runs reuse it; production always runs the step.