Your JSON Schema is the contract. It defines the shape of the output, it tells Veralens what each field means, and through format it decides which values come back computed, decoded, validated or normalized for you.
This page is the full list of what the schema controls.
Which keywords do what
Numbers
Two things happen to a numeric field that you do not have to write yourself.
Derived numbers are exact. When a value is a calculation (a tax base from a total and a rate, a line amount from quantity and unit price), you get the arithmetic done properly: correct precedence, rounded once at the end, the same answer on every run.
Constraints hold on what you get back. 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 you stop receiving whatever the page happened to print. You receive the canonical value.
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 come back decoded
A qrcode or barcode field carries the real payload of the symbol on the page, character for character. 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:
One job classifies and extracts, so you can drop the separate classify-then-route step.
Composition keywords: anyOf and oneOf are supported, $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.