Skip to content

Structured outputs

A request has an input (context, messages or an image) and a schema: an object whose keys are the field names you want back and whose values describe each field.

"schema": {
  "merchant": {"type": "string", "extract": true, "instruction": "Store name"},
  "total":    {"type": "number", "integer": true, "instruction": "Final total"},
  "paid":     {"type": "bool", "instruction": "Was it paid?"},
  "category": {"enum": ["food", "transport", "other"]},
  "items":    {"type": "list", "items": {"type": "string"}, "max_items": 10}
}

Every field is filled in parallel from the same input, and result contains one value per field, in schema order.

Field types

Type Spec Value in result
string {"type": "string"} or just "string" text
number {"type": "number"}; "integer": true for whole numbers number
bool {"type": "bool"} true / false
enum {"enum": ["a", "b", "c"]} one of the listed values
list {"type": "list", "items": {...}, "max_items": 20} array (may be empty)
compute {"type": "compute", "expr": "a / b * 100"} number calculated by code; see Computed fields
call {"type": "call", "http": {...}} data fetched from an HTTP API; see Tool calls

Aliases: boolean, integer / int (number with integer: true), float, str, array (list). JSON-Schema style nullable types are accepted: {"type": ["number", "null"]}.

number, bool and enum are constrained while they are generated: the model can only produce a valid value. string is free text.

Options

Option Applies to Meaning
instruction (alias description) all What the field means. This is the most important option for accuracy.
nullable: true all The field may be null when the input does not contain it. For enums, add null to the list instead: {"enum": ["a", "b", null]}.
extract: true string The value must be copied verbatim from the input: the model cannot invent or re-spell it. Use it for names, IDs, reference numbers.
free_form: true string Long text (reasoning, summaries, replies). Without it, the model is told to answer with only the value.
max_digits number Maximum characters of the number, including - and . (default 8). Money fields often need 10–12.
integer: true number No decimal point; the value is an integer.
max_items list Maximum number of items (default 20).
items list Spec of each item: string, number, bool or enum (not lists, not objects).
depends_on: [...] all Run after other fields and see their values. See Multi-step schemas.
on: [...] all Rules checked in code after the field is filled. See Flow rules.
on_reject all Value to use when a rule rejects the request before this field is filled.
votes typed fields Fill the field 1–5 times with different wordings and take the majority. See Confidence and review.

Shared instructions: system

Put rules that apply to every field in system, not in each instruction:

{
  "system": "You extract data from Indonesian receipts. Copy values exactly; write numbers without thousands separators. Never guess.",
  "context": "...",
  "schema": {...}
}

system is read once for all fields, so it costs nothing extra per field.

Writing good instructions

  • Say what the value is, not how to find it. "Final total paid by the customer, in rupiah" beats "look for the total".
  • Name the traps. If the document has a subtotal and a total, say which one you want. If tax can be called PPN, PB1 or Tax, list them.
  • Define null. "null if the document does not show a tax amount" is clearer than relying on nullable alone.
  • Give examples of odd formats. "(56rb = 56000)" fixed a real error.
  • Keep one fact per field. Two small fields are more accurate than one field that asks for two things.

Limits

  • An enum can have at most 24 values.
  • Numbers are digits with an optional sign and decimal point; no thousands separators in the output.
  • Lists hold simple values only (not objects) for now.
  • Formatting requests in free text (bullets, date formats) are followed on a best-effort basis; use typed fields when the format matters.