Structured Output Schema Design for Document Extraction APIs
Schema design matters more than model choice for extraction accuracy past a few dozen fields.

Schema design decides whether a document extraction API holds up in production, and past a certain field count it affects extraction accuracy more than which model runs underneath. Engineers swap in a newer model expecting an accuracy bump, and they usually get one, right up until the schema crosses a few dozen fields. Past that line, even a strong model starts dropping rows, mislabeling values, and returning JSON that won't parse. The model didn't change. The shape of the request did, and that shape is something engineers control directly, even when they treat it like an afterthought. Field granularity, nesting depth, type constraints, and description quality are engineering decisions with consequences you can measure and test, and this piece walks through each one using what benchmarks and production APIs actually show about where schemas break.
Use strict mode with a json_schema definition rather than plain JSON mode, which only guarantees parseable output and does nothing to enforce that required fields are present or that enum values fall within the allowed set. Write a detailed description for every field that carries any ambiguity, stating explicit inclusion and exclusion rules, because vague or absent descriptions are the single most common source of extraction errors in production. Keep schemas narrow: ExtractBench found that at 369 fields every frontier model tested returned zero valid output, so once a schema grows toward that scale the fix is to split the job into several narrower extraction calls rather than widen a single schema further. Constrain classification fields with enums wherever possible, since strict-mode enum enforcement applies at the moment of generation and has been associated with hallucination reductions around 30% when paired with a model's reasoning mode.
What a structured output schema does inside an extraction pipeline
Document extraction runs in two steps. Parsing takes a raw file, a scanned PDF, a photographed receipt, a multi-page contract, and turns it into clean text a language model can read. Extraction takes that text and pulls out the fields a team cares about, mapping each one into JSON, usually with a confidence score and a citation pointing back to the spot in the document the value came from.
The schema is what makes the second step possible. Think of it as an executable spec: it tells the model which fields exist, what type each one takes, and often how to judge whether a given answer counts as correct.
Most teams get the guarantees wrong in the same spot. JSON mode, the type: json_object setting available in most model APIs, guarantees the output parses as valid JSON. That's the whole guarantee, and JSON mode goes no further than parsing as valid JSON. That's the whole guarantee. It doesn't confirm the fields match what was asked for, that required fields showed up, or that an enum field holds a value from the allowed set. Plain JSON mode guarantees only that output parses as valid JSON; it does not enforce that fields match the schema. Strict mode, built around a json_schema definition, enforces the schema during generation itself, so the constraint applies at the moment of output rather than getting cleaned up afterward. Under the hood, the schema sits alongside the parsed text as a JSON object, where every field is a property carrying a type, an optional description, an optional enum, an optional format, and sometimes a list of alternate names the field might show up under in the wild.
Field naming and description quality as the first accuracy lever
Field names aren't decoration, and treating them that way is the most common mistake in schema design. invoice_number tells a model something number doesn't. patient_name narrows the target in a way name never will. Vague field names produce vague extractions, especially on documents where several candidate values compete for the same slot: three dollar figures on an invoice, two names on a medical intake form.
Descriptions do the heavier lifting, and skipping them is the single most common way teams sabotage their own accuracy. The description field is technically optional in the schema spec, and plenty of engineers leave it blank to save five minutes. That's a mistake on anything past the simplest documents. A useful description states what to pull, spells out explicit inclusion and exclusion rules ("excluding tax," "including area code"), and calls out the edge cases that actually occur in the document set. A field described as "the total monetary value of the invoice, including all charges and taxes" beats a bare field labeled total every time, because the model stops guessing at intent.
Alternate names solve a related problem worth building into any schema meant for real documents. Different vendors label the same concept differently: "Grand Total," "Amount Due," "Total Invoice Amount." Instead of maintaining a separate schema per template, a field can carry a list of alternate labels that all resolve to one canonical output field. Template variation across invoices, remittance forms, and lab reports is the default. It isn't the exception, and schemas that assume otherwise fall apart on the second vendor.
None of this is a one-time task, either. Description quality needs to keep improving as real extraction failures surface. A description that handles one batch of documents cleanly can still miss an edge case the next batch throws at it.
Type constraints and enum restrictions as guardrails against hallucination
Type choice shapes accuracy more than most engineers give it credit for. string is the default for text, and it's always technically valid, which is exactly the problem: a string field happily accepts a malformed date, a misspelled category, or a number spelled out in words. Choosing number over integer affects downstream data integrity directly. Monetary values and anything needing decimal precision belong under number; counts belong under integer. Get this wrong and the mismatch becomes visible later, in a database column that expected an int and got a float.
Boolean fields get underused across the board. True/false values keep ending up typed as strings ("yes"/"no", "Y"/"N"), which forces someone downstream to write parsing logic a real boolean constraint would have made unnecessary from the start.
Enums do the heaviest lifting against hallucination, and there's no good reason they get skipped as often as they do. An enum restricts a string field to a closed set of values ("Premium Checking," "Basic Checking," "Savings"), and under strict mode the model cannot return anything outside that set. This isn't a soft preference the model tries to respect. A context-free grammar engine masks invalid tokens during generation itself, so the constraint applies at the moment of generation rather than getting cleaned up afterward. Reported gains from this kind of enforcement are substantial: complex JSON reliability moving from roughly 82% in GPT-5.1 to over 92% in GPT-5.2, with hallucination reductions around 30% reported when strict schema enforcement pairs with a model's reasoning or "thinking" mode. The two appear to interact rather than simply stack.
Enums earn their keep most clearly on classification fields: document type, account category, status code, anywhere a free-text string would produce enough variation to break whatever routing logic sits downstream.
Nesting depth and object structure, where schemas start to break models
Flat schemas are easy to validate and easy to reason about, but they lose relationships that matter. A nested object, an address broken into street, city, state, and zip, preserves the actual structure of the document. Nesting isn't free, though: every added layer increases output length and structural complexity, and large models start to struggle once that complexity piles up.
Nesting earns its place when fields genuinely belong together, or when a field's meaning only holds up inside its parent context: a line item's unit price only means something next to its own quantity and description. Nesting for organizational tidiness alone, without that logical dependency, adds risk without buying any accuracy back. Most teams reach for nested objects out of habit, mirroring a database schema, and that habit drives the failures below.
Performance degrades as schemas grow wider, and the drop-off isn't gradual. ExtractBench, tested against 35 PDFs and 12,867 evaluatable fields with schema complexities ranging from a few dozen fields to several hundred, found performance falling off sharply as width increased. At 369 fields, every frontier model tested returned 0% valid output. Not degraded output. Zero. Long, complex outputs are brittle: they truncate, they lose formatting, they break in ways short outputs simply don't. A 16k output token limit, such as the one GPT-5.2 supports for structured outputs, covers part of the failure, but schema breadth compounds the problem on its own: a schema can fail well before it ever hits the token ceiling. Once a schema creeps toward hundreds of fields, the fix isn't a bigger model. Split the job instead: run several narrower, targeted extraction calls rather than one schema trying to do everything at once.
Array fields, the extraction failure mode that accuracy benchmarks most often hide
Arrays don't behave like scalar fields, and grading them the same way is where a lot of benchmark numbers quietly lie. Each item in an array is its own extraction task. Order matters, since lining values back up against the source document depends on it. And a dropped row, an invoice with 12 line items that comes back with 8, looks structurally identical to a correctly empty result unless the evaluator is specifically checking recall.
That silent-drop problem is a real production failure, and surface-level accuracy scores hide it well. A system can score well across most fields and still quietly throw out a third of a line-item table, and unless someone checks row counts against the source document by hand, nobody catches it until a customer does.
ExtractBench addresses this head-on: it matches array items one by one with sensitivity to ordering, and it tells apart a genuinely missing field from an explicit null value, a distinction most homegrown evaluation setups never bother making. LongArray-Extract, a benchmark built specifically around large-array completeness, shows how badly this goes even for capable systems. One system in that benchmark completed only 35 of 45 documents, with mean accuracy at 34.7%. A parser can nail every easy scalar field and still fail badly the moment a document asks it to hold onto a long, ordered list.
How benchmarks expose schema-sensitivity in production systems
Every extraction vendor claims high accuracy. That claim means almost nothing on its own, because general-purpose language model benchmarks test reasoning and code generation, not whether a system can pull a complex table off a scanned invoice under an 80-field schema with three levels of nesting.
ExtractBench exists to close that specific gap. It runs 39 systems across 35 PDFs, 12,867 fields, 8 business domains, and 67 document types, using a headline metric called Unified Value F1, an unweighted mean across documents, so a handful of long documents don't dominate the score the way they would in a naive average. A second metric, Grounding F1, only credits a field as correct when the value is right and the evidence, a bounding box or cited page, actually lines up with the source at an IoU threshold of 0.5. That's a far stricter bar than value accuracy alone, and it sits closer to what production actually demands, since a value with no traceable source is a liability in any regulated or auditable workflow. What the benchmark really shows is how fast accuracy falls off as the schema grows, not how any one model performs in isolation.
Other benchmarks fill in adjacent gaps. A comparison approach used across the industry, testing many models against thousands of real documents through open benchmarks, shows rankings that shift depending on which benchmark gets used: a model ranked seventh overall can outscore the top-ranked model on one specific test. That inconsistency is the finding itself, and it means a single accuracy number from a vendor isn't enough to base a procurement decision on. ParseBench, built from roughly 2,000 human-verified pages pulled from real enterprise documents, evaluates something adjacent but different: whether parsed output keeps enough structure and meaning for an autonomous agent to make a correct decision downstream, tested across five separate capability dimensions, each targeting a distinct way production agent workflows tend to break.
Choosing an extraction API that respects schema design investments
Does the API treat the schema as an executable spec, with per-field scoring and validation? Or does it treat the schema as a loose hint that shapes formatting and nothing more? Whether the schema work described above pays off at all comes down to that question.
A few criteria follow directly from it. The API should return schema-mapped JSON with per-field confidence scores and source citations, not raw block-level primitives that force an engineering team to build its own mapping layer from scratch. It needs demonstrated reliability on large arrays and multi-file batches, since schema design for array fields only pays off if the system underneath can finish large-array extractions without silently truncating them. Multiple extraction modes, something lighter and faster for simple documents, something heavier for multi-step reasoning over dense filings, let a team tune cost against latency instead of paying premium prices on every request regardless of complexity. Built-in evaluation tooling and schema versioning catch schema-induced errors before they reach a customer, not after. And for sensitive document types, healthcare remittance advice, financial filings, anything under compliance scrutiny, the platform needs to cover SOC 2, HIPAA, and GDPR, with zero data retention and flexible deployment options. Schema design for regulated documents is only as safe as the infrastructure running underneath it.
Some platforms return raw block primitives and leave schema mapping, evaluation, and review entirely to the team building on top. That's a defensible choice for a team that already has those capabilities in-house. For everyone else, it's engineering cost hiding behind what looked, at first glance, like a simpler integration.
What does a production-ready schema design checklist for document extraction actually include?
Field naming deserves the first pass, and it's the cheapest fix on this whole list. Use specific, descriptive names: invoice_number rather than number, patient_name rather than name. Where a field's label shifts across document templates, building in alternate names helps unify extraction into one canonical output field.
Descriptions come next, and they need writing for every field carrying any ambiguity. Optional by the schema spec, essential in practice, full stop. Each one should spell out what to extract, state explicit include and exclude rules, and get revised as real extraction failures reveal edge cases nobody caught the first time around. Treat the description field less like a comment and more like living documentation, the kind a team patches the moment a bug appears in production, not the kind that gets written once and forgotten.


