Extraction schema dialect
The subset of JSON Schema that POST /v1/extract accepts, and what each rejection means.
The schema you send to extract is JSON Schema, with five constraints. They’re checked at the gateway before the run starts, so a bad schema comes back in milliseconds as a 400 invalid_schema with the offending path and a link to the rule it broke:
The message path (schema.properties.a.oneOf) points at the exact node to fix.
Root object
The root schema must be {"type": "object"}.
Extraction returns one record per document, and a record is a named set of fields. A root array or a bare scalar has no field names to cite against, so grounding would have nothing to attach to. For repeating data, put the array on a property, or use "unit": "rows_per_document" to get a row array back from an object schema.
Max depth
Nesting may not exceed 5 levels. The root counts as level 1; every step into properties, items, prefixItems, patternProperties, definitions, or additionalProperties counts as one more.
Deeply nested shapes cost accuracy: the model has to hold the whole path in mind to place a value, and a wrong turn high up invalidates everything under it. Flat, named fields extract better and produce citations you can actually check.
If you genuinely need the deep shape, extract it flat and reassemble it in your own code.
Unsupported keywords
These keywords are rejected: $ref, $defs, oneOf, anyOf, allOf, const, and pattern — anywhere the validator walks, which is every property, array item, and nested object of your schema. Treat them as unsupported everywhere: a keyword that slips past validation is not honoured by the extractor either. Write every field out in full — with $ref rejected, a definitions block has nothing that can point at it.
The first five make the output shape conditional — the schema no longer says what the result will look like, so neither the extractor nor your code can rely on it. const and pattern are assertions about a value that was read off a page; failing them tells you nothing useful about the document and would turn a legible field into an error. Constrain values with enum and describe formats in description or instructions instead.
A property named pattern is fine — only keyword positions are checked, and anything under properties is a field name, not a keyword.
Nullable primitives
Recommended, not enforced: declare every field that might be absent as nullable — {"type": ["string", "null"]} rather than {"type": "string"}.
A non-nullable field tells the extractor the value exists. When the document doesn’t contain it, that pressure produces a plausible-looking guess instead of an admission. A nullable field gives it somewhere honest to land, and null in your result is unambiguous: the field wasn’t in the document.
This rule never rejects a request. It’s the single highest-impact change you can make to extraction quality.
Size limit
The serialized schema must be 64 KB or smaller.
The schema travels with the run through internal dispatch, which has a hard message ceiling; the limit is checked up front so you get a clear 400 instead of a failure part-way through a run. In practice 64 KB is hundreds of fields — hitting it usually means one schema is doing several jobs.
Split the work instead: run several narrower extractions over the same file (reuse the file id so it’s parsed once), or drop fields you don’t consume. Long prose belongs in instructions, not in per-field description repeated across hundreds of properties.