Documentation Index
Fetch the complete documentation index at: https://docs.anyreach.ai/llms.txt
Use this file to discover all available pages before exploring further.
JSONata is a small query language for JSON. This page is a list of patterns you’ll reach for often.
For the full spec, see jsonata.org. The patterns below are the ones that cover ~90% of workflow use.
Reference fields
| Want | JSONata |
|---|
| Top-level input field | {{ email }} |
| Nested input field | {{ contact.email }} |
| Step output field | {{ `Lookup contact`.body.id }} |
| Top-level by full path | {{ ctx.input.email }} |
Backticks are required when a step name contains spaces or special characters.
Comparisons and logic
{{ score > 50 }}
{{ tier = 'gold' }} // single =, not ==
{{ tier in ['gold', 'platinum'] }}
{{ score > 50 and tier = 'gold' }}
{{ found = false or status_code != 200 }}
{{ not (tier = 'free') }}
Defaults and null-checks
{{ name ? name : 'friend' }} // ternary
{{ $exists(name) ? name : 'unknown' }} // explicit existence
{{ contact.email ?? 'no-reply@example.com' }} // null-coalesce
String operations
{{ first_name & ' ' & last_name }} // concat
{{ $uppercase(name) }}
{{ $lowercase(email) }}
{{ $trim(' hello ') }}
{{ $contains(message, 'urgent') }}
{{ $substring(phone, 0, 3) }}
{{ $replace(phone, '-', '') }}
Number operations
{{ price * 1.08 }} // tax
{{ $round(price * quantity, 2) }}
{{ $floor(score) }}
{{ $abs(delta) }}
{{ $sum(items.price) }} // aggregate
{{ $count(items) }}
{{ $average(scores) }}
Array operations
// indexing
{{ contacts[0] }}
{{ contacts[-1] }} // last element
// filter
{{ contacts[tier = 'gold'] }} // all gold contacts
{{ contacts[score > 50][0] }} // first high-scoring
// map (project a field)
{{ contacts.email }} // ['a@b', 'c@d']
{{ contacts.{ "id": id, "label": name } }} // reshape each item
// boolean over array
{{ $count(contacts[tier = 'gold']) > 0 }} // any gold?
{{ $count(contacts[tier = 'gold']) = $count(contacts) }} // all gold?
// concat / flatten
{{ list_a & list_b }} // arrays merge with &? — use [list_a, list_b].**
{{ $each(contacts, function($v) { $v.email }) }}
Object operations
{{ $keys(contact) }} // ['id', 'email', ...]
{{ $merge([contact, {"verified": true}]) }} // shallow merge
{{ contact.{ "name": $.name, "tier": $.tier } }} // pick fields
Conditionals
{{ score > 50 ? 'qualified' : 'not qualified' }}
// chained
{{ score > 90 ? 'a' : (score > 70 ? 'b' : (score > 50 ? 'c' : 'fail')) }}
// switch-style
{{ {
"high": score > 80,
"medium": score > 50 and score <= 80,
"low": score <= 50
}[$$ = true] }} // returns the matching key
Date operations
{{ $now() }} // current ISO timestamp
{{ $millis() }} // current epoch ms
{{ $fromMillis(ms) }}
{{ $toMillis(iso_string) }}
For richer date math, do it in a Code step instead.
Common workflow snippets
Build a Slack mention
"text": "{{ '<@' & user_id & '> new lead from ' & $.name }}"
Extract domain from email
"domain": "{{ $substringAfter(email, '@') }}"
First non-null
"phone": "{{ contact.mobile ?? contact.work ?? contact.home }}"
Pick by enum
"slack_channel": "{{ {
'billing': 'C_BILLING',
'support': 'C_SUPPORT',
'sales': 'C_SALES'
}[intent] }}"
Sum with filter
"qualified_revenue": "{{ $sum(leads[score > 50].arr) }}"
Debugging tips
When an expression isn’t producing what you expect:
- Inspect
ctx at the failing step. Open the run trace, click the step before the failing one, and copy the visible ctx.
- Test the expression manually. Paste
ctx and the expression into try.jsonata.org — same engine.
- Add a Code step temporarily that just returns the raw value, so you can see what JSONata sees.
- Check for backticks around step names with spaces (most common bug).