Dataset Templates
A data product packages your extraction, resolution, and validation outputs into a shareable, deliverable dataset. Each product wraps one or more run_ids, a validation_run_id, or a pipeline_id, and moves through a status lifecycle of draft, ready, published, and archived (archiving is a soft delete, so the underlying data is retained).
Data products live under Data Products (/delivery/data-products), a tabbed shell that embeds the data-products list alongside a Delivery tab (the Delivery surface itself is hidden from the main nav). Open the list to see each product with its name, the schema it was built against, document counts, and its status.
Shaping the output
To shape the output of a product, configure the upstream pipeline rather than a separate template. Column order, renamed headers, excluded fields, and transforms are governed by the schema and the Resolution stage of the pipeline that feeds the product. Re-running the pipeline produces a fresh set of run values, and the data product reflects them on its next assembly.
Most teams keep one data product per downstream consumer. If your finance team and operations team need different views of the same documents, build two products from the appropriate runs (or pipelines) rather than reconfiguring a single export each time. Archive a product when it is no longer needed: the soft delete keeps its history available for audit while removing it from the active list.
- A data product wraps
run_ids[], avalidation_run_id, or apipeline_id - Status lifecycle:
draft→ready→published→archived(archive is a soft delete) - Output shape is governed by the schema and pipeline, not a separate template object
- Lives under Data Products (/delivery/data-products), a tabbed shell with the list plus a Delivery tab
- The Dataset Templates page is a non-functional stub
- One data product per downstream consumer is the recommended pattern
curl -s "https://api.talonic.com/v1/data-products?status=published" \
-H "Authorization: Bearer $TALONIC_API_KEY"
# Response:
# {
# "data": [
# {
# "id": "6a7b8c9d-…",
# "name": "Q1 2026 Invoice Extract",
# "description": "Finance handover set",
# "schema_id": "…",
# "run_id": "…",
# "status": "published",
# "created_at": "2026-04-01T08:00:00Z"
# }
# ],
# "pagination": { ... }
# }
# status filter accepts: draft, ready, published, archived# The public API create path is run-backed; validation-session and
# pipeline-backed products are created from within the app.
curl -X POST https://api.talonic.com/v1/data-products \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Q1 2026 Invoice Extract",
"run_ids": ["d4e5f6a7-…"],
"thresholds": { "min_confidence": 0.8 }
}'
# -> 201: the created product, with its share token mintedThe data product is the bridge between your pipeline and production-ready delivery. A schema defines what fields to extract; the pipeline (extraction, resolution, validation) governs how those fields appear in the final output. The product wraps the resulting runs (or a validation run, or a pipeline) so the assembled dataset is reproducible. To serve different consumers, build separate products from the appropriate runs rather than maintaining a separate template layer.
Shared group-grain data products
Beyond single-pipeline products, a shared data product is a standing, group-grain dataset that many pipelines feed over time. It is defined once — a name, a stable product_key slug that pipelines bind to, and a definition describing the record identity — and every bound pipeline then contributes its documents into it. The grain is the group: documents from any contributing pipeline that share the same identity key (an order number, a load ID, a contract reference) fold into one group, and one group is one row of the product. This is how a load's invoice, its rate confirmation, and its proof of delivery — each arriving through a different Spec — become a single composed record.
Contribution is keyed. Each contributing pipeline binds a key_field, and every document's key value is normalized (trimmed, inner whitespace collapsed, uppercased by default) before it is hashed into a group identity — so "ord-1042" and "ORD 1042" land in the same group. Each contribution records its evidence: the key field, the raw value, the canonical value, and the pipeline it came from. Contributions are append-only and idempotent — re-running a pipeline with unchanged data is a no-op, and changed data supersedes the earlier contribution rather than duplicating it. A document with no key value is, by default, parked as its own provisional single-document group (or held entirely, if the definition says so) rather than guessed into a group.
The definition can declare roles with cardinalities — say one invoice (exactly 1), one optional rate_confirmation (0..1), any number of pod documents (0..n) — plus a rollup naming which role wins each field on the composed row. Group readiness is then rule-driven: roles_complete requires the named roles to be present, no_open_conflicts requires no cardinality ceiling to be exceeded, and amount_match compares two amounts within a tolerance. Amount checks are role-scoped where both sides share one schema field name: left_role and right_role read the amount from a specific role's contribution, so an invoice's carrier_rate can be checked against the rate confirmation's carrier_rate even though both use the same field. Each group carries a state — provisional, conflict, incomplete, or ready — with the failing rule spelled out in its state detail.
On the Products list, a group-grain product's Records column counts its groups across states (a dash until the first group forms — never a fabricated zero), and per-corpus chips show how many distinct documents each contributing Spec has supplied. Opening the product lists its groups, filterable by state and corpus and searchable by key, each group showing its document count and which corpora contributed. For history that predates the binding, a Talonic administrator can run a backfill over completed pipelines — dry-run by default, scoped to one tenant and product — in plain contribute mode or in rematch mode, which re-runs matching against current reference data before contributing.
Governed read contracts
A cross-pipeline consumer — an App making decisions over the product, an external report — should not read whatever happens to be in the table today. A governed read contract freezes the product's published surface: the served fields with their types, nullability, and sensitivity labels, the record identity, the serve path, and a contract version tied to a schema hash. Fields hidden by suppress_output or demoted by a resolution policy (include: false) are excluded from the contract surface, so a consumer sees only what the product deliberately serves. Publishing a new contract supersedes the old one, and consumers can pin to the version they were built against.
The contract declares one record identity, in one of two ways: record_key_fields names 1-8 served fields that jointly identify a record, or record_key_anchored: true says the record set's own record ID is the identity (an anchored identity is never a join key). Declaring both is rejected with a 400 — a contract has one record identity. A schema whose fields already carry a record_key, business_key, or unique tag publishes with no body at all. When the product cannot be published — no served fields, an unconfigured or non-unique record key — the publish returns a 422 with a machine-readable code (for example source_record_key_unconfigured or source_record_key_not_unique) telling you what to fix.
# Publish (or re-publish) the contract, declaring the record identity:
curl -X POST https://api.talonic.com/v1/data-products/PRODUCT_UUID/read-contract/publish \
-H "Authorization: Bearer $TALONIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "record_key_fields": ["order_number"] }'
# Declaring record_key_fields AND record_key_anchored is a 400:
# "…a contract has one record identity."
# Read the active contract:
curl -s https://api.talonic.com/v1/data-products/PRODUCT_UUID/read-contract \
-H "Authorization: Bearer $TALONIC_API_KEY"
# -> { "contractVersion": "…", "schemaHash": "…",
# "fields": [ { "fieldId": "…", "name": "order_number", "type": "string",
# "nullable": false, "sensitivity": "internal" } ],
# "recordKeyFieldIds": ["…"], "recordKeyAnchored": false,
# "publishedAt": "2026-08-20T09:00:00.000Z", "supersededAt": null }
# 404 (no active contract) tells you to publish one first.