Structured data extraction: schema design and validation architecture
"Make it return JSON" describes an output format. A reliable pipeline needs more. A schema is a contract, and how tightly it's written decides how much room a technically valid but wrong response has to slip through. The work that matters is the schema and what happens downstream when a response fails it.
1. What structured extraction is
Extraction turns unstructured or semi-structured input (a document, an email, a scanned form) into a fixed, machine-consumable shape: named fields with defined types, ready for a database or the next pipeline step without a person retyping the model's prose. Claude tool use and function calling covers how to get structured output, usually through a tool call whose input schema defines the shape. Getting one correctly formatted response is a solved mechanical problem. This guide is about the other one: a pipeline that behaves predictably across thousands of messy inputs, where the target field is missing, ambiguous, or written three different ways in three documents. That's a design problem.
2. Schema design as the interface
A loose schema (a notes string where a tighter schema would name the fields) pushes interpretation onto the consumer, and that shows up later as a parsing bug downstream instead of a validation failure where it should have been caught. Make every field that can be an enum an enum. A status field with three known values, encoded as an enum, cannot come back as a fourth string a downstream switch statement doesn't handle. As free text it can, and eventually will, once a document arrives phrased in a way nobody tested.
Required versus optional is a design decision. A required field the model can't confidently fill forces a choice between guessing (bad) and failing the extraction (usually more recoverable). An optional field that's often absent in real inputs, modeled as optional, lets the model return nothing instead of inventing a value to satisfy a schema that claimed it would always exist.
3. Forcing the extraction contract
A schema attached to a tool definition, with the model forced to call that tool, is a stronger contract than asking for JSON in a prompt and hoping it parses. A forced tool call constrains what a valid response can look like at generation time. Prompted JSON produces free text that your code must parse, with no structural guarantee it's valid.
Prompted JSON still has a place: a lightweight, low-stakes extraction where an occasional malformed response is cheap to detect and retry. For anything feeding a pipeline where a malformed response is expensive to catch later, start with a forced tool call and a well-designed schema. Don't wait until the lighter approach causes problems.
4. Handling validation failures
A schema constrains a response's shape. It doesn't check whether the values are correct. A response can have every required field and every type right and still contain a wrong value: a total that doesn't match the sum of the line items on the same document, a date that's valid but implausible for that document. Catching this second class of failure takes a separate, deliberate check. Skipping it because the schema validated is a common and costly gap.
The retry strategy that works best feeds the specific validation failure back to the model. Whether the failure is the schema or a semantic check like a total that doesn't reconcile, include the error in a follow-up turn, so the model corrects a named problem instead of guessing what went wrong. Cap the retries. An extraction that keeps failing on the same input more likely reveals an ambiguous or malformed source document than a bad model turn. After a small fixed number of attempts, route it to a dead-letter queue for human review.
5. Nested and repeated structures fail differently
A flat schema of a few top-level scalar fields is close to the easiest case and the least representative of real documents. An invoice's line items, a resume's jobs, a form's repeated sections all need an array of nested objects, which fails in ways a flat schema doesn't. One malformed item deep in an otherwise correct array can invalidate the whole response if validation can't tell "this item is wrong" from "the extraction is wrong." A document with a variable number of repeated sections also tests the model's consistency across items, since each one must independently follow the same shape.
Validate, and where sensible salvage, at the item level. An invoice with nine correctly extracted line items and one malformed one, flagged for review, is usually more useful than discarding all ten because the response failed strict validation. That requires a validation layer deliberately built to separate item-level from document-level failures; a generic schema check won't do it.
6. Where extraction sits in a larger pipeline
An extraction call is rarely the last step. It composes with a queue, a retry mechanism, and a human review path for the cases in sections 4 and 5. A pipeline that treats every extraction as clean success or hard failure, with no middle path, either over-escalates routine, fixable ambiguities to a reviewer or under-escalates wrong extractions that downstream systems then act on. Build a confidence signal into the pipeline instead of trusting the model's stated confidence: cross-check extracted values against constraints the document implies (line items against a stated grand total, a date against a plausible range for the document type), and send only extractions that fail those checks to review. Don't sample review targets at random or review everything.
7. Worked example: an invoice schema that failed silently
An invoice-extraction pipeline typed its total field as a string, because totals appeared in inconsistent formats across vendors (some with currency symbols, some with different decimal conventions). Every response validated, because any string satisfies a string field, and for months the pipeline reported a clean extraction rate with zero validation failures. The problem surfaced in accounting: a batch of totals from one vendor had the wrong decimal placement because the model had been misreading that vendor's format. Nothing in the pipeline flagged it, because a string field has no idea what a plausible invoice total looks like.
The fix split the field into a numeric total_amount and a separate currency. The numeric field gets a semantic check: is it within a plausible range for this vendor's typical invoice, and does it reconcile with the sum of the extracted line items? The type system was never positioned to catch this class of error. The general lesson: a schema that validates cleanly shows the response has the right shape, and says nothing about whether the values are correct. Conflating the two let this failure run silently for months.
See guardrails architecture for validating model output outside the model, and claude tool use and function calling for the mechanism a forced-tool-call extraction schema is built on.