Composing the Rail
The rail is the ordered sequence of stages that defines a Spec. You compose it visually in the Spec editor, or set it through the API. When you run the Spec, the rail compiles deterministically into the pipeline phases the engine executes. The rail is a faithful description of the run: the compiler preserves order and stage configuration, so what you see in the editor is exactly what the engine does.
A typical rail reads left to right as the document journey: Source (where documents come from), Field Registry (deterministic transfer), Extraction (AI capture of gaps), Resolution (Data Policy normalization), Validation (quality checkpoints), and a terminal Data Product. Beyond that core, the rail carries first-class stages for Matching (decisive reference linkage), Agent (via MCP) (external-agent field filling — placed after Assembly it becomes Agentic Review), and Assembly (post-run composition). You add only the stages you need. The minimum runnable rail is a single extraction stage; everything else is optional configuration layered on top.
Rail stage types
| Parameter | Type | Description |
|---|---|---|
| schema | extraction | The extraction stage. Compiles to the Extraction phase that runs Claude over gap fields. The minimum runnable rail is a single schema stage. |
| registry | transfer | Field Registry transfer. Compiles to the Transfer phase that fills cells deterministically from known field occurrences at no AI cost. |
| resolve | resolution | Resolution. Carries json.policy_ids; compiles to one resolution phase per active Data Policy, applied in order. |
| valid | checkpoint | A positional validation checkpoint. Carries json.stage_ids (member gates) and optional json.target_phases. Validates the output of the stages before it. |
| reconcile | matching | The Matching stage. Bound to a matcher stage via json.matcher_stage_id, it compiles to a per-document matching phase at its rail position with a decisive verdict (auto_match, shortlist, or no_match). Unbound, it emits no phase. |
| agent | agent | Agent (via MCP). Before Assembly it compiles to a per-document agent phase that parks the document as a task for an external agent; after Assembly it becomes Agentic Review, running once per composed record and writing canonical cells. |
| assembly | compose | Post-run compose. Carries json.grouping_field and json.anchor_field, and compiles only when both are set. Always runs last, once every document is terminal, regardless of its rail position. |
| source / deliver | marker | Composition markers for where documents enter and where the data product is delivered. They frame the rail; the per-document phases sit between them. |
Each stage points at server-backed configuration rather than carrying its own copy of the data. A resolve stage references the Data Policies bound to the schema by ID. A valid stage references validation gates configured through the validation-stages endpoints. A reconcile stage references its matcher stage by ID. An assembly stage names the grouping and anchor fields. This indirection keeps the rail small and means editing a policy or a gate updates every Spec that uses it, with no rail rewrite.
Compiling the rail produces phases from a seven-type union: transfer, extraction, resolution, agent, validation, assembly, and matching. A few authoring stages expand rather than mapping one-to-one: a switch stage (Extraction with Routing) compiles to one extraction phase per lane and suppresses the plain full extraction; business_rules and decide stages are authoring conveniences that expand into validation checkpoints before compile. And position changes meaning: a resolve, valid, or agent stage dragged after the Assembly node becomes a pipeline-scoped tail step over the composed records instead of a per-document phase.
The agent stage carries its own configuration: the input fields snapshotted into each task, the output fields the agent is expected to fill (each with a data type and required flag), free-text instructions, and an agent binding naming which API key or OAuth client may claim the tasks. A lease keeps a claimed task exclusive (default 15 minutes, renewed by heartbeat), and a timeout (default 24 hours) decides what happens to a task nobody completes: hold the document, skip the stage, or route the fields to review. An autonomy block bounds what the agent may do, from observe-only, through propose (every output lands as a pending approval), to autonomous writes gated by a confidence floor.
Setting the rail via API
Set the rail with a PUT to the schema rail endpoint. Pass an array of stages; an empty array clears the rail. The example below composes a four-stage rail: registry transfer, extraction, resolution against one policy, and a validation checkpoint that covers the extraction output.
curl -X PUT https://api.talonic.com/v1/schemas/sch_delivery_notes/rail \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rail": [
{ "type": "registry", "name": "Field Registry" },
{ "type": "schema", "name": "Extraction" },
{ "type": "resolve", "name": "Normalize",
"json": { "policy_ids": ["pol_dates_amounts"] } },
{ "type": "valid", "name": "Checks",
"json": { "stage_ids": ["vs_required_fields"] } }
]
}'curl -X PUT https://api.talonic.com/v1/schemas/sch_quick_start/rail \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "rail": [ { "type": "schema", "name": "Extraction" } ] }'
# One extraction stage is enough to run. An empty rail array clears the rail.