Your JSON Schema is the contract. It defines the shape of the output, it steers what the model looks for, and through format it decides which values Veralens computes, decodes, validates or normalizes for you after extraction.
This page is the full list of what the schema controls.
Which keywords do what
Not every keyword reaches the model, and that is deliberate. A model is not a validator: given multipleOf, it will confidently return a value that violates it. So constraints are applied by the runtime, after extraction, where they are enforceable.
Numbers
Two things happen to a numeric field that you do not have to write yourself.
Calculations run in code, not in the model. When a value has to be derived (a tax base from a total and a rate, a line amount from quantity and unit price), the model identifies the quantities and how they combine, and the runtime performs the arithmetic. Deterministic, correct precedence, rounded once at the end.
Constraints are applied to the result. Declare the decimal step you want and you get it:
You get 110.6, never 110.60000000000001.
The format table
Add format to a field and the runtime resolves the value after extraction. The model always reads what is printed; the conversion happens afterwards.
The rule that governs all of them
Resolve or leave alone. Never fabricate.
A value that does not resolve comes back exactly as printed, unchanged. An IBAN that fails its checksum is returned as raw text and is not silently corrected. A currency word with no ISO mapping stays the word. A date whose parts are genuinely ambiguous is not forced into a confident wrong ISO string.
This is deliberate: a value that came through raw is visible to you and can be routed for review, while a quietly guessed one is undetectable.
Codes are decoded, not read
qrcode and barcode fields are not the model reading the symbol off the pixels. Every image and page is passed through a barcode decoder before extraction, and the decoded payload is what fills the field. QR, Micro QR and the common linear families (EAN, UPC, Code 128, Code 39, ITF) are supported.
**If the page carries no readable code, the field is null.** It is never filled with something that looks like a payload.
One schema for mixed documents
You do not have to know which document you are sending. Describe every shape you accept as branches of an anyOf, give each branch a discriminating enum field, and each entry comes back as the shape it actually is:
Classification and extraction happen in the same pass, so this replaces a separate classify-then-route step.
Composition keywords: anyOf and oneOf are passed through, $ref and $defs (or definitions) are resolved, and allOf branches are merged into one shape. discriminator is ignored, so use an enum field as your discriminator, which also narrows a TypeScript union directly.
Absent fields
A field that is genuinely not on the document comes back null. Extraction is built to abstain rather than to produce a plausible value, because a wrong value you cannot detect costs more than a gap you can.
Two consequences for your schema:
- **Keep
requiredshort.** Everything you mark required is pressure to produce a value. Reserve it for fields that are genuinely always present. - **Handle
nullin your application** rather than forcing the extraction to guess. Route it to review.
Markdown and plaintext targets
With target: "markdown" or "plaintext" and no schema, you get the whole document as one string. Elements that are not text still get represented rather than dropped:
Markdown output uses HTML <table> for tabular data, LaTeX for mathematics, and Mermaid for diagrams that can be recovered.
Multiple sources in one job
sources takes an array, and every source is read as part of the same document set. Send the front and back of an ID card, or a set of receipts, and the schema is filled from all of them together.
Next
- API reference for the request and response shapes.
- Playground to iterate on a schema before writing code.
- Jobs and queue for webhooks, statuses and retries.