# Field references

> Point a question at one field of a JSON state with the path syntax, without paying for that field twice.

When the state is JSON, `instructions` and `criteria` can point at a field with `{{path}}`. The model reads that field where it sits in the state, and you do not pay for it twice.

## Syntax

A path is a dot-separated chain of keys, with array indexes in square brackets:

| Reference | Points at |
| --- | --- |
| `{{ticket.body}}` | The `body` key inside `ticket` |
| `{{bericht.tekst}}` | The `tekst` key inside `bericht` |
| `{{ticket.messages[0].text}}` | The `text` key of the first item in `ticket.messages` |

Array indexes start at 0.

### Keys with other characters

Keys made of letters, digits, underscores and hyphens can be written as they are. A key with any other character, such as a space or a dot, goes in square brackets with double quotes:

```text
{{customer["full name"]}}
```

## Pointer semantics

Dex does not paste the field's value into the question. The rendered question names the path, and the rendered state shows every field with its path, so the model reads the field in place.

Two things follow:

- **You are not billed twice.** The field's value counts once, in `state_tokens`. The reference itself adds only the few question tokens of the path.
- **Many questions can point at one field.** Three questions that each reference `{{ticket.body}}` still pay for the body once.

## Validation

Every referenced path must exist in the state. The check runs before anything is charged.

| Problem | Error |
| --- | --- |
| The path is not in the state | 422 `state_path_not_found` |
| The state is a string, not JSON | 422 `state_not_json` |

A missing path error names the question field that holds the reference in `param`, and the path in `message`:

```json
{
  "error": {
    "type": "validation",
    "code": "state_path_not_found",
    "message": "Question 'spoed' refers to {{bericht.txt}}, which is not in the state.",
    "param": "questions.spoed.instructions",
    "request_id": "req_01M5CJXHG0M9S346Q3D25VT4F5"
  }
}
```

## Literal braces

- `{{` without a matching `}}` is literal text.
- `\{{` escapes a literal `{{` that would otherwise start a reference.

In a JSON string the backslash itself must be escaped, so the text `\{{` is written `"\\{{"` in the request body:

```json
{ "instructions": "Does {{post.text}} contain template syntax such as \\{{name}}?" }
```

## SDK helpers

Until the SDKs are on PyPI and npm, install them from [Downloads](/docs/reference/sdks/#downloads).

`ref` builds a reference from path parts and adds brackets and quotes where a key needs them. `escape_braces` (Python) and `escapeBraces` (TypeScript) turn every `{{` in a text into `\{{`.

```python tab="Python"
from thinqit_dex import ref, escape_braces

ref("ticket", "messages", 0, "text")  # "{{ticket.messages[0].text}}"
ref("customer", "full name")          # '{{customer["full name"]}}'
escape_braces("a {{b}}")               # "a \\{{b}}"
```

```ts tab="TypeScript"
import { ref, escapeBraces } from "@thinqit/dex";

ref("ticket", "messages", 0, "text"); // "{{ticket.messages[0].text}}"
ref("customer", "full name");         // '{{customer["full name"]}}'
escapeBraces("a {{b}}");              // "a \\{{b}}"
```
