Validation Stages
Manage a Spec's validation stages: positional quality checkpoints that validate the pipeline phases before them. Create, reorder, and replay blocking stages.
These endpoints configure a Spec (the same entity as /v1/schemas). A Spec is a Schema plus the rail of stages that govern how documents flow through the engine — source, field registry, extraction, resolution, validation, and data product — together with the Data Policies and validation gates those stages reference. You configure a Spec with the endpoints here, then you run it by compiling the rail into a pipeline via POST /v1/pipelines. Field-level normalization and transform config is not set on fields here: it lives in Data Policies (/v1/data-policies), which the resolution stage references.
A validation stage is a positional quality checkpoint. It validates the output of the pipeline phases that ran before it at its rail position, so where a stage sits in the order determines what it checks. Several checkpoints may appear in one rail, each guarding a different segment. Stages are returned ordered by position and are appended to the end of the sequence on create.
Each stage carries a severity: info logs only, warning flags failing cells, and blocking halts the failing field and its dependents. A blocking stage also carries a blockingConfig that decides the pass threshold, the critical_fields that always block on failure, and the on_block routing. The write path enforces what the engine actually supports: on_block must be review_queue or hold, and scope must be document — on_block: auto_retry, other scopes, and retry are rejected with a 400 explaining they are not yet enforced. The fieldScope narrows a stage to a subset of fieldKeys, or leaves it validating every field.
The threshold decides when failing fields actually block. { "type": "any" } blocks on any failure; { "type": "percentage", "min_pass_rate": 95 } blocks only when the fail rate exceeds the allowance — note min_pass_rate is 0–100, not a fraction; { "type": "count", "max_failures": n } blocks once n fields fail. Fields listed in critical_fields block on failure regardless of the threshold. A percentage or count threshold with a missing parameter degrades to any (fail closed) rather than silently disabling the gate.
/v1/schemas/{id}/validation-stagesPath parameters
/v1/schemas/{id}/validation-stagesBody parameters
infoallCreate a blocking checkpoint
curl -X POST https://api.talonic.com/v1/schemas/a1b2c3d4-e5f6-7890-abcd-ef1234567890/validation-stages \
-H "Authorization: Bearer tlnc_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Totals must reconcile",
"description": "Block delivery when line items do not sum to the invoice total.",
"severity": "blocking",
"config": { "nshot": { "enabled": true, "runs": 2 } },
"blockingConfig": {
"threshold": { "type": "percentage", "min_pass_rate": 95 },
"critical_fields": ["total"],
"scope": "document",
"on_block": "review_queue"
}
}'/v1/schemas/{id}/validation-stages/{stageId}/v1/schemas/{id}/validation-stages/{stageId}Body parameters (all optional)
/v1/schemas/{id}/validation-stages/{stageId}/v1/schemas/{id}/validation-stages/reorderBody parameters
/v1/schemas/{id}/validation-stages/{stageId}/replay-blockingBody parameters
falseResponse
A stage object reflects the persisted validation_stages row. ordinal is the stage position in the rail. blocking_config is null unless severity is blocking.
Stage fields
Response
[
{
"id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"user_schema_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Totals must reconcile",
"description": "Block delivery when line items do not sum to the invoice total.",
"severity": "blocking",
"config": {
"nshot": { "enabled": true, "runs": 2 }
},
"blocking_config": {
"threshold": { "type": "percentage", "min_pass_rate": 95 },
"critical_fields": ["total"],
"scope": "document",
"on_block": "review_queue"
},
"field_scope": "all",
"field_keys": [],
"ordinal": 0,
"created_at": "2024-09-14T10:32:00.000Z",
"updated_at": "2024-09-14T10:32:00.000Z"
}
]Reorder returns { "reordered": true }. Delete returns { "deleted": true, "id": "<stageId>" }. Replay-blocking returns the blast radius of retroactively applying the stage: documents scanned and affected, fields blocked, a breakdown of skipped fields (already in review, cleared by a prior review decision, no field state, or capacity-demoted by triage), and per-pipeline counts. With dryRun: true the same counts are a preview and nothing is written.
Response (replay-blocking, dryRun)
{
"stageId": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"dryRun": true,
"documentsScanned": 42,
"documentsAffected": 6,
"fieldsBlocked": 9,
"skipped": {
"alreadyInReview": 2,
"clearedByReview": 1,
"noFieldState": 0,
"capacityDemoted": 0
},
"byPipeline": [
{
"pipelineId": "1a0c681d-ea20-4bb4-8892-01a6d7f834da",
"pipelineName": "Invoices July",
"documentsAffected": 6,
"fieldsBlocked": 9
}
]
}Errors
Error responses