Sign-up and API keys are open. Paid top-ups open soon.What changed
Docs menu

Concepts

State

The material every question is about, sent once per request as a string or as JSON, with its limits and billing.

View as Markdown

The state is the material you want decisions about: a support ticket, a forum post, a lead, a model's output. Every request has exactly one state, and all of its questions share it.

String or JSON

The state field takes one of three shapes:

Shape Use it when Example
A string The material is plain text, such as a message body. "This is the third time I am writing."
A JSON object The material has named fields, such as a subject, a body and a channel. It needs at least one field. { "ticket": { "subject": "...", "body": "..." } }
A JSON array The material is a list, such as the messages in a thread. It needs at least one item. [ { "from": "customer", "text": "..." } ]

A string is the simplest choice. JSON lets you do two more things:

  • Keep context the model can read, such as the channel or how long someone has been a customer.
  • Point a question at one field with a field reference, such as {{ticket.body}}. Field references need a JSON state: with a string state they give 422 state_not_json.

Here is the state of the Dutch support example from the contract. The questions point at {{bericht.tekst}}, and the model can also read the channel, the subject and the customer fields:

{
  "bericht": {
    "kanaal": "webformulier",
    "onderwerp": "Accu laadt niet meer op",
    "tekst": "Sinds de software-update van vorige week laadt de accu van mijn e-bike niet meer op. Ik heb de fiets pas drie maanden en heb hem elke dag nodig voor mijn werk. Kan ik hem omruilen of komt er een monteur langs?"
  },
  "klant": { "klant_sinds": "2026-06-14", "bestellingen": 2 }
}

How JSON is rendered

Dex does not show your JSON to the model as raw text. It renders it as lines, one per field, and each line carries the field's path. Keys keep the order you sent them in.

This has three effects:

  • A field reference can name a path, and the model finds that field in the state by its path.
  • Key names are part of what the model reads. Clear names such as body or customer_since help more than f1 or x.
  • Rendered JSON is what you are billed for, so the token count of a JSON state is not the same as the byte count of your JSON.

Limits

Limit Value Error
String length 1 to 65,536 characters 422 invalid_value
String content Not only whitespace 422 invalid_value
JSON nesting At most 32 levels deep 422 invalid_value
JSON key length 1 to 256 characters per key 422 invalid_value
JSON object At least 1 field 422 invalid_value
JSON array At least 1 item 422 invalid_value
State tokens after rendering 16,384 413 state_too_long
Whole request body 256 KiB 413 body_too_large

An empty object or array inside the state, such as { "ticket": { "tags": [] } }, is fine: only the state as a whole must not be empty. A string of only whitespace (spaces, tabs, line breaks) is rejected because there is nothing for the questions to be about.

Token limits are counted after rendering, with the same tokenizer that counts your bill, so the limit and the billed state_tokens always agree. The full list is in Limits.

The state is data

The model reads the state as material, never as instructions. From dex-1.0.1, text in the state cannot act as part of the prompt: a </state> or <state> tag in your text, and markup that means something special to the model (<think>, <tool_call>, <|im_end|> and similar), are read as plain text, and the prompt reminds the model that the state is data. This also applies to text in your questions.

A model can still be swayed by persuasive text inside the state, such as "the answer is billing". For moderation and guardrail decisions with real consequences, keep a person in the loop and send "fallback": "never" (see Fallback and served_by). Known limits has the measured rate and a guardrail question to route on.

Sent once, billed once

The state is rendered, tokenized and read by the model once per request. Every question in that request reuses the model's reading of it, and it is freed when the request ends. It is never kept across requests.

You pay for the state once, as usage.state_tokens, however many questions share it. Asking five questions in one request bills the state once. Asking the same five questions in five requests bills it five times. See Tokens and billing.

Tips

  • Send what the decision needs. Every state token is billed. Leave out fields that cannot change the answer, such as internal ids or HTML markup.
  • Ask together. Put all the questions about one state in one request.
  • Name keys clearly. The model reads key names as part of each path.
  • Keep rules out of the state. Definitions and edge cases belong in a question's criteria, where they apply to that question only. See Questions.
  • Minimize personal data. Dex does not store request content by default, but a field you do not send is a field nobody has to protect. See Data handling and residency.
  • Use one state per subject. A request answers questions about one state. To decide about ten tickets, send ten requests.