Skip to main content
A Validate step runs a list of rules over the data an Extract step produced and flags anything that fails. Every rule is one of two kinds, and a single Validate step can mix both.
Rule kindHow it’s checkedBest forCost
AIA model reads your plain-language description and judges the dataFuzzy / semantic conditions (“the vendor looks like a real company”, “the two names refer to the same person”)Billed per rule
DeterministicA structured check runs in plain code — no modelExact, testable conditions (a number in range, a + b = c, a value in a set, a pattern, a required field)Free
Reach for a deterministic rule whenever the condition is something you could check with a calculator or a lookup — it’s instant, free, and never flakes. Use an AI rule when the condition needs judgement or language understanding.

Deterministic checks

Each deterministic rule is built from one check. Fields are referenced by name (the field name from the upstream Extract step).
CheckWhat it assertsExample
Number in rangeA numeric field falls within boundsamount between 0 and 1000000
Date in rangeA date field falls within bounds — each an ISO date or today (relative)expiry_date on or after today (not expired)
ArithmeticA sum / difference / product of fields equals another field, within a tolerancesubtotal + tax = total (±0.01)
ComparisonA field compares (=, , >, , <, ) to a fixed value or another fieldend_date ≥ start_date
One ofA field’s value is in an allowed setcurrency is one of EUR, USD, GBP
PatternA field matches a regular expressioniban matches ^[A-Z]{2}\d{2}
RequiredA field is present and not emptyvendor_name is filled in
Each check resolves to pass, fail, or inconclusive (when the referenced field is missing or can’t be read as the expected type). Numbers are read leniently — "€1.234,56" and "1,234.56" both parse — and dates accept the common written formats.

Build a rule in Studio

  1. Add a Validate step after the Extract step whose output you want to check.
  2. Open the step, go to the Rules tab, and click Add rule.
  3. Choose AI or Deterministic with the toggle.
    • AI — type the rule in plain language; reference a field with the field picker.
    • Deterministic — pick a check type, choose the field(s), and enter the values.
  4. Set a severity (error or warning) and save.
Failures appear in the Validation tab of the results, next to the extracted data.

Author rules via the API

Validation rules are part of the validate node in the typed-graph create body — no separate endpoint. A deterministic rule carries a check; an AI rule carries a description. Reference fields by their name as defined on the upstream extract.
{
  "name": "Invoice with checks",
  "nodes": [
    { "id": "parse_1", "type": "parse" },
    {
      "id": "extract_1",
      "type": "extract",
      "extraction_schema": {
        "fields": [
          { "name": "invoice_date", "description": "Invoice date.", "data_type": "date" },
          { "name": "subtotal", "description": "Subtotal before tax.", "data_type": "float" },
          { "name": "tax", "description": "Tax amount.", "data_type": "float" },
          { "name": "total", "description": "Grand total.", "data_type": "float" },
          { "name": "currency", "description": "ISO currency code.", "data_type": "string" }
        ]
      }
    },
    {
      "id": "validate_1",
      "type": "validate",
      "rules": [
        {
          "id": "not-expired",
          "kind": "deterministic",
          "severity": "error",
          "check": { "type": "date", "field": "invoice_date", "earliest": "2000-01-01", "latest": "today" }
        },
        {
          "id": "totals-add-up",
          "kind": "deterministic",
          "severity": "error",
          "check": { "type": "arithmetic", "operands": ["subtotal", "tax"], "operator": "sum", "equals": "total", "tolerance": 0.01 }
        },
        {
          "id": "known-currency",
          "kind": "deterministic",
          "severity": "warning",
          "check": { "type": "one_of", "field": "currency", "allowed": ["EUR", "USD", "GBP"] }
        },
        {
          "id": "vendor-legit",
          "kind": "ai",
          "severity": "warning",
          "description": "The vendor is a real, named company."
        }
      ]
    }
  ],
  "edges": [
    { "source": "parse_1", "target": "extract_1" },
    { "source": "extract_1", "target": "validate_1" }
  ]
}

Check payloads

{ "type": "range", "field": "amount", "min": 0, "max": null }
// Date bounds: an ISO date "YYYY-MM-DD", "today" (relative), or null.
{ "type": "date", "field": "expiry_date", "earliest": "today", "latest": null }   // not expired
{ "type": "date", "field": "invoice_date", "earliest": "2024-01-01", "latest": "2024-12-31" }
{ "type": "arithmetic", "operands": ["subtotal", "tax"], "operator": "sum", "equals": "total", "tolerance": 0.01 }
{ "type": "comparison", "left": "end_date", "op": ">=", "right": { "source": "field", "field": "start_date" } }
{ "type": "comparison", "left": "year", "op": ">=", "right": { "source": "literal", "value": 2000 } }
{ "type": "one_of", "field": "currency", "allowed": ["EUR", "USD", "GBP"], "case_sensitive": false }
{ "type": "regex", "field": "iban", "pattern": "^[A-Z]{2}\\d{2}" }
{ "type": "required", "field": "vendor_name" }
operator defaults to sum; tolerance to 0; case_sensitive to false. Unset date bounds are null.

Author rules with the SDK

The check classes are exported from the workflow package; pass ValidationRules to .validate().
from anyformat.workflow import (
    Schema, ValidationRule,
    DateCheck, ArithmeticCheck, OneOfCheck,
)

wf = (
    client.workflow("Invoice with checks")
    .parse()
    .extract([
        Schema.date("invoice_date", "Invoice date."),
        Schema.float("subtotal", "Subtotal before tax."),
        Schema.float("tax", "Tax amount."),
        Schema.float("total", "Grand total."),
        Schema.string("currency", "ISO currency code."),
    ])
    .validate(
        ValidationRule(id="not-expired", kind="deterministic", severity="error",
            check=DateCheck(type="date", field="invoice_date", earliest="2000-01-01", latest="today")),
        ValidationRule(id="totals-add-up", kind="deterministic", severity="error",
            check=ArithmeticCheck(type="arithmetic", operands=["subtotal", "tax"], equals="total", tolerance=0.01)),
        ValidationRule(id="known-currency", kind="deterministic", severity="warning",
            check=OneOfCheck(type="one_of", field="currency", allowed=["EUR", "USD", "GBP"])),
        # An AI rule still works the same way:
        ValidationRule(id="vendor-legit", description="The vendor is a real, named company."),
    )
    .create()
)
const wf = await client
  .workflow("Invoice with checks")
  .parse()
  .extract([
    Schema.date("invoice_date", "Invoice date."),
    Schema.float("subtotal", "Subtotal before tax."),
    Schema.float("tax", "Tax amount."),
    Schema.float("total", "Grand total."),
    Schema.string("currency", "ISO currency code."),
  ])
  .validate([
    { id: "not-expired", kind: "deterministic", severity: "error",
      check: { type: "date", field: "invoice_date", earliest: "2000-01-01", latest: "today" } },
    { id: "totals-add-up", kind: "deterministic", severity: "error",
      check: { type: "arithmetic", operands: ["subtotal", "tax"], operator: "sum", equals: "total", tolerance: 0.01 } },
    { id: "known-currency", kind: "deterministic", severity: "warning",
      check: { type: "one_of", field: "currency", allowed: ["EUR", "USD", "GBP"] } },
    { id: "vendor-legit", kind: "ai", description: "The vendor is a real, named company." },
  ])
  .create();
Rules referencing a field by name require that field to exist on the upstream Extract step. The Studio also lets you reference fields you haven’t saved yet by their persistent id — both forms resolve at run time.

What’s next?

Workflow steps

The five building blocks, including Validate.

How credits work

AI rules bill per rule; deterministic rules are free.