> ## 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.

# Expressions overview

> JSONata and AI expressions — when to use which, and how they compose.

Expressions are how you reference and transform data inside step configurations. Anyreach supports two expression flavors:

| Flavor      | Syntax      | When to use                                                                                 |
| ----------- | ----------- | ------------------------------------------------------------------------------------------- |
| **JSONata** | `{{ ... }}` | Deterministic transformations: addressing fields, filtering arrays, basic math, type checks |
| **AI**      | `${ ... }`  | Free-text classification, extraction, generation: anything an LLM does well                 |

You can use both inside the same step (different fields), but you cannot mix them in the same expression — see [Condition step](/workflows/steps/condition) for the constraint.

## JSONata Expressions

JSONata is a query language for JSON. The minimum you need:

| Want to...                  | Write...                                                  |
| --------------------------- | --------------------------------------------------------- |
| Get a top-level input field | `{{ n3.customer_email }}` or `{{ input.customer_email }}` |
| Get a step's output field   | `{{ n3.body.id }}`                                        |
| Compare values              | `{{ n3.score > 50 }}`                                     |
| Boolean logic               | `{{ n3.score > 50 and n3.tier = 'gold' }}`                |
| Filter an array             | `{{ n3.contacts[tier = 'gold'] }}`                        |
| Map an array                | `{{ n3.contacts.email }}` (returns an array of emails)    |
| Default value               | `{{ n3.name ? name : 'friend' }}`                         |
| String concat               | `{{ n3.first_name & ' ' & n3.last_name }}`                |

See [JSONata cookbook](/workflows/expressions/jsonata-cookbook) for more.

## AI Expressions

`${...}` sends the contents (with `{{...}}` interpolations resolved first) to an LLM and replaces the expression with the response.

```jsonc theme={null}
"summary": "${a one-sentence summary of {{n3.transcript}}}"
"intent": "${classify the intent: billing, support, or sales. transcript: {{n3.transcript}}}"
"is_qualified": "${is the lead qualified? answer yes or no. transcript: {{n3.transcript}}}"
```

For AI to behave reliably, **be specific** about the response format. "answer yes or no" is much better than "is the lead qualified?".

See [AI expressions](/workflows/expressions/ai-expressions) for more.

## Pure vs interpolated

A field whose **entire value** is one expression is **pure**:

```jsonc theme={null}
"customer_id": "{{ n3.body.id }}"   → pure JSONata
"summary":     "${a summary of: {{n3.transcript}}}" → pure AI
```

A field with surrounding text is **interpolated** (the whole thing becomes a string):

```jsonc theme={null}
"message": "Hello {{ n3.body.first_name }}, welcome!"  → string
```

### Why this matters

Pure expressions **preserve types** — if `n3.body.id` is an integer, `customer_id` is an integer. Interpolated expressions always produce strings.

This affects HTTP request bodies (where APIs may require integer IDs, not strings) and Output schemas (where validation enforces types).

## Evaluation order

When a step is about to run, the platform evaluates expressions in its config:

1. **JSONata** is resolved first, top to bottom, against the current `ctx`
2. **AI** is resolved next, with `{{...}}` interpolations already replaced

This means you can reference upstream steps inside AI prompts:

```
"intent": "${what is the user's intent in this transcript? {{`Last turn`.body.text}}}"
```

The `{{...}}` is replaced with the actual transcript before the LLM sees the prompt.

## Where expressions are evaluated

Most fields in most step types support expressions. A few notable places:

| Step      | Expression-evaluated fields                                              |
| --------- | ------------------------------------------------------------------------ |
| HTTP API  | `url`, `headers` (values), `query_params` (values), `body` (recursively) |
| Condition | each condition's `condition`                                             |
| Output    | each `output_data` value                                                 |
| Action    | each `operation_input` value                                             |
| Code      | not in `code` itself — the code reads `ctx` directly                     |

Code steps don't use expressions inside the source code — your Python reads `ctx` natively.
