Skip to main content

Review Gates

A review gate (formerly called an approval gate) in Talonic is a blocking validation checkpoint that gates the pipeline at its position in the Spec rail. Fields that fail the checkpoint mechanisms do not flow downstream automatically: they are held at the checkpoint or routed to the field-review queue for human resolution. This replaces the older auto-approve-threshold model: there is no per-schema confidence threshold that emits a result.approved signal.

The gating behaviour is governed by the checkpoint blocking_config. The on_block setting selects what happens to failing fields: review_queue sends them to the central field-review queue (see Field Review) where a reviewer approves, corrects, or overrides each one, while hold parks them at the checkpoint until they are resolved. Fields that pass the checkpoint continue downstream without manual intervention.

Start by making your most consequential checkpoint blocking with on_block: review_queue, so failing fields reach a human while the rest of the run proceeds. Use hold when nothing downstream of the checkpoint should run until the failures are cleared. As you gain trust in a stage, you can switch its checkpoint to non-blocking so it records pass/fail for inspection without gating the run.

A practical example: you place a blocking checkpoint after the resolution stage on your invoice pipeline with the evidence and business-rules mechanisms and on_block: review_queue. An invoice whose fields all pass continues to the data product unattended. An invoice with a failing total_amount has only that field routed to the field-review queue; a reviewer corrects it, and the corrected value continues downstream. This field-level routing keeps clean fields moving while focusing human attention on the specific failures.

  • A blocking validation checkpoint gates the pipeline at its position in the rail
  • blocking_config.on_block = review_queue routes failing fields to the central field-review queue
  • blocking_config.on_block = hold parks failing fields at the checkpoint until resolved
  • Routing is field-level: passing fields continue downstream while failures are held
  • There is no auto-approve confidence threshold and no result.approved signal
  • Switch a checkpoint to non-blocking to record pass/fail without gating the run
Configure a blocking checkpoint to route failures to review
# A Validation node in the Spec rail compiles to a validation phase.
# Its blocking_config decides what happens to failing fields.
{
  "type": "validation",
  "stage_ids": ["gate_evidence", "gate_business_rules"],
  "blocking_config": {
    "blocking": true,
    "on_block": "review_queue"   // or "hold"
  }
}

# on_block = review_queue -> failing fields land in the central
#   field-review queue (worked from Products -> Review Mode)
#   for approve / correct / override.
# on_block = hold        -> failing fields are parked at the
#   checkpoint until they are resolved.
# Passing fields continue downstream with no manual step.

Blocking checkpoints and the field-review queue together create a controlled path from extraction to delivery. Fields that pass every mechanism at a checkpoint flow downstream and into the data product without manual intervention. Failing fields are routed (review_queue) or held (hold) so a reviewer resolves them before they continue. This field-level gating keeps clean data moving while ensuring the specific failures get human oversight before reaching production systems.

A blocking validation checkpoint gates the pipeline at its rail position. Set blocking_config.on_block to review_queue to route failing fields to the central field-review queue, or to hold to park them at the checkpoint. There is no auto-approve confidence threshold and no result.approved signal in this model.

Tuning when a gate blocks

A gate only blocks when its severity is blocking; info (the default) logs verdicts and warning flags cells without holding them. Within a blocking gate, blocking_config.threshold decides how much failure a document may carry before anything is held. { "type": "any" } blocks every failed field and is the default. { "type": "percentage", "min_pass_rate": 90 } blocks the failed fields only when the share of checked fields that failed exceeds 10%. { "type": "count", "max_failures": 3 } blocks once a document has three or more failed fields. The percentage and count forms trade review volume for tolerance: below the bar, the failures are recorded but the values flow on.

Two settings sit on top of the threshold. critical_fields lists fields that block on any failure regardless of the threshold, which is the right home for must-be-right values such as an invoice total or a contract end date. verdict_sensitivity.similar controls how an ambiguous n-shot verdict is treated: by default a similar result is ignored and only fail or different counts, while setting it to fail routes the medium-disagreement cells too. A ground-truth mapping can also escalate a single field to blocking or silence it to warning or info, and a silenced field neither blocks nor counts toward the percentage or count threshold of its siblings.

The configuration is checked when you save it, so a gate never claims behaviour it does not have. on_block: auto_retry, any scope other than document, and a retry block are rejected with a 400 VALIDATION_ERROR, because the pipeline does not enforce them. A percentage threshold saved without min_pass_rate, or a count threshold without max_failures, is rejected for the same reason. If an older stage still carries an unparseable threshold, the run fails closed: it treats the threshold as any and blocks every failed field rather than silently disabling the gate.

A review gate can also bound how long a held field waits. With auto_release_hours set, a scheduler sweep (every 15 minutes, up to 50 fields per pass) approves gate-blocked fields that have sat unreviewed past the deadline and records the reason "Auto-released: unreviewed for N h" in the decision log, attributed to the machine rather than a reviewer. This applies to gate blocks only: a field held by a declarative requires_review rule has no stage config and is never auto-released. Setting notify.webhook_url sends a plain JSON POST (event: "fields_blocked", with the stage name, pipeline id, and field keys) each time the gate blocks fields. That notification is fire-and-forget, with a five-second timeout and no retries, so use signed delivery webhooks for anything that must arrive.

Make an existing gate blocking with a tolerance and critical fields
curl -X PATCH https://api.talonic.com/v1/schemas/3f6c1b7e-2a4d-4c1e-9b8a-5d2e7f9a1c04/validation-stages/8b1d4e2a-6c3f-4a7b-9e5d-1f2a3b4c5d6e \
  -H "Authorization: Bearer $TALONIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "severity": "blocking",
    "blockingConfig": {
      "threshold": { "type": "count", "max_failures": 2 },
      "critical_fields": ["total_amount", "invoice_date"],
      "verdict_sensitivity": { "similar": "ignore", "different": "fail" },
      "scope": "document",
      "on_block": "review_queue",
      "auto_release_hours": 72
    }
  }'

Fields a gate routed to the queue are listed on GET /v1/field-reviews. Filter with trigger=gate to separate gate blocks from declarative holds, and with pipeline_id to scope to one run. Each item carries a reason class (for example failed_check, missing_value, disagreement, or low_confidence) and links to its detail and resolve routes. Resolve it with POST /v1/field-reviews/{docId}/{fieldKey}/resolve and an action of approve, correct, or override. A resolved field resumes from the phase after the gate, and any dependent fields that were waiting on it continue too.

GET /v1/field-reviews?trigger=gate (one item)
{
  "data": [
    {
      "pipeline_document_id": "c2a7e9f1-4b3d-4e8a-a1c6-7d9e0f2b3a45",
      "field_key": "total_amount",
      "field_display_name": "Total amount",
      "status": "blocked",
      "trigger": "gate",
      "reason": "failed_check",
      "current_value": "1.240,00",
      "confidence": 0.62,
      "requested_at": "2026-10-02T09:14:31.000Z",
      "pipeline": { "id": "5e8d2c1a-9f7b-4a3e-b6d4-0c1f2e3a4b5c", "name": "Supplier invoices" },
      "document": { "id": "a9b8c7d6-e5f4-4a3b-8c2d-1e0f9a8b7c6d", "name": "invoice-1042.pdf" },
      "links": {
        "self": "/v1/field-reviews/c2a7e9f1-4b3d-4e8a-a1c6-7d9e0f2b3a45/total_amount",
        "resolve": "/v1/field-reviews/c2a7e9f1-4b3d-4e8a-a1c6-7d9e0f2b3a45/total_amount/resolve"
      }
    }
  ],
  "pagination": { "total": 1, "limit": 20, "has_more": false, "next_cursor": null }
}
Gates on a Spec authored in the Spec editor are owned by that Spec document. Public API writes to its validation stages (create, update, delete, reorder, replay) return 409 spec2_owned, because the next publish would overwrite them. Reads stay open, so you can still list gates and work the field-review queue through the API.

Frequently asked questions

What are review gates?+
A review gate (formerly approval gate) is a blocking validation checkpoint in the Spec rail. Fields that fail the checkpoint mechanisms are gated: they route to the central field-review queue or are held, governed by the checkpoint blocking_config. There is no auto-approve confidence threshold.
What does blocking_config.on_block control?+
It selects what happens to failing fields. review_queue routes them to the central field-review queue for approve, correct, or override. hold parks them at the checkpoint until they are resolved. Passing fields continue downstream regardless.
Is routing per record or per field?+
Per field. When a checkpoint blocks, only the fields that fail are routed to the review queue or held. The fields that pass continue downstream, so a single failing field does not stall the rest of the record.
Can different pipeline stages have different gates?+
Yes. Validation checkpoints are positional, so you can place several along the rail and make each one blocking or non-blocking independently. Each checkpoint validates the output of the stage immediately before it.
What happens if nobody reviews a blocked field?+
By default it stays held, and the value never reaches the data product. Set auto_release_hours on the gate to have a scheduled sweep approve gate-blocked fields once they have waited that long; each release is logged with an explicit auto-release reason. Fields held by a declarative requires_review rule are never auto-released.
Why was my blocking_config rejected with a 400?+
The API only accepts options the pipeline actually enforces. on_block auto_retry, a scope other than document, and a retry block are rejected, as is a percentage threshold without min_pass_rate or a count threshold without max_failures. Remove the option or switch to a supported value and save again.