DrexDocs

State

Put any JSON in the state field, choose between text and structured data, and stay under the 1 MiB body limit and the model's token limits.

state is the data every question in a request is about. This guide shows what Drex accepts in state, how text and JSON compare in cost, and what to do when a request is too long. For the questions themselves, see Questions.

Send any JSON value

state is required. It can be any JSON value: a string, an object, an array, a number, a boolean, or null. Drex serializes it and hands it to the model together with your questions.

A string
{
	"state": "I was charged twice for my March invoice. Please refund one of the charges.",
	"questions": { "wants_refund": { "type": "noul", "instructions": "Is the customer asking for a refund?" } }
}
An object
{
	"state": {
		"ticket_id": "T-1042",
		"plan": "enterprise",
		"messages": [
			{ "from": "customer", "text": "Our SSO login has been broken since this morning and 40 people are locked out." }
		]
	},
	"questions": {
		"is_outage": { "type": "noul", "instructions": "Does this describe a service outage?" },
		"priority": {
			"type": "choice",
			"instructions": "Which priority should this ticket get?",
			"criteria": { "p0": "Production down for many users", "p1": "Major feature broken", "p2": "Minor issue", "p3": "Question or request" }
		}
	}
}

A request without state is rejected with HTTP 422 and the issue { "path": "state", "message": "is required" }. "state": null passes validation and reaches the model. In one run it answered "Is there any customer message?" with a noul of 0.4727, so treat a null state as a bug in your code rather than a way to ask context-free questions.

An array works when the state is a list, for example the messages of a thread. ["Refund please", "Where is my order?"] with the question "Does any message ask for a refund?" returned a noul of 0.9827 at 28 input tokens.

Choose text or structured JSON

Send the data in the shape you already have. We sent the same lead as a sentence and as an object and asked the same two questions. Output is an example and varies slightly per call.

stateinput_tokensworth_a_call (noul)segment (choice)
"Company: Northwind Traders. Employees: 250. Budget: $40,000 per year. Timeline: wants to go live next quarter. Current tool: spreadsheets."1090.8226mid_market at 0.9669
{ "company": "Northwind Traders", "employees": 250, "budget_usd_per_year": 40000, "timeline": "wants to go live next quarter", "current_tool": "spreadsheets" }1090.6883mid_market at 0.9686

The token count was the same. Rebuilding text as JSON did not save anything here, and rebuilding JSON as text would not either. The choice answer was the same in both runs. The noul answer moved by 0.13, so the format is part of the question. If you compare a probability with a fixed threshold, keep the format of state fixed too.

Prefer an object when your data is already structured. You can then drop a field without editing prose, and dropping fields is the first step when a request is too long. Prefer a string when the data is a message, a document, or a transcript.

Send text, not media

The model reads text only. Drex rejects images, audio, video and PDFs anywhere in state or questions with a 422 whose issue points at the field, for example { "path": "state.photo", "message": "is media (image/png data URL); drex-v1.5 reads text only" }. It looks for base64 data URLs, raw base64 files, and image, audio and file content parts in the OpenAI, Anthropic and Gemini formats. Describe the media in text instead: a caption, an OCR result or a transcript. A URL that points at an image is fine; it is text, though the model can't open it.

Stay under three limits

Drex checks a request against a byte limit and two token limits before it reaches the model. All three come back as HTTP 422 with error.type of invalid_request_error, and none is retryable.

1 MiB body. The serialized { "state": ..., "questions": ... } must be at most 1,048,576 bytes. Drex rejects a larger body with:

{ "path": "", "message": "request body must be at most 1048576 bytes" }

The state limit. The state alone must fit the model's state limit: 131,072 tokens on drex-v1.5, 32,768 on drex-v1.0. Drex counts it with the model's tokenizer, so a body under 1 MiB can still fail here. The response is:

422 response
{
	"error": {
		"type": "invalid_request_error",
		"message": "Request too long: the state exceeds the 131,072-token state limit of drex-v1.5. Shorten the state.",
		"issues": [{ "path": "state", "message": "exceeds the 131,072-token state limit" }]
	},
	"request_id": "req_<32 hex characters>"
}

The row limit. The model reads the state once for each question. Each read, the state plus one question with its instructions and options, must fit the model's row limit: 139,264 tokens on drex-v1.5, 32,768 on drex-v1.0. So the limit applies to the state plus your longest question. The response is:

422 response
{
	"error": {
		"type": "invalid_request_error",
		"message": "Request too long: the state plus its longest question exceed the 139,264-token limit of drex-v1.5. Shorten the state or that question.",
		"issues": [{ "path": "state", "message": "exceeds the 139,264-token limit" }]
	},
	"request_id": "req_<32 hex characters>"
}

Adding questions doesn't use up the row limit; only the longest one counts. English text runs about 4 characters per token, so a state of roughly 500,000 characters leaves room for short questions on drex-v1.5. usage.input_tokens counts the state once plus every question, so it can pass the row limit while each read still fits. The full table of limits is on the limits reference.

Trim a request that is too long

The 422 message names both levers. Start with the state.

  1. Send only the fields the questions need. A support ticket's messages matter for "Is the customer asking for a refund?", but its created_at, assignee, and tags do not. Every field you send is billed as input tokens and counts toward the limit.
  2. Ask fewer questions per call. Questions share the limit with the state. In our batching measurement, going from 1 question to 4 added 73 input tokens, about 24 per question, so questions are the smaller lever unless you send hundreds of them. See Batch questions into one call.

A request that fits is billed by usage.input_tokens. Prices are on the pricing reference.

On this page