Skip to main content
A selection request is two halves: the records you could send, and the task they have to serve. Everything else is a budget.

Records

text is the only required field. Everything else is a signal, and each one earns its place:
Do not move content into attributes to avoid the meter.These three fields are bounded by the published size cap. Values beyond it are outside the contract and may be rejected — a payload built around stuffing them is not an allowance the contract grants.It also does not do what you would want in the meantime. These fields are read — the engine folds them into the text it infers entities, keywords and tags from — but they are indexing hints, not content: nothing parked there can be selected, excerpted or returned. Keep them short and structured, and put the content in text.

Record kinds

A kind outside this list is currently rejected with a 500 rather than a 400 (known rough edge).
tool_schema today behaves like any other record. A protected-kind behaviour exists for it — never dropped, never lossily excerpted — but it is not enabled on the operating points serving today. Label your schemas correctly anyway: protection is enabled per operating point, and mislabelled records will not benefit when it is.

Records are sent every time

There is no upload step and no index. The records ride in on each request and are gone when it returns. That is what makes the service stateless. It is also what the cost model will follow: metering is not live yet, and when it arrives it counts what you send, every time you send it. So if the same 40 documents accompany every call in a loop, that is 40 documents metered on every call. Worth designing around now — pre-filter with your retriever, or keep a working set. It is not worth working around by splitting one call into several, which costs more rather than less. The same records also count against your per-request ceiling today, which is live.

Tasks

Nothing in the request pins a record today

required_record_ids is the field designed to do it — a constraint the engine satisfies, as against importance, which is only a prior it weighs.
It does not pin today. Ids you send are matched against the engine’s own internal record identity rather than against your records[].id, so they currently match nothing. importance was never the pin either: turning it up to 1.0 is a strong hint and not a guarantee.So if a record must reach the model, do not send it to be selected. Keep it out of records[] and concatenate it around rendered_context yourself:
That is the right pattern for a system prompt, a schema the model must call against, and the current user turn — and it stays right after the field is wired, because those things do not benefit from being selected anyway.
Keep sending required_record_ids for the records where a pin is a preference rather than a requirement. It costs nothing and starts working without a change on your side.

recent_prompts is not wired yet — send few, or none

The field exists so the engine can judge whether the conversation has drifted, which feeds the decision to engage or stand down.
It is not used for that today. The deployed service accepts the field and does not pass it into selection — but it does count it against your per-request ceiling, and the meter will count it too. Right now a long history costs you allowance and buys nothing.Send a handful if you want to be ready for when it is wired. Do not send the whole transcript.

Three mistakes that cost you quality

It is the default, so it is what you get if you never think about kind. But a tool result, a schema and a retrieved page are treated differently, and flattening them into one role discards a signal that costs you nothing to supply.
Without id, selected[].record_id is a server-generated value you cannot join back to anything, so you can render rendered_context but you cannot reason about which of your objects survived. Supply ids from the first call.
Selection works at record granularity. One 200 KB record is one keep-or-drop decision, so there is nothing to select between — you get pass-through or nothing. Split along the boundaries that already exist in your system: one tool result, one document, one turn.

What comes back

selected[] carries, per record: record_id, kind, title, source, score, reason, excerpt, excerpt_format and selected_tokens.
  • excerpt is built from your record’s own content. Nothing is paraphrased or summarised into new prose, and no model writes replacement text. It is not guaranteed to be a byte-identical span of your input, and the difference is worth knowing before you build on it: If you need byte-identical spans — because you are resolving citations back to offsets, or re-parsing an excerpt as JSON — use selected[].record_id to look the record up in your own store and use your copy. That is what the ids are for.
  • score is comparable within one response, and is not calibrated across requests or across operating points. Rank with it; do not threshold on an absolute value.
  • reason is open-ended. Show it in a debugging view, do not branch on it.
Set return_per_record: false if you only want rendered_context and the token arithmetic — the response body gets materially smaller.