Expressions

Every node field is read in one of three ways, and that decides how you write the value. This is the most common source of broken flows.

Three kinds of field

KindHow it is readExample
textas writtenorders
JSONatathe whole field is one expressioninput.body.title
hybridwith {{…}} it is text with substitutions; without, the whole field is an expressionhttps://api.x/orders/{{input.body.id}}

The hybrid trap: with no {{ anywhere, the value is an expression, so a fixed URL must be quoted:

"https://api.example.com/orders"

Unquoted, JSONata looks for a path called https: and returns nothing.

Where data comes from

  • input is the output of the previous node, not the request payload.
  • outputs.<node id> is the output of any node that already ran.
  • variables.<key> is a flow variable.
{{ outputs.incoming.body.title }}
{{ outputs.save_order.first.id }}

When a node id contains a hyphen — the editor used to make ids like that — the name must be quoted, because a hyphen is minus in JSONata:

outputs."node-1760459115475".items
{{ outputs.'node-7'.text }}

Both quote styles work. Without them the expression reads as a subtraction and silently returns nothing. New nodes get underscore ids, so they need no quotes.

You can only read nodes that run earlier on your branch. A node on a parallel branch, or one that runs later, gives an empty value.

The trigger payload

Right after the trigger, input is the trigger output, so input.body.title works. Two nodes down, input is the middle node output instead. Reading the request by trigger name always works:

{{ outputs.incoming.body.title }}

JSONata traps

  • $now() takes a format picture, not a timezone name. $now('Europe/Kyiv') returns the string "Europe/Kyiv". The zone is the second argument: $now('[H01]:[m01]', '+0200').
  • An empty result is not an error. A path that does not match yields undefined, so the flow carries on with an empty value.
  • In a field that already contains {{…}}, everything outside the braces stays literal text.