Validation Operators
Reference for every operator in the Docutray validation DSL — presence, comparison, numeric precision, text, lists, dates and calculated values — with a runnable example for each.
This page lists every operator the validation DSL accepts, with a runnable
example for each. For the syntax around them (when, forEach, define, field
references) and how to read the results, see the
validation guide.
Every example below runs in Docutray's test suite against the same validator the
platform uses, so the Result block is exactly what the API returns, minus
value, severity, source and details. Issue messages are in Spanish.
Missing values are skipped
Except for required, exists and notExists, an operator does not run when the
field is missing or null: the rule passes. Add required when a value must be
present.
Presence
required
The field must exist and must not be null or an empty string "". Fails with
REQUIRED_FIELD.
field.invoice_number required
field.customer.name required{ "invoice_number": "", "customer": { "name": "Acme SpA" } }{
"valid": false,
"errors": [
{ "field": "invoice_number", "code": "REQUIRED_FIELD", "message": "El campo 'invoice_number' es obligatorio" }
]
}exists
The field must exist and must not be null. Unlike required, an empty string
passes. Fails with REQUIRED_FIELD.
field.notes exists
field.reference exists{ "notes": "" }{
"valid": false,
"errors": [
{ "field": "reference", "code": "REQUIRED_FIELD", "message": "El campo 'reference' debe existir" }
]
}notExists
The field must be missing or null. Useful inside a when block. Fails with
VALIDATION_ERROR.
field.cancellation_date notExists{ "cancellation_date": "2024-05-01" }{
"valid": false,
"errors": [
{ "field": "cancellation_date", "code": "VALIDATION_ERROR", "message": "El campo 'cancellation_date' no debe existir" }
]
}optional
Always passes. It documents that a field may be absent; it has no other effect.
field.purchase_order optional{}{ "valid": true, "errors": [] }Comparison
Comparison operators accept a literal or a reference to another field
(field.… or calculated.…). Numbers are compared as numbers even when one side
is a string, such as "1.234,56" against 1234.56; see
numbers.
equals
The values must be equal. Numbers, and strings that read as numbers, are compared
numerically; other strings are compared exactly, including case. Symbol forms:
= and ==.
field.currency equals "CLP"
field.total equals field.amount_due{ "currency": "clp", "total": "1.190,50", "amount_due": 1190.5 }{
"valid": false,
"errors": [
{ "field": "currency", "code": "VALIDATION_ERROR", "message": "El campo 'currency' debe ser igual a 'CLP', pero es 'clp'" }
]
}notEquals
The values must differ, with the same comparison as equals. Symbol form: !=.
field.buyer_tax_id notEquals field.seller_tax_id{ "buyer_tax_id": "76.123.456-7", "seller_tax_id": "76.123.456-7" }{
"valid": false,
"errors": [
{ "field": "buyer_tax_id", "code": "VALIDATION_ERROR", "message": "El campo 'buyer_tax_id' no debe ser igual a '76.123.456-7'" }
]
}greaterThan
The value must be a number strictly greater than the parameter. Symbol form: >.
A value or parameter that is not a number fails with INVALID_TYPE; a comparison
that does not hold fails with INVALID_RANGE.
field.total greaterThan 0
field.quantity > 0{ "total": 0, "quantity": "abc" }{
"valid": false,
"errors": [
{ "field": "total", "code": "INVALID_RANGE", "message": "El campo 'total' debe ser mayor que 0" },
{ "field": "quantity", "code": "INVALID_TYPE", "message": "El campo 'quantity' debe ser un número y su parámetro también: 'abc' (string) y '0' (number)" }
]
}lessThan
The value must be a number strictly less than the parameter. Symbol form: <.
field.discount lessThan field.subtotal{ "discount": 500, "subtotal": 400 }{
"valid": false,
"errors": [
{ "field": "discount", "code": "INVALID_RANGE", "message": "El campo 'discount' debe ser menor que 400" }
]
}greaterOrEqual / greaterThanOrEqual
The value must be a number greater than or equal to the parameter. Both names
are the same operator. Symbol form: >=.
field.balance greaterOrEqual 0
field.paid greaterThanOrEqual field.total{ "balance": -10, "paid": 1000, "total": 1000 }{
"valid": false,
"errors": [
{ "field": "balance", "code": "INVALID_RANGE", "message": "El campo 'balance' debe ser mayor o igual a 0" }
]
}lessOrEqual / lessThanOrEqual
The value must be a number less than or equal to the parameter. Both names are
the same operator. Symbol form: <=.
field.used_credit lessOrEqual field.credit_limit
field.tax_rate lessThanOrEqual 0.19{ "used_credit": 1200000, "credit_limit": 1000000, "tax_rate": 0.19 }{
"valid": false,
"errors": [
{ "field": "used_credit", "code": "INVALID_RANGE", "message": "El campo 'used_credit' debe ser menor o igual a 1000000" }
]
}between
The value must be a number within the two limits, both included:
between(min, max).
field.tax_rate between(0, 0.19){ "tax_rate": 0.21 }{
"valid": false,
"errors": [
{ "field": "tax_rate", "code": "INVALID_RANGE", "message": "El campo 'tax_rate' debe estar entre 0 y 0.19" }
]
}range
Same check as between, reported with rule range: range(min, max).
field.month range(1, 12){ "month": 13 }{
"valid": false,
"errors": [
{ "field": "month", "rule": "range", "code": "INVALID_RANGE", "message": "El campo 'month' debe estar entre 1 y 12" }
]
}equalsWithPrecision
The value must equal the expected number when both are rounded to precision
decimals: equalsWithPrecision(expected, precision). precision defaults to 2.
Use it to compare totals that carry rounding, such as a sum of line items against
the document total. In practice, two numbers pass when they differ by less than
half a unit of the last decimal (0.005 with precision 2).
field.subtotal equalsWithPrecision(field.items_total, 2)
field.total equalsWithPrecision(1000, 0){ "subtotal": 1000.004, "items_total": 1000, "total": 1000.6 }{
"valid": false,
"errors": [
{ "field": "total", "code": "VALIDATION_ERROR", "message": "El campo 'total' debe ser igual a '1000' con 0 decimales de precisión, pero es '1000.6'" }
]
}Text and format
length
A string or list must have exactly this length.
field.country_code length 2{ "country_code": "CHL" }{
"valid": false,
"errors": [
{ "field": "country_code", "code": "INVALID_LENGTH", "message": "El campo 'country_code' debe tener longitud 2, actual: 3" }
]
}minLength
A string or list must have at least this length.
field.holder_name minLength 2
field.items minLength 1{ "holder_name": "A", "items": [] }{
"valid": false,
"errors": [
{ "field": "holder_name", "code": "INVALID_LENGTH", "message": "El campo 'holder_name' debe tener al menos 2 caracteres, actual: 1" },
{ "field": "items", "code": "INVALID_LENGTH", "message": "El campo 'items' debe tener al menos 1 caracteres, actual: 0" }
]
}maxLength
A string or list must have at most this length.
field.description maxLength 10{ "description": "Consulting services" }{
"valid": false,
"errors": [
{ "field": "description", "code": "INVALID_LENGTH", "message": "El campo 'description' debe tener máximo 10 caracteres, actual: 19" }
]
}pattern
A string must match a regular expression, written as /regex/flags or as a
quoted string. The expression is not anchored: add ^ and $ to match the whole
value.
field.tax_id pattern /^\d{1,2}\.\d{3}\.\d{3}-[\dkK]$/
field.folio pattern "^F-\d+$"{ "tax_id": "76.123.456-7", "folio": "INV-1" }{
"valid": false,
"errors": [
{ "field": "folio", "code": "INVALID_PATTERN", "message": "El campo 'folio' no cumple con el patrón requerido" }
]
}Patterns run on RE2, which takes time proportional to the length of the value, so no expression can stall validation. RE2 supports most of JavaScript's syntax, with these differences:
- Not supported (rejected): backreferences (
\1,\k<name>), lookahead and lookbehind ((?=…),(?!…),(?<=…),(?<!…)),[],[^],[\b],\cX,\eand\C, and a lone surrogate (\uD83Dwithout its pair). - Different meaning: in JavaScript these were literal characters; in RE2
they mean something.
\Aand\zare the start and end of the text,\ais the bell character,\Q…\Equotes literal text,\p{…}is a Unicode class, and\u{…}is a code point. - Flags:
i,mandswork as in JavaScript, with one difference: withm, only\nends a line, so^a$withmdoes not matchafollowed by\r\n. Withouts,.matches every character except\n, including\r, U+2028 and U+2029. Unicode is always on, souis implied:.matches one character, even an emoji.gandyhave no effect. - Whitespace:
\smatches only ASCII whitespace; it does not match a non-breaking space (U+00A0),\vor U+FEFF. - Escapes and emoji:
\uXXXXand\u{X}work. An emoji works written literally (😀), as\u{1F600}, or as its escaped surrogate pair\uD83D\uDE00, also inside a class that lists emoji one by one ([😀😃]). A lone surrogate, including\u{D800}–\u{DFFF}, is rejected. A range of emoji in a class, such as[😀-🙏], only works in a regular expression literal with theuflag (/^[😀-🙏]$/u); a quoted pattern has no flags, so it cannot express one.
Patterns also have size limits. A pattern can have at most 500 characters, and
it can compile to at most 300 instructions. Ordinary patterns compile to 8–70;
counted repetitions are what grow it, so .{1000} repeated is rejected as too
complex, and so is ^.{1,255}$. To limit the length of a value, use
maxLength instead.
Saving or testing rules through the API or MCP fails with 400, with line and
column, if a pattern is not valid, uses unsupported syntax, or exceeds a limit.
Rules saved before keep running; a pattern that cannot be used, or a value
longer than 10,000 characters, reports RULE_EXECUTION_ERROR instead.
format
A string must have a known format: email, date (YYYY-MM-DD, a real calendar
day), time (HH:MM:SS), datetime or date-time (ISO 8601), url (http or
https), phone or uuid. An unknown format fails with VALIDATION_ERROR.
field.contact_email format email
field.issue_date format date
field.website format url{ "contact_email": "billing@acme.example", "issue_date": "2024-02-30", "website": "ftp://acme.example" }{
"valid": false,
"errors": [
{ "field": "issue_date", "code": "INVALID_FORMAT", "message": "El campo 'issue_date' debe ser una fecha válida" },
{ "field": "website", "code": "INVALID_FORMAT", "message": "El campo 'website' debe tener formato url" }
]
}startsWith
A string must start with the given text (case-sensitive).
field.folio startsWith "F-"{ "folio": "B-1001" }{
"valid": false,
"errors": [
{ "field": "folio", "code": "VALIDATION_ERROR", "message": "El campo 'folio' debe empezar con 'F-'" }
]
}endsWith
A string must end with the given text (case-sensitive).
field.file_name endsWith ".pdf"{ "file_name": "invoice.docx" }{
"valid": false,
"errors": [
{ "field": "file_name", "code": "VALIDATION_ERROR", "message": "El campo 'file_name' debe terminar con '.pdf'" }
]
}includes
A string must contain the given text (case-sensitive).
field.supplier_name includes "SpA"{ "supplier_name": "Acme Ltda" }{
"valid": false,
"errors": [
{ "field": "supplier_name", "code": "VALIDATION_ERROR", "message": "El campo 'supplier_name' debe contener 'SpA'" }
]
}Lists of values
contains
A list must contain the given element, or a string must contain the given text.
List elements are compared exactly: 3 does not match "3".
field.tags contains "urgent"
field.payment_terms contains "30"{ "tags": ["paid", "export"], "payment_terms": "30 days" }{
"valid": false,
"errors": [
{ "field": "tags", "code": "VALIDATION_ERROR", "message": "El campo 'tags' debe contener 'urgent'" }
]
}in
The value must be one of the listed values: in("CLP", "USD"). Values are
compared exactly, without number normalization: "2" is not in in(1, 2, 3).
field.currency in("CLP", "USD"){ "currency": "EUR" }{
"valid": false,
"errors": [
{ "field": "currency", "code": "INVALID_ENUM", "message": "El campo 'currency' debe ser uno de: CLP, USD" }
]
}notIn
The value must not be one of the listed values, compared exactly.
field.status notIn("void", "draft"){ "status": "void" }{
"valid": false,
"errors": [
{ "field": "status", "code": "VALIDATION_ERROR", "message": "El campo 'status' no puede ser uno de: void, draft" }
]
}Dates
Date operators compare calendar days. They accept YYYY-MM-DD, ISO 8601
date-times (converted to their UTC day), and DD/MM/YYYY or DD-MM-YYYY. The
parameter can be a literal date or a field reference. A value that is not a valid
date fails with INVALID_TYPE; a comparison that does not hold fails with
VALIDATION_ERROR. See dates.
dateEquals
Both dates must be the same day.
field.delivery_date dateEquals field.issue_date{ "delivery_date": "15/03/2024", "issue_date": "2024-03-15" }{ "valid": true, "errors": [] }dateNotEquals
The dates must be different days.
field.due_date dateNotEquals field.issue_date{ "due_date": "2024-03-15", "issue_date": "15-03-2024" }{
"valid": false,
"errors": [
{ "field": "due_date", "code": "VALIDATION_ERROR", "message": "El campo 'due_date' no debe ser igual a '2024-03-15'" }
]
}dateAfter
The date must be strictly after the parameter.
field.due_date dateAfter field.issue_date{ "due_date": "2024-03-01", "issue_date": "2024-03-15" }{
"valid": false,
"errors": [
{ "field": "due_date", "code": "VALIDATION_ERROR", "message": "El campo 'due_date' debe ser posterior a '2024-03-15'" }
]
}dateBefore
The date must be strictly before the parameter.
field.issue_date dateBefore "2030-01-01"{ "issue_date": "2031-06-30" }{
"valid": false,
"errors": [
{ "field": "issue_date", "code": "VALIDATION_ERROR", "message": "El campo 'issue_date' debe ser anterior a '2030-01-01'" }
]
}dateAfterOrEqual
The date must be the same day as the parameter or later.
field.period_end dateAfterOrEqual field.period_start{ "period_start": "2024-05-01", "period_end": "30/04/2024" }{
"valid": false,
"errors": [
{ "field": "period_end", "code": "VALIDATION_ERROR", "message": "El campo 'period_end' debe ser posterior o igual a '2024-05-01'" }
]
}dateBeforeOrEqual
The date must be the same day as the parameter or earlier.
field.issue_date dateBeforeOrEqual field.received_date{ "issue_date": "2024-05-02", "received_date": "2024-05-01" }{
"valid": false,
"errors": [
{ "field": "issue_date", "code": "VALIDATION_ERROR", "message": "El campo 'issue_date' debe ser anterior o igual a '2024-05-01'" }
]
}dateBetween
The date must fall between two dates, both included: dateBetween(start, end).
field.transaction_date dateBetween(field.period_start, field.period_end){ "transaction_date": "2024-07-01", "period_start": "2024-06-01", "period_end": "2024-06-30" }{
"valid": false,
"errors": [
{ "field": "transaction_date", "code": "VALIDATION_ERROR", "message": "El campo 'transaction_date' debe estar entre '2024-06-01' y '2024-06-30'" }
]
}Calculated values
These operators compute a value and store it as calculated.<name>. They only
work inside define; written as a standalone rule, such as field.items sum,
they fail with the message "El operador 'sum' solo se puede usar dentro de
define". See calculated values.
define
define <name> <expression> stores a value under calculated.<name>. The
expression is a field, a literal, or one of the operations below. define
itself never produces an issue unless the calculation fails.
define declared field.total
define minimum 1000
calculated.declared greaterOrEqual calculated.minimum{ "total": 800 }{
"valid": false,
"errors": [
{ "field": "calculated.declared", "code": "INVALID_RANGE", "message": "El campo 'calculated.declared' debe ser mayor o igual a 1000" }
]
}sum
Adds every number from its operands. An operand can be a field, a [*] wildcard
over a list, a calculated value or a literal. An empty list adds up to 0; a
value that is not a number makes define fail with VALIDATION_ERROR.
define itemsTotal sum field.items[*].amount
calculated.itemsTotal equals field.subtotal{ "subtotal": 2500000, "items": [{ "amount": "1.000.000" }, { "amount": 1500000 }] }{ "valid": true, "errors": [] }count
Counts the elements of a list, written as field.items[*] or field.items.
Add where <property> <comparison> <number> to count only the elements that
match, with >, <, >=, <=, =, == or !=.
define lines count field.items[*]
define bigLines count field.items[*] where amount > 100000
calculated.lines equals 3
calculated.bigLines lessOrEqual 1{ "items": [{ "amount": 50000 }, { "amount": 200000 }, { "amount": 300000 }] }{
"valid": false,
"errors": [
{ "field": "calculated.bigLines", "code": "INVALID_RANGE", "message": "El campo 'calculated.bigLines' debe ser menor o igual a 1" }
]
}average
The average of every number from its operands; 0 when there are none.
define averagePrice average field.items[*].unit_price
calculated.averagePrice lessThan 1000{ "items": [{ "unit_price": 900 }, { "unit_price": 1500 }] }{
"valid": false,
"errors": [
{ "field": "calculated.averagePrice", "code": "INVALID_RANGE", "message": "El campo 'calculated.averagePrice' debe ser menor que 1000" }
]
}min
The smallest number from its operands.
define lowestQuantity min field.items[*].quantity
calculated.lowestQuantity greaterThan 0{ "items": [{ "quantity": 3 }, { "quantity": 0 }] }{
"valid": false,
"errors": [
{ "field": "calculated.lowestQuantity", "code": "INVALID_RANGE", "message": "El campo 'calculated.lowestQuantity' debe ser mayor que 0" }
]
}max
The largest number from its operands.
define largestCharge max field.transactions[*].amount
calculated.largestCharge lessOrEqual field.credit_limit{ "credit_limit": 500000, "transactions": [{ "amount": 120000 }, { "amount": 480000 }] }{ "valid": true, "errors": [] }subtract
Subtracts every following operand from the first one. Each operand is added up
first, so a [*] wildcard subtracts the sum of the list. It needs at least two
operands.
define pending subtract field.total field.payments[*].amount
calculated.pending equals field.balance_due{ "total": 1000, "payments": [{ "amount": 300 }, { "amount": 200 }], "balance_due": 400 }{
"valid": false,
"errors": [
{ "field": "calculated.pending", "code": "VALIDATION_ERROR", "message": "El campo 'calculated.pending' debe ser igual a '400', pero es '500'" }
]
}define also accepts two operations that are not standalone operators: add, the
same as sum, written as add(field.a, field.b), and concat, which joins
values into text. See the validation guide.
Related pages
Validating Extracted Data
Check the data Docutray extracts with JSON Schema and business rules written in the validation DSL — syntax, conditional and list rules, calculated totals, full examples, issue codes, and how to manage rules over REST and MCP.
API Reference
Complete Docutray REST API reference — endpoints for document conversion, identification, document-type management, knowledge bases, and webhook automation.