Drex guides

State

Put any JSON in the state field, choose between text and structured data, and stay under the 4 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.

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 state and questions fields carry text/JSON. Drex Max additionally supports a separate top-level images array when the model is available to your account. Drex rejects embedded 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-v2.0 reads text only" }. It rejects a string holding a base64 data: URL of an image, audio, video or PDF; a string that is entirely base64, at least 512 characters long, and starts like a JPEG, PNG, GIF, WebP, WAV, PDF, MP3 or MP4 file; and media content parts: OpenAI image_url, input_image, input_audio, input_file and file, Anthropic image and document, and Gemini inline_data and file_data. Shorter base64, or base64 of another format, passes as text and is billed as tokens. 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 request limits

Drex checks a request against a byte limit and the token limits of the model that answers it. A retired model id follows its successor's limits. A request that exceeds a model token limit comes back as HTTP 422 with error.type of invalid_request_error, and is not retryable without changing the request. See the generated limits reference for the current limits of each served model.

4 MiB body. The serialized { "state": ..., "questions": ... } must be at most 4,194,304 bytes. On Drex Max a nonempty images array is counted with them, so inline base64 images take from the same budget. When the request reaches Drex, it rejects a larger body with HTTP 422 and this issue, which names the byte limit rather than a model:

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

The hosting layer can reject an oversized body before it reaches Drex, returning HTTP 413 without Drex's error format. Shorten the request before retrying either response.

State and row limits. Drex 2.0 checks the state alone, then the state plus its longest question, including that question's instructions and options. It counts tokens with the model's tokenizer, so a body under the byte limit can still exceed a token limit. Adding questions does not increase the longest row unless one of them is longer, but every question adds billed input tokens.

Whole-request limits. The served Drex Max models check the whole packed request: the state, images, every question, and the model's formatting and decision markers. All questions share that budget, so adding questions can exceed it even when each state-and-question pair would fit. Retired ids that resolve to Max use this accounting too.

The Errors reference shows how Drex reports state, row and whole-request validation errors.

Trim a request that is too long

The 422 message names the limit and suggests what to shorten. 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