Field types
Each entry in a schema’s fields list describes one value to extract. Every field needs a
name and a type. The other keys are optional, apart from a few that some types require, and
they narrow down what counts as a valid value:
{ "name": "rent", "type": "money", "currency": "EUR", "scale": 2, "description": "Monthly rent excluding service costs" }type |
Example value in the response | Required keys | Optional type keys |
|---|---|---|---|
string |
"INV-004211" |
prefix, pattern |
|
bool |
true |
||
int |
12 |
range |
|
decimal |
"1250.50" |
scale |
range, evidence_role |
money |
{ "amount": "1250.00", "currency": "EUR" } |
scale |
currency, range, evidence_role |
date |
"2027-03-01" |
range |
|
datetime |
"2027-03-01T09:30:00+01:00" |
range |
|
duration |
"P12M" |
basis, range |
|
enum |
"terminated" |
values |
Decimals and money amounts come back as strings, because a JSON number would be read as floating point by most parsers and could lose precision. Integers are 64-bit.
Key reference
Section titled “Key reference”| Key | Applies to | Type | Default |
|---|---|---|---|
name |
all | string | required |
type |
all | string | required |
description |
all | string | none |
nullable |
all | boolean | false |
array |
all | boolean | false |
applicable_when |
all | expression | always applicable |
locales |
all | string[] | defaults.locales |
scale |
decimal, money | integer 0 to 28 | required |
currency |
money | ISO 4217 code | defaults.currency, else "EUR" |
evidence_role |
decimal, money | string | none |
basis |
duration | string | "calendar" |
values |
enum | string[] | required |
range |
int, decimal, money, date, datetime, duration | object | none |
prefix |
string | string | none |
pattern |
string | regex | none |
Any other key is rejected with E-SCHEMA-UNKNOWN-KEY.
Starts with an ASCII letter or _, followed by letters, digits or _. Case-sensitive and unique
within the schema. Reserved: and, or, not, when, round, is_null, is_present, true,
false, null, date, dur.
{ "name": "startDate", "type": "date" }One of the types above.
description
Section titled “description”A hint for the model about which value you mean. It matters most when a document contains several values of the same type, such as an invoice with a subtotal, a VAT amount and a total.
{ "name": "total", "type": "money", "scale": 2, "description": "Amount payable including VAT" }nullable
Section titled “nullable”Set this when a document may say outright that there is no value (“N/A”, “none”). Without it,
Tickmarks can’t tell “the lease says there’s no deposit” from “the lease doesn’t mention a deposit”:
both give status no_evidence. With it, the first case gets explicit_null.
{ "name": "deposit", "type": "money", "scale": 2, "nullable": true }A document reading “Deposit: none” gives deposit status explicit_null.
Extract every occurrence as a list instead of one value, such as all the tenants named on a lease.
Array fields can’t appear in relations or in applicable_when (E-ARRAY-RELATION).
{ "name": "tenants", "type": "string", "array": true }Response: "tenants": ["Alice Johnson", "Bram de Vries"] in data, and per-element evidence in
fields.
applicable_when
Section titled “applicable_when”The field only applies when the condition holds. When it’s false, the field gets status
not_applicable and value null, so a lease that isn’t terminated doesn’t report a missing
termination date. The condition is an expression
that may not mention the field itself, use when, or form a cycle with other conditions.
{ "name": "status", "type": "enum", "values": ["active", "terminated"] },{ "name": "terminationDate", "type": "date", "applicable_when": "status == 'terminated'" }locales
Section titled “locales”Overrides defaults.locales for this field. Use it when one field is written differently from the
rest, such as an American-style date in a Dutch document.
{ "name": "signedOn", "type": "date", "locales": ["en-US"] }Required for decimal and money. The maximum number of digits after the decimal point, 0 to 28.
Trailing zeros don’t count: with scale 1, 1.50 fits and 1.55 doesn’t. Tickmarks drops a value
that doesn’t fit rather than rounding it, because silently rounding a figure from a document would
change it. If you want rounding, write it in a relation with round.
{ "name": "vatRate", "type": "decimal", "scale": 2 }currency
Section titled “currency”The field’s currency. An amount with no currency marker in the text is read in this currency, and
an amount with a different marker ($ on a euro field) is dropped. Tickmarks never converts between
currencies.
{ "name": "fee", "type": "money", "currency": "USD", "scale": 2 }evidence_role
Section titled “evidence_role”Which printed amount on the document the field is. Tickmarks uses it to avoid mistakes such as
deriving a total from an amount that is only a subtotal. One of: subtotal, taxable_subtotal,
non_taxable_subtotal,
tax_amount, total, surcharge, discount_amount, withholding_amount.
{ "name": "vat", "type": "money", "scale": 2, "evidence_role": "tax_amount" }How a duration is measured. A month is 28 to 31 days, so “1 month” and “30 days” are different kinds of length, and durations of different bases can’t be combined or compared.
basis |
Holds | Typical use |
|---|---|---|
calendar |
months and days | Contract terms: “1 year and 2 weeks” |
months |
whole months | Terms always given in months |
days |
whole days | Notice periods: “30 days” |
exact |
an exact length of time | Time spans between datetime fields: “2 hours” |
{ "name": "noticePeriod", "type": "duration", "basis": "days" }See calendar and money rules for how each basis measures the distance between two dates.
values
Section titled “values”Required for enum. The allowed labels, each a distinct, non-empty string. Relations compare an
enum with a label: status == 'terminated'.
{ "name": "status", "type": "enum", "values": ["active", "terminated", "draft"] }Inclusive bounds: give min, max or both. Integer bounds are JSON numbers, and every other type
uses a string in the field’s format. Candidates outside the range are dropped, and if none remain
the field’s status is normalization_failed.
{ "name": "units", "type": "int", "range": { "min": 1, "max": 10 } }{ "name": "rent", "type": "money", "scale": 2, "range": { "min": "0" } }{ "name": "signedOn", "type": "date", "range": { "min": "2000-01-01" } }{ "name": "noticePeriod", "type": "duration", "basis": "days", "range": { "max": "P90D" } }A duration bound must fit the field’s basis: "P1M" is not a valid bound for a days field.
min greater than max is E-RANGE.
prefix and pattern
Section titled “prefix and pattern”Restrict a string to a known format, such as an invoice number. prefix is a literal,
case-sensitive start. pattern is a regular expression that must match the whole value, so ^
and $ are optional. If you set both, a candidate must pass both, and each one rejected is
reported as a W-PATTERN warning.
{ "name": "invoiceNumber", "type": "string", "prefix": "INV-", "pattern": "INV-[0-9]{6}" }| Candidate | Result |
|---|---|
INV-004211 |
accepted |
inv-004211 |
rejected: prefix is case-sensitive |
INV-4211 |
rejected: pattern needs six digits |
Before matching, strings are normalized to Unicode NFC, trimmed, and runs of whitespace are
collapsed to one space. Look-around and backreferences aren’t supported. Write [0-9] rather than
\d, since \d also matches digits from other scripts.
A pattern only filters: it rejects candidates in the wrong format, but if the document contains
two valid invoice numbers, it can’t tell you which one you meant. Use description for that.