@talonic/docs 0.20.31 → 0.20.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/content.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/content/helpers.ts
2
- function deriveBreadcrumbs(sections62, leafId, domain) {
2
+ function deriveBreadcrumbs(sections66, leafId, domain) {
3
3
  const domainLabels = {
4
4
  api: "API Reference",
5
5
  platform: "Platform Guide",
@@ -10,20 +10,20 @@ function deriveBreadcrumbs(sections62, leafId, domain) {
10
10
  label: domainLabels[domain] ?? domain,
11
11
  slug: domain
12
12
  };
13
- for (const group of sections62) {
13
+ for (const group of sections66) {
14
14
  const child = group.children?.find((c) => c.id === leafId);
15
15
  if (child) {
16
16
  return [root, { label: group.label, slug: group.id }, { label: child.label, slug: child.id }];
17
17
  }
18
18
  }
19
- const topLevel = sections62.find((s) => s.id === leafId);
19
+ const topLevel = sections66.find((s) => s.id === leafId);
20
20
  if (topLevel) {
21
21
  return [root, { label: topLevel.label, slug: topLevel.id }];
22
22
  }
23
23
  return [root];
24
24
  }
25
- function derivePrevNext(sections62, leafId) {
26
- const flat = sections62.flatMap(
25
+ function derivePrevNext(sections66, leafId) {
26
+ const flat = sections66.flatMap(
27
27
  (s) => s.children ?? [{ id: s.id, label: s.label }]
28
28
  );
29
29
  const idx = flat.findIndex((c) => c.id === leafId);
@@ -53,14 +53,18 @@ var API_NAV_SECTIONS = [
53
53
  { id: "documents", label: "Documents", children: [
54
54
  { id: "list-documents", label: "List Documents" },
55
55
  { id: "get-document", label: "Get Document" },
56
+ { id: "get-document-fields", label: "Get Document Fields" },
57
+ { id: "get-document-lineage", label: "Get Document Lineage" },
56
58
  { id: "delete-document", label: "Delete Document" },
57
- { id: "convert-document", label: "Convert to Markdown" }
59
+ { id: "convert-document", label: "Convert to Markdown" },
60
+ { id: "inject-customer-metadata", label: "Inject Metadata" }
58
61
  ] },
59
62
  { id: "extractions", label: "Extractions", children: [
60
63
  { id: "list-extractions", label: "List Extractions" },
61
64
  { id: "get-extraction", label: "Get Extraction" },
62
65
  { id: "get-extraction-fields", label: "Get Extraction Data" },
63
- { id: "get-extraction-markdown", label: "Correct Fields" }
66
+ { id: "get-extraction-markdown", label: "Correct Fields" },
67
+ { id: "correct-extraction", label: "Correct Extraction" }
64
68
  ] },
65
69
  { id: "schemas", label: "Schemas", children: [
66
70
  { id: "list-schemas", label: "List Schemas" },
@@ -78,7 +82,8 @@ var API_NAV_SECTIONS = [
78
82
  { id: "list-sources", label: "List Inputs" },
79
83
  { id: "create-source", label: "Create Input" },
80
84
  { id: "get-source", label: "Get / Update / Delete" },
81
- { id: "update-source", label: "Source Documents" }
85
+ { id: "update-source", label: "Source Documents" },
86
+ { id: "ingest-source-document", label: "Ingest a Document" }
82
87
  ] },
83
88
  { id: "filter-search", label: "Filter & Search", children: [
84
89
  { id: "field-autocomplete", label: "Field Autocomplete" },
@@ -88,7 +93,8 @@ var API_NAV_SECTIONS = [
88
93
  { id: "omnisearch", label: "Saved Filters" },
89
94
  { id: "saved-filters", label: "Document Counts" },
90
95
  { id: "document-counts", label: "Materialize" },
91
- { id: "materialized-index", label: "Materialized Index" }
96
+ { id: "materialized-index", label: "Materialized Index" },
97
+ { id: "search-endpoints", label: "Search Endpoints" }
92
98
  ] },
93
99
  { id: "webhooks", label: "Webhooks", children: [
94
100
  { id: "webhook-events", label: "Events" },
@@ -97,7 +103,8 @@ var API_NAV_SECTIONS = [
97
103
  { id: "webhook-retry", label: "Retry Policy" },
98
104
  { id: "list-webhook-configs", label: "List Configs" },
99
105
  { id: "create-webhook-config", label: "Create Config" },
100
- { id: "manage-webhook-config", label: "Manage Config" }
106
+ { id: "manage-webhook-config", label: "Manage Config" },
107
+ { id: "webhook-reference-endpoints", label: "Reference Endpoints" }
101
108
  ] },
102
109
  { id: "resolutions", label: "Resolutions", children: [
103
110
  { id: "list-resolutions", label: "List Resolutions" },
@@ -195,6 +202,7 @@ var API_NAV_SECTIONS = [
195
202
  { id: "manage-delivery-binding", label: "Manage Binding" },
196
203
  { id: "delivery-history", label: "History" },
197
204
  { id: "delivery-dlq", label: "Dead Letter Queue" },
205
+ { id: "delivery-pending-events", label: "Pending & Events" },
198
206
  { id: "delivery-catalog", label: "Catalog" }
199
207
  ] },
200
208
  { id: "batches", label: "Batches", children: [
@@ -207,6 +215,7 @@ var API_NAV_SECTIONS = [
207
215
  { id: "list-cases", label: "List Cases" },
208
216
  { id: "get-case", label: "Get Case" },
209
217
  { id: "case-anomalies", label: "Anomalies" },
218
+ { id: "case-evidence", label: "Evidence" },
210
219
  { id: "case-operations", label: "Operations" },
211
220
  { id: "case-edges", label: "Edges" },
212
221
  { id: "case-documents", label: "Documents" },
@@ -233,8 +242,7 @@ var API_NAV_SECTIONS = [
233
242
  { id: "routing-rules", label: "Routing Rules", children: [
234
243
  { id: "list-routing-rules", label: "List Rules" },
235
244
  { id: "create-routing-rule", label: "Create Rule" },
236
- { id: "manage-routing-rule", label: "Manage Rule" },
237
- { id: "reorder-routing-rules", label: "Reorder" }
245
+ { id: "manage-routing-rule", label: "Manage Rule" }
238
246
  ] },
239
247
  { id: "reference-data", label: "Reference Data", children: [
240
248
  { id: "list-reference-data", label: "List & Create" },
@@ -252,13 +260,23 @@ var API_NAV_SECTIONS = [
252
260
  { id: "get-field", label: "Get Field" }
253
261
  ] },
254
262
  { id: "registry", label: "Registry", children: [
255
- { id: "registry-query", label: "Query" }
263
+ { id: "registry-query", label: "Query" },
264
+ { id: "registry-health", label: "Health" }
265
+ ] },
266
+ { id: "customer-ontologies", label: "Customer Ontologies", children: [
267
+ { id: "customer-ontologies-overview", label: "Overview" },
268
+ { id: "import-customer-ontology", label: "Import" },
269
+ { id: "get-customer-ontology", label: "Get & Versions" },
270
+ { id: "publish-customer-ontology", label: "Update & Publish" },
271
+ { id: "customer-ontology-mappings", label: "Map to Talonic Types" }
256
272
  ] },
257
273
  { id: "data-products", label: "Data Products", children: [
258
274
  { id: "list-data-products", label: "List Data Products" },
275
+ { id: "create-data-product", label: "Create Data Product" },
259
276
  { id: "get-data-product", label: "Get Data Product" },
260
277
  { id: "delete-data-product", label: "Delete Data Product" },
261
278
  { id: "get-data-product-results", label: "Get Results" },
279
+ { id: "export-data-product", label: "Export Data Product" },
262
280
  { id: "data-product-share", label: "Data Product Share Link" }
263
281
  ] },
264
282
  { id: "data-policies", label: "Data Policies", children: [
@@ -281,12 +299,25 @@ var API_NAV_SECTIONS = [
281
299
  { id: "billing", label: "Billing", children: [
282
300
  { id: "billing-settings", label: "Settings" },
283
301
  { id: "billing-topup", label: "Auto Top-Up" },
302
+ { id: "billing-upgrade-link", label: "Upgrade Link" },
284
303
  { id: "cost-headers", label: "Cost Headers" }
285
304
  ] },
286
305
  { id: "usage", label: "Usage", children: [
287
306
  { id: "usage-overview", label: "Overview" },
288
307
  { id: "usage-credits", label: "Credit Usage" }
289
308
  ] },
309
+ { id: "account", label: "Account", children: [
310
+ { id: "get-account", label: "Account Snapshot" },
311
+ { id: "account-keys", label: "Manage API Keys" }
312
+ ] },
313
+ { id: "nodes", label: "Node Jobs", children: [
314
+ { id: "node-jobs", label: "Node Jobs" }
315
+ ] },
316
+ { id: "process", label: "Process", children: [
317
+ { id: "submit-process", label: "Submit for Processing" },
318
+ { id: "get-run", label: "Get Run" },
319
+ { id: "list-configs", label: "Processing Configs" }
320
+ ] },
290
321
  { id: "pricing", label: "Pricing", children: [
291
322
  { id: "pricing-overview", label: "Pricing Catalog" }
292
323
  ] },
@@ -314,7 +345,9 @@ var API_NAV_SECTIONS = [
314
345
  { id: "resolve-field-review", label: "Resolve Field Review" },
315
346
  { id: "bulk-resolve-field-reviews", label: "Bulk Resolve Field Reviews" },
316
347
  { id: "list-field-review-decisions", label: "Decision Log" },
317
- { id: "export-field-review-decisions", label: "Export Decision Log" }
348
+ { id: "export-field-review-decisions", label: "Export Decision Log" },
349
+ { id: "field-review-triage", label: "Triage Breakdown" },
350
+ { id: "field-review-value-clusters", label: "Value Clusters" }
318
351
  ] },
319
352
  { id: "spec-config", label: "Spec Config", children: [
320
353
  { id: "get-spec-rail", label: "Get Rail" },
@@ -1135,6 +1168,11 @@ var sections2 = [
1135
1168
  name: "Delivery",
1136
1169
  type: "read",
1137
1170
  description: "Check delivery status and preview binding output."
1171
+ },
1172
+ {
1173
+ name: "Spec Architect",
1174
+ type: "read",
1175
+ description: "Look up the Spec config catalog, estimate review volume before a Spec runs, and back-solve the review rate over a completed run."
1138
1176
  }
1139
1177
  ]
1140
1178
  },
@@ -1167,6 +1205,24 @@ var sections2 = [
1167
1205
  language: "json",
1168
1206
  title: "Response",
1169
1207
  code: '{\n "data": [\n {\n "id": "doc_7f3a1b2c",\n "filename": "invoice_2026_0472.pdf",\n "document_type": "Invoice",\n "status": "completed",\n "fields_extracted": 24\n }\n ],\n "meta": { "total": 156, "cursor": "eyJpZCI6MTU2fQ" }\n}'
1208
+ },
1209
+ {
1210
+ type: "heading",
1211
+ level: 3,
1212
+ id: "agent-spec-architect",
1213
+ text: "Spec Architect"
1214
+ },
1215
+ {
1216
+ type: "paragraph",
1217
+ text: "When you ask the agent to help design or tune a Spec, it acts as a Spec Architect: it reasons over the real configuration catalog rather than from memory, and it can estimate how much human review a Spec will generate before you commit to it. This matters because the cost of a Spec is not just extraction \u2014 it is the number of cells a person has to review, and that is driven by which validation gates and triage thresholds you place in the rail. The agent grounds every suggestion in the actual primitives and their review-volume effect."
1218
+ },
1219
+ {
1220
+ type: "paragraph",
1221
+ text: "Review estimation comes in two touches. **Touch-0** (`estimate_review_volume`) is an instant, free, read-only heatmap of which fields are likely to need review, computed from data already captured at ingestion \u2014 it returns a qualitative per-field risk band (low / medium / high), not a percentage, and is the right tool for a Spec that has not run yet. **Touch-1** (`estimate_review_rate`) is the precise, warm estimate over a completed pipeline run: it replays the real triage decision over the run's real cells and validation verdicts, returns the actual review rate under a candidate triage config, plots the review-rate-vs-capacity tradeoff curve, and \u2014 given a target review rate \u2014 back-solves the capacity and risk cut that hit a sustainable review budget. Neither touch extracts or writes anything."
1222
+ },
1223
+ {
1224
+ type: "paragraph",
1225
+ text: 'These capabilities run inside the agent loop; there is no separate REST endpoint to call them directly. `GET /v1/agent/tools` lists the tools the agent can use, including the Spec Architect set, and you reach them by asking the agent in natural language \u2014 for example, "Estimate the review rate if I cap review capacity at three cells per document" or "Which fields will need the most review on this Spec before I run it?".'
1170
1226
  }
1171
1227
  ],
1172
1228
  related: [
@@ -2434,6 +2490,26 @@ var sections4 = [
2434
2490
  language: "bash",
2435
2491
  title: "Get registry aggregate stats",
2436
2492
  code: 'curl https://api.talonic.com/v1/schemas/stats \\\n -H "Authorization: Bearer $TALONIC_API_KEY"'
2493
+ },
2494
+ {
2495
+ type: "heading",
2496
+ level: 3,
2497
+ id: "registry-health-monitoring",
2498
+ text: "Monitoring Registry Health"
2499
+ },
2500
+ {
2501
+ type: "paragraph",
2502
+ text: "A richer health snapshot is available over the public API at `GET /v1/registry/health`. It returns tier and admission counts, the singleton and weak-name rates, occurrence and recent-resolution stats, the deterministic transfer hit rate (the share of cells filled with no AI call), and coherence and atomicity metrics. This is the same view the in-app registry dashboard reads, and it is read-only: it exposes no lifecycle or cleanup actions. Teams poll it to track how the knowledge graph matures \u2014 a rising transfer hit rate means more extraction resolves for free, while a rising singleton or weak-name rate is an early signal that naming or clustering needs attention."
2503
+ },
2504
+ {
2505
+ type: "heading",
2506
+ level: 3,
2507
+ id: "customer-ontology-overlay",
2508
+ text: "The Customer Ontology Overlay"
2509
+ },
2510
+ {
2511
+ type: "paragraph",
2512
+ text: "The registry learns field names bottom-up from your documents, but you can also seed it top-down with a **customer ontology** \u2014 a versioned overlay of your own document types and field concepts that augments the built-in Talonic ontology. You author the doctypes and fields specific to your domain, anchor each doctype to a Talonic type, and publish. Publishing projects your authored fields into the registry as pinned canonical rows (protected from cleanup) and adds your custom doctypes to the classifier on a customer axis that runs alongside the always-on Talonic axis. The overlay is additive: an empty or unpublished ontology leaves capture and classification identical to the Talonic-only path, so adopting it never regresses existing extractions. The full lifecycle \u2014 create, import, update, publish, unpublish, delete, and embedding-assisted mapping to Talonic types \u2014 is available over the API under `/v1/customer-ontologies`."
2437
2513
  }
2438
2514
  ],
2439
2515
  related: [
@@ -6715,6 +6791,10 @@ curl -X POST https://api.talonic.com/v1/field-reviews/frev_001/override \\
6715
6791
  type: "paragraph",
6716
6792
  text: "The field-review queue is the human checkpoint in your pipeline. It ensures that fields a blocking checkpoint could not clear receive a person's decision before they flow downstream into the data product. Because routing is field-level, reviewers see only the specific fields that failed rather than whole records, which keeps the queue focused. The Decision log preserves the full history of who decided what, satisfying audit requirements without a separate tracking system."
6717
6793
  },
6794
+ {
6795
+ type: "paragraph",
6796
+ text: "Two endpoints help a reviewer work the queue efficiently at scale. `GET /v1/field-reviews/triage` returns the queue's shape rather than its items \u2014 a classification-backed breakdown by reason, field, document, assignee, and tag, each group carrying a per-group reason mix \u2014 so a dashboard can show which fields are mostly safe to bulk-approve versus which need a careful look. `GET /v1/field-reviews/value-clusters` groups the held items by their current value: when a field is held across many documents with the same value, those items form one cluster you can review once and approve together with `POST /v1/field-reviews/bulk-resolve`. Together they turn a long flat queue into a prioritized, batch-resolvable worklist."
6797
+ },
6718
6798
  {
6719
6799
  type: "callout",
6720
6800
  text: "The legacy `POST /v1/review/:id/approve` and `/reject` endpoints and the `result.approved` / `result.rejected` signal model are superseded. The central field-review queue uses `/v1/field-reviews` with approve / correct / override. If you must call the older review surface, the correct shape is `POST /v1/review/:id/action` with an `{ action }` body."
@@ -9796,6 +9876,10 @@ var sections17 = [
9796
9876
  type: "paragraph",
9797
9877
  text: "The engine runs four kinds of per-document phase in sequence. **Transfer** fills cells deterministically from the Field Registry, binding known values with no AI call. **Extraction** runs Claude over the gap fields that transfer could not fill. **Resolution** applies your Data Policies to normalize and transform values. **Validation** checks the document against the gates you placed in the rail. After every document is terminal, a pipeline-scoped **Assembly** step can compose grouped documents into a single record. Phases are strictly sequential per document: phase N+1 starts only once phase N completes."
9798
9878
  },
9879
+ {
9880
+ type: "paragraph",
9881
+ text: "Resolution and Validation can also run **after** Assembly, not only per document. When a Data Policy or a validation checkpoint sits after the Assembly node in the rail, it becomes a pipeline-scoped phase that runs once over the composed record. A **post-assembly resolution** normalizes the assembled row and mirrors the resolved values back onto each anchor document, so the data product ships the resolved value rather than the raw composed one. A **post-assembly validation** gate then checks the full composed row. The two run in that order \u2014 resolution before validation \u2014 so a gate placed after Assembly sees resolved composed values. Both are best-effort tail steps and do not appear in the per-document progress phases."
9882
+ },
9799
9883
  {
9800
9884
  type: "callout",
9801
9885
  variant: "info",
@@ -11443,7 +11527,7 @@ var sections20 = [
11443
11527
  },
11444
11528
  { type: "paragraph", text: "Most integrations call this endpoint after receiving an `extraction.complete` webhook or after polling a document's status until it reaches `completed`. A typical workflow is to extract a document via `POST /v1/extract`, store the returned `document.id`, then fetch full metadata here when needed." },
11445
11529
  { type: "paragraph", text: "The response includes the current `status` field which will be `completed` when extraction has finished, `processing` while in progress, or `error` if something went wrong. Use the `latest_extraction_id` to navigate directly to the extraction result via `GET /v1/extractions/:id`." },
11446
- { type: "paragraph", text: "Pair this with `GET /v1/documents/:id/markdown` to retrieve the raw OCR text, or with `GET /v1/extractions/:id/data` for just the structured field values. Note that the `triage` object is only populated after ingestion completes and may be `null` for documents still in processing." },
11530
+ { type: "paragraph", text: "Pair this with `GET /v1/documents/:id/markdown` to retrieve the raw OCR text, or with `GET /v1/extractions/:id/data` for just the structured field values. To list every extraction a document has, call `GET /v1/documents/:id/extractions`. Note that the `triage` object is only populated after ingestion completes and may be `null` for documents still in processing." },
11447
11531
  { type: "callout", variant: "info", text: "The `links.dashboard` URL opens the document directly in the Talonic platform UI, which is useful for sharing with team members who need to review or correct extractions." },
11448
11532
  { type: "heading", level: 2, id: "get-document-errors", text: "Errors" },
11449
11533
  {
@@ -11609,6 +11693,245 @@ var sections20 = [
11609
11693
  { question: "How do I disable vision OCR during conversion?", answer: "Send a `vision` form field set to the string `false`. Scanned pages and images will then rely on the native text layer only, and the `vision_pages` array in the response will be empty." }
11610
11694
  ],
11611
11695
  mentions: ["convert", "markdown", "multipart upload", "vision OCR", "stateless conversion"]
11696
+ },
11697
+ {
11698
+ slug: "get-document-fields",
11699
+ parentSlug: "documents",
11700
+ title: "Get Document Fields",
11701
+ seoTitle: "Get Document Fields Endpoint \u2014 Talonic Docs",
11702
+ description: "Return a document's captured fields bound into the field registry \u2014 one row per occurrence with its canonical concept, tier, value, and confidence.",
11703
+ content: [
11704
+ { type: "paragraph", text: "Returns the fields captured from a document and bound into the field registry. Where the extraction endpoints give you the raw field-name/value pairs a document produced, this endpoint gives you the registry-resolved view: each captured value is reported alongside the canonical concept it bound to, that concept's tier, and the confidence of the binding. It is the structured result most integrations actually want per document." },
11705
+ { type: "paragraph", text: "Each row carries the occurrence value and confidence, the resolved registry field (its id, canonical name, display name, and data type), the registry tier (1 = canonical, 2 = provisional, 3 = candidate), and the semantic cluster the field belongs to when one is assigned. Rows are ordered by tier, so the most established concepts come first." },
11706
+ { type: "callout", variant: "info", text: "A **linked duplicate** transparently returns the canonical document's fields. When a re-ingested file is byte-identical to one already in your workspace, it becomes a thin link to the canonical, and reads dereference to it \u2014 so you always see the captured data without re-extraction." },
11707
+ {
11708
+ type: "endpoint",
11709
+ method: "GET",
11710
+ path: "/v1/documents/{id}/fields",
11711
+ summary: "List a document's captured fields bound into the field registry.",
11712
+ description: "Requires read scope. A linked duplicate returns the canonical document's fields.",
11713
+ blocks: [
11714
+ {
11715
+ type: "param-table",
11716
+ title: "Path parameters",
11717
+ params: [
11718
+ { name: "id", type: "string", required: true, description: "The document UUID." }
11719
+ ]
11720
+ }
11721
+ ]
11722
+ },
11723
+ { type: "heading", level: 2, id: "get-document-fields-response", text: "Response" },
11724
+ {
11725
+ type: "param-table",
11726
+ title: "Response fields",
11727
+ params: [
11728
+ { name: "document_id", type: "string", description: "The document UUID the fields belong to." },
11729
+ { name: "data[].field_id", type: "string", description: "Registry field UUID the value bound to." },
11730
+ { name: "data[].canonical_name", type: "string", description: "Canonical name of the registry concept." },
11731
+ { name: "data[].display_name", type: "string | null", description: "Human-readable display name, when set." },
11732
+ { name: "data[].cluster_name", type: "string | null", description: "Semantic cluster the field belongs to, when assigned." },
11733
+ { name: "data[].data_type", type: "string | null", description: "Declared data type of the concept." },
11734
+ { name: "data[].tier", type: "integer", description: "Registry tier: 1 canonical, 2 provisional, 3 candidate." },
11735
+ { name: "data[].value", type: "any", description: "The captured value for this occurrence." },
11736
+ { name: "data[].confidence", type: "number | null", description: "Confidence of the binding (0\u20131)." }
11737
+ ]
11738
+ },
11739
+ {
11740
+ type: "code",
11741
+ title: "curl",
11742
+ language: "bash",
11743
+ code: `curl -s https://api.talonic.ai/v1/documents/7f3a1b2c-0000-0000-0000-000000000000/fields \\
11744
+ -H "Authorization: Bearer tlnc_your_api_key"`
11745
+ },
11746
+ {
11747
+ type: "code",
11748
+ title: "Response",
11749
+ code: `{
11750
+ "document_id": "7f3a1b2c-0000-0000-0000-000000000000",
11751
+ "data": [
11752
+ {
11753
+ "field_id": "b2c3d4e5-0000-0000-0000-000000000000",
11754
+ "canonical_name": "invoice_total",
11755
+ "display_name": "Invoice Total",
11756
+ "cluster_name": "amounts",
11757
+ "data_type": "number",
11758
+ "tier": 1,
11759
+ "value": "490.00",
11760
+ "confidence": 0.97
11761
+ }
11762
+ ]
11763
+ }`
11764
+ }
11765
+ ],
11766
+ related: [
11767
+ { label: "Get Document", slug: "get-document" },
11768
+ { label: "Get Document Lineage", slug: "get-document-lineage" },
11769
+ { label: "List Extractions", slug: "list-extractions" }
11770
+ ],
11771
+ faq: [
11772
+ { question: "How is this different from the extraction data endpoint?", answer: "The extraction endpoints return the raw field-name/value pairs a document produced. This endpoint returns the registry-resolved view: each value reported with the canonical concept it bound to, that concept's tier, and the binding confidence." },
11773
+ { question: "What does tier mean?", answer: "Tier is the registry maturity of the concept a value bound to: 1 is a canonical (proven) concept, 2 is provisional, 3 is a candidate. Rows are ordered by tier so the most established concepts appear first." },
11774
+ { question: "Why does a duplicate document return fields it never extracted?", answer: "A byte-identical re-ingest becomes a thin link to the canonical document and reads its captured data through. The link carries no occurrences of its own, so this endpoint returns the canonical's fields." }
11775
+ ],
11776
+ mentions: ["field registry", "captured fields", "canonical concept", "tier", "linked duplicate"]
11777
+ },
11778
+ {
11779
+ slug: "get-document-lineage",
11780
+ parentSlug: "documents",
11781
+ title: "Get Document Lineage",
11782
+ seoTitle: "Get Document Lineage Endpoint \u2014 Talonic Docs",
11783
+ description: "Return a document's downstream footprint: the pipelines that consumed it, the data products built from them, the field-registry concepts it contributed, and the cases it belongs to.",
11784
+ content: [
11785
+ { type: "paragraph", text: `Returns the document's lineage \u2014 its downstream footprint across the platform. This is the "used in" view: where a document ended up after ingestion, rather than what it contains. It answers questions like "which pipelines consumed this file?", "which data products ship its values?", and "which cases does it belong to?".` },
11786
+ { type: "paragraph", text: "The response groups the footprint into sections: `pipelines` (the One Engine runs that included the document), `data_products` (datasets built from those pipelines), `field_registry` (the canonical concepts the document contributed, with per-tier counts and a sample), `cases`, and `business_cases`. Each entry carries a deep link to the corresponding public surface, so you can follow lineage outward without constructing URLs yourself." },
11787
+ { type: "callout", variant: "info", text: "Pipeline, data-product, and case membership key on the document's own id; the field-registry section keys on the effective (canonical) id, so a linked duplicate reflects the concepts it reads through. This is the document passport \u2014 new platform surfaces that reference documents are added here over time." },
11788
+ {
11789
+ type: "endpoint",
11790
+ method: "GET",
11791
+ path: "/v1/documents/{id}/lineage",
11792
+ summary: "Get a document's downstream footprint across pipelines, data products, the registry, and cases.",
11793
+ description: "Requires read scope.",
11794
+ blocks: [
11795
+ {
11796
+ type: "param-table",
11797
+ title: "Path parameters",
11798
+ params: [
11799
+ { name: "id", type: "string", required: true, description: "The document UUID." }
11800
+ ]
11801
+ }
11802
+ ]
11803
+ },
11804
+ { type: "heading", level: 2, id: "get-document-lineage-response", text: "Response" },
11805
+ {
11806
+ type: "param-table",
11807
+ title: "Response fields",
11808
+ params: [
11809
+ { name: "document_id", type: "string", description: "The document UUID." },
11810
+ { name: "pipelines[]", type: "object", description: "Pipelines that consumed the document (id, name, status, document_status, schema, links.self)." },
11811
+ { name: "data_products[]", type: "object", description: "Data products built from those pipelines (id, name, status, links.self)." },
11812
+ { name: "field_registry.total", type: "integer", description: "Distinct canonical concepts the document contributed." },
11813
+ { name: "field_registry.by_tier", type: "object[]", description: "Per-tier concept counts." },
11814
+ { name: "field_registry.sample", type: "object[]", description: "A capped sample of contributed concepts." },
11815
+ { name: "cases[]", type: "object", description: "Cases the document belongs to (id, case_key, status, links.self)." },
11816
+ { name: "business_cases[]", type: "object", description: "Business cases the document belongs to." }
11817
+ ]
11818
+ },
11819
+ {
11820
+ type: "code",
11821
+ title: "curl",
11822
+ language: "bash",
11823
+ code: `curl -s https://api.talonic.ai/v1/documents/7f3a1b2c-0000-0000-0000-000000000000/lineage \\
11824
+ -H "Authorization: Bearer tlnc_your_api_key"`
11825
+ },
11826
+ {
11827
+ type: "code",
11828
+ title: "Response",
11829
+ code: `{
11830
+ "document_id": "7f3a1b2c-0000-0000-0000-000000000000",
11831
+ "pipelines": [
11832
+ { "id": "c3d4e5f6-...", "name": "Delivery Notes", "status": "completed",
11833
+ "document_status": "completed", "schema_id": "b2c3d4e5-...",
11834
+ "schema_name": "Delivery Note", "links": { "self": "/v1/pipelines/c3d4e5f6-..." } }
11835
+ ],
11836
+ "data_products": [
11837
+ { "id": "d4e5f6a7-...", "name": "Q2 Deliveries", "status": "active",
11838
+ "links": { "self": "/v1/data-products/d4e5f6a7-..." } }
11839
+ ],
11840
+ "field_registry": {
11841
+ "total": 18,
11842
+ "by_tier": [ { "tier": 1, "count": 12 }, { "tier": 2, "count": 6 } ],
11843
+ "sample": [ { "canonical_name": "invoice_total", "display_name": "Invoice Total", "tier": 1 } ],
11844
+ "links": { "fields": "/v1/fields?document_id=7f3a1b2c-..." }
11845
+ },
11846
+ "cases": [],
11847
+ "business_cases": []
11848
+ }`
11849
+ }
11850
+ ],
11851
+ related: [
11852
+ { label: "Get Document", slug: "get-document" },
11853
+ { label: "Get Document Fields", slug: "get-document-fields" },
11854
+ { label: "List Documents", slug: "list-documents" }
11855
+ ],
11856
+ faq: [
11857
+ { question: "What is lineage useful for?", answer: "It shows a document's downstream footprint \u2014 which pipelines consumed it, which data products ship its values, which registry concepts it contributed, and which cases it belongs to. Use it to trace provenance outward from a single file." },
11858
+ { question: "Does a duplicate document show the same lineage as its canonical?", answer: "Pipeline, data-product, and case membership key on the document's own id, so a duplicate shows where its own id was used. The field-registry section keys on the canonical id, so it reflects the concepts the duplicate reads through." }
11859
+ ],
11860
+ mentions: ["lineage", "document passport", "pipelines", "data products", "provenance"]
11861
+ },
11862
+ {
11863
+ slug: "inject-customer-metadata",
11864
+ parentSlug: "documents",
11865
+ title: "Inject Metadata",
11866
+ seoTitle: "Inject Customer Metadata Endpoint \u2014 Talonic Docs",
11867
+ description: "Upload a CSV of customer-provided metadata and attach those values to matching documents by document_id or a custom reference column.",
11868
+ content: [
11869
+ { type: "paragraph", text: "Attach your own metadata to documents in bulk by uploading a CSV. Each row identifies a document and carries one or more metadata fields to write onto it. This is how you bring system-of-record identifiers and status flags \u2014 contract IDs, account numbers, lifecycle states \u2014 alongside the fields Talonic extracted, so downstream filtering, matching, and delivery can use them." },
11870
+ { type: "paragraph", text: "By default the endpoint expects a comma-delimited UTF-8 CSV with a `document_id` column; every other non-empty column is written as injected metadata on the matching document. Point at a different layout with `reference_column` (the column used to identify the document), `field_columns` (an explicit comma-separated subset of columns to inject), `delimiter` (for example `\\t` for tab-separated), and `encoding`." },
11871
+ { type: "paragraph", text: "The response summarizes the run: a `batch_id` for the injection, `rows_processed`, how many documents were `documents_matched` versus `documents_pending` (rows whose reference did not yet match a document), and the total `fields_written`. Rows that do not match are reported as pending rather than failing the whole upload." },
11872
+ { type: "callout", variant: "info", text: "Identification is exact: `document_id` matches the document UUID. Use `reference_column` to match on a different column present in your CSV, paired with a reference the documents already carry. The upload accepts files up to 200 MB." },
11873
+ {
11874
+ type: "endpoint",
11875
+ method: "POST",
11876
+ path: "/v1/inject-customer-metadata",
11877
+ summary: "Upload a CSV of metadata and attach it to matching documents.",
11878
+ description: "Requires write scope. Multipart upload.",
11879
+ blocks: [
11880
+ {
11881
+ type: "param-table",
11882
+ title: "Multipart form fields",
11883
+ params: [
11884
+ { name: "file", type: "file", required: true, description: "CSV file containing document references and metadata values. Max 200 MB." },
11885
+ { name: "reference_column", type: "string", description: "CSV column used to identify the document.", default: "document_id" },
11886
+ { name: "field_columns", type: "string", description: "Comma-separated columns to inject. Defaults to all non-reference columns." },
11887
+ { name: "delimiter", type: "string", description: "CSV delimiter. Use \\t for tab-delimited files.", default: "," },
11888
+ { name: "encoding", type: "string", description: "Buffer encoding used to read the uploaded file.", default: "utf8" }
11889
+ ]
11890
+ }
11891
+ ]
11892
+ },
11893
+ { type: "heading", level: 2, id: "inject-customer-metadata-response", text: "Response" },
11894
+ {
11895
+ type: "param-table",
11896
+ title: "Response fields",
11897
+ params: [
11898
+ { name: "batch_id", type: "string", description: "UUID identifying this injection run." },
11899
+ { name: "rows_processed", type: "integer", description: "Number of CSV rows read." },
11900
+ { name: "documents_matched", type: "integer", description: "Rows whose reference matched a document." },
11901
+ { name: "documents_pending", type: "integer", description: "Rows whose reference did not match a document yet." },
11902
+ { name: "fields_written", type: "integer", description: "Total metadata fields written across matched documents." }
11903
+ ]
11904
+ },
11905
+ {
11906
+ type: "code",
11907
+ title: "curl",
11908
+ language: "bash",
11909
+ code: `curl -s -X POST https://api.talonic.ai/v1/inject-customer-metadata \\
11910
+ -H "Authorization: Bearer tlnc_your_api_key" \\
11911
+ -F "file=@contract-ids.csv"`
11912
+ },
11913
+ {
11914
+ type: "code",
11915
+ title: "Response",
11916
+ code: `{
11917
+ "batch_id": "b0c1d2e3-f4a5-6789-abcd-ef0123456789",
11918
+ "rows_processed": 30,
11919
+ "documents_matched": 30,
11920
+ "documents_pending": 0,
11921
+ "fields_written": 30
11922
+ }`
11923
+ }
11924
+ ],
11925
+ related: [
11926
+ { label: "List Documents", slug: "list-documents" },
11927
+ { label: "Get Document Fields", slug: "get-document-fields" }
11928
+ ],
11929
+ faq: [
11930
+ { question: "How are CSV rows matched to documents?", answer: "By default each row's document_id column is matched to a document UUID. Set reference_column to match on a different column in your CSV against a reference the documents already carry." },
11931
+ { question: "What happens to rows that do not match a document?", answer: "They are counted as documents_pending in the response rather than failing the upload. The rest of the batch still applies." },
11932
+ { question: "Which columns get written?", answer: "By default every non-empty column other than the reference column. Pass field_columns with a comma-separated list to inject only a specific subset." }
11933
+ ],
11934
+ mentions: ["metadata injection", "CSV upload", "document_id", "customer metadata", "bulk"]
11612
11935
  }
11613
11936
  ];
11614
11937
 
@@ -11997,6 +12320,44 @@ var sections21 = [
11997
12320
  { question: "What happens to the confidence score of a corrected field?", answer: "Corrected fields are automatically set to confidence 1.0 and added to the `locked_fields` array. The overall confidence score is recalculated to reflect the correction." }
11998
12321
  ],
11999
12322
  mentions: ["field corrections", "propagation", "locked fields", "confidence override"]
12323
+ },
12324
+ {
12325
+ slug: "correct-extraction",
12326
+ parentSlug: "extractions",
12327
+ title: "Correct Extraction",
12328
+ seoTitle: "Correct Extraction Endpoint \u2014 Talonic Docs",
12329
+ description: "Override extracted field values on an extraction. Each corrected field is set to full confidence and locked.",
12330
+ content: [
12331
+ { type: "paragraph", text: "The correct endpoint overrides field values on an extraction. Pass a flat map of field name to corrected value; each field you set is written at confidence 1.0 and locked, so it is treated as ground truth and not re-derived. Use it to fix a misread value programmatically without re-running extraction." },
12332
+ {
12333
+ type: "endpoint",
12334
+ method: "POST",
12335
+ path: "/v1/extractions/:id/correct",
12336
+ summary: "Override extracted field values (each set to confidence 1.0 and locked).",
12337
+ description: "Requires write scope. Body is a flat map of field name to corrected value.",
12338
+ blocks: [
12339
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "The extraction UUID." }] }
12340
+ ]
12341
+ },
12342
+ {
12343
+ type: "code",
12344
+ title: "curl",
12345
+ language: "bash",
12346
+ code: `curl -s -X POST https://api.talonic.ai/v1/extractions/EXTRACTION_ID/correct \\
12347
+ -H "Authorization: Bearer tlnc_your_api_key" \\
12348
+ -H "Content-Type: application/json" \\
12349
+ -d '{ "invoice_total": "1240.00" }'`
12350
+ }
12351
+ ],
12352
+ related: [
12353
+ { label: "Get Extraction", slug: "get-extraction" },
12354
+ { label: "Get Extraction Data", slug: "get-extraction-fields" }
12355
+ ],
12356
+ faq: [
12357
+ { question: "What happens to a corrected field?", answer: "It is written at confidence 1.0 and locked, so it is treated as ground truth and not overwritten by a later re-derivation." },
12358
+ { question: "How is this different from PATCH /v1/extractions/{id}/data?", answer: "Both set field values. Correct is the explicit correction path that locks each field at full confidence; use it when you are asserting a known-good value." }
12359
+ ],
12360
+ mentions: ["correct extraction", "override", "locked", "confidence", "ground truth"]
12000
12361
  }
12001
12362
  ];
12002
12363
 
@@ -13413,6 +13774,55 @@ var sections24 = [
13413
13774
  { question: "What file formats are supported for document ingestion?", answer: "Talonic supports 25+ formats including PDF, DOCX, XLSX, CSV, PPTX, MSG, EML, PNG, JPG, HTML, XML, JSON, and more. See the supported file types documentation for the full list." }
13414
13775
  ],
13415
13776
  mentions: ["document ingestion", "batch processing", "duplicate detection", "file upload"]
13777
+ },
13778
+ {
13779
+ slug: "ingest-source-document",
13780
+ parentSlug: "sources",
13781
+ title: "Ingest a Document",
13782
+ seoTitle: "Ingest Source Document \u2014 Talonic Docs",
13783
+ description: "Upload a single document into a source for extraction. Alias of POST /v1/sources/{id}/documents.",
13784
+ content: [
13785
+ { type: "paragraph", text: "The ingest endpoint uploads one document (up to 500 MB) into a source and queues it for extraction. It is an alias of POST /v1/sources/{id}/documents, provided as a clearer verb for one-off ingestion. Pass the file as multipart form data; optionally set processing_mode to batch for deferred, lower-cost extraction (otherwise realtime)." },
13786
+ {
13787
+ type: "endpoint",
13788
+ method: "POST",
13789
+ path: "/v1/sources/:id/ingest",
13790
+ summary: "Upload a document into a source for extraction.",
13791
+ description: "Requires the extract scope. Multipart upload.",
13792
+ blocks: [
13793
+ {
13794
+ type: "param-table",
13795
+ title: "Parameters",
13796
+ params: [
13797
+ { name: "id", type: "string", required: true, description: "The source id (path)." },
13798
+ { name: "file", type: "file", required: true, description: "The document to ingest (multipart, max 500 MB)." },
13799
+ { name: "processing_mode", type: "string", description: "batch or realtime. Batch defers extraction at lower cost." }
13800
+ ]
13801
+ }
13802
+ ]
13803
+ },
13804
+ { type: "heading", level: 2, id: "ingest-source-document-response", text: "Response" },
13805
+ {
13806
+ type: "param-table",
13807
+ title: "Response fields",
13808
+ params: [
13809
+ { name: "document_id", type: "string", description: "The created document UUID." },
13810
+ { name: "filename", type: "string", description: "The uploaded filename." },
13811
+ { name: "status", type: "string", description: "queued." },
13812
+ { name: "processing_mode", type: "string", description: "The mode the document will be processed in." },
13813
+ { name: "source_id", type: "string", description: "The source it was ingested into." }
13814
+ ]
13815
+ }
13816
+ ],
13817
+ related: [
13818
+ { label: "Source Documents", slug: "update-source" },
13819
+ { label: "List Inputs", slug: "list-sources" }
13820
+ ],
13821
+ faq: [
13822
+ { question: "How is ingest different from POST /v1/sources/{id}/documents?", answer: "They are the same operation. Ingest is an alias with a clearer verb for submitting a single document." },
13823
+ { question: "What does batch processing_mode do?", answer: "It defers extraction through the provider batch APIs at roughly half the cost, delivered within 48 hours, instead of realtime processing." }
13824
+ ],
13825
+ mentions: ["ingest", "source", "upload", "processing mode", "batch"]
13416
13826
  }
13417
13827
  ];
13418
13828
 
@@ -13430,7 +13840,7 @@ var sections25 = [
13430
13840
  {
13431
13841
  type: "endpoint",
13432
13842
  method: "GET",
13433
- path: "/fields/autocomplete",
13843
+ path: "/v1/search/autocomplete",
13434
13844
  summary: "Autocomplete field names from the registry.",
13435
13845
  description: "Returns matching fields ranked by relevance and occurrence count. Use this to power field picker UIs.",
13436
13846
  blocks: [
@@ -13511,7 +13921,7 @@ var sections25 = [
13511
13921
  {
13512
13922
  type: "endpoint",
13513
13923
  method: "GET",
13514
- path: "/fields/:fieldId/values",
13924
+ path: "/v1/search/field-values",
13515
13925
  summary: "List distinct values for a field across documents.",
13516
13926
  description: "Returns value distribution with counts. Useful for building filter dropdowns and faceted search.",
13517
13927
  blocks: [
@@ -13519,6 +13929,7 @@ var sections25 = [
13519
13929
  type: "param-table",
13520
13930
  title: "Query parameters",
13521
13931
  params: [
13932
+ { name: "field", type: "string", required: true, description: "The field registry UUID to read values for (resolve it via field autocomplete)." },
13522
13933
  { name: "sourceId", type: "string", description: "Scope to a specific source connection." },
13523
13934
  { name: "q", type: "string", description: "Filter values by substring match." },
13524
13935
  { name: "limit", type: "integer", default: "20", description: "Maximum number of values to return." }
@@ -13558,7 +13969,7 @@ var sections25 = [
13558
13969
  { name: "429", type: "rate_limited", description: "Too many requests. Retry after the period indicated in the Retry-After header." }
13559
13970
  ]
13560
13971
  },
13561
- { type: "paragraph", text: 'Pair this endpoint with the **field autocomplete** endpoint to build a two-step filter UI: first let the user select a field via `GET /fields/autocomplete`, then populate a dropdown with that field\'s distinct values from this endpoint. The `totalDistinct` count is useful for showing "N of M values" pagination hints.' }
13972
+ { type: "paragraph", text: 'Pair this endpoint with the **field autocomplete** endpoint to build a two-step filter UI: first let the user select a field via `GET /v1/search/autocomplete`, then populate a dropdown with that field\'s distinct values from this endpoint. The `totalDistinct` count is useful for showing "N of M values" pagination hints.' }
13562
13973
  ],
13563
13974
  related: [
13564
13975
  { label: "Field Autocomplete", slug: "field-autocomplete" },
@@ -13661,7 +14072,7 @@ var sections25 = [
13661
14072
  { name: "429", type: "rate_limited", description: "Too many requests. Retry after the period indicated in the Retry-After header." }
13662
14073
  ]
13663
14074
  },
13664
- { type: "paragraph", text: "Build conditions programmatically by first calling `GET /fields/autocomplete` to resolve field IDs, then `GET /fields/:fieldId/values` to populate value pickers. The response includes the full `fieldValues` map per document, so you can render result tables without additional per-document fetches." }
14075
+ { type: "paragraph", text: "Build conditions programmatically by first calling `GET /v1/search/autocomplete` to resolve field IDs, then `GET /v1/search/field-values` to populate value pickers. The response includes the full `fieldValues` map per document, so you can render result tables without additional per-document fetches." }
13665
14076
  ],
13666
14077
  related: [
13667
14078
  { label: "Field Autocomplete", slug: "field-autocomplete" },
@@ -13769,7 +14180,7 @@ var sections25 = [
13769
14180
  {
13770
14181
  type: "endpoint",
13771
14182
  method: "GET",
13772
- path: "/saved-filters",
14183
+ path: "/v1/search/saved-filters",
13773
14184
  summary: "List saved filters.",
13774
14185
  description: "Returns all saved filter configurations, optionally scoped to a source.",
13775
14186
  blocks: [
@@ -13799,7 +14210,7 @@ var sections25 = [
13799
14210
  },
13800
14211
  {
13801
14212
  type: "code",
13802
- title: "Response (GET /saved-filters)",
14213
+ title: "Response (GET /v1/search/saved-filters)",
13803
14214
  code: `{
13804
14215
  "data": [
13805
14216
  {
@@ -13819,7 +14230,7 @@ var sections25 = [
13819
14230
  {
13820
14231
  type: "endpoint",
13821
14232
  method: "POST",
13822
- path: "/saved-filters",
14233
+ path: "/v1/search/saved-filters",
13823
14234
  summary: "Create a saved filter.",
13824
14235
  description: "Persist a filter configuration for reuse.",
13825
14236
  blocks: [
@@ -13995,6 +14406,58 @@ var sections25 = [
13995
14406
  { question: "Does the materialized index affect omnisearch results?", answer: "Yes. Omnisearch queries field values from the same materialized index. An out-of-date index may cause missing results in both filter and omnisearch endpoints." }
13996
14407
  ],
13997
14408
  mentions: ["materialized index", "filter performance"]
14409
+ },
14410
+ {
14411
+ slug: "search-endpoints",
14412
+ parentSlug: "filter-search",
14413
+ title: "Search Endpoints",
14414
+ seoTitle: "Search Endpoints \u2014 Talonic Docs",
14415
+ description: "The search namespace: a filter alias, the omnisearch POST, and the materialize trigger that rebuilds the field-value index.",
14416
+ content: [
14417
+ { type: "paragraph", text: "The `/v1/search` namespace gathers the document filter, omnisearch, and the materialization trigger under one prefix. The filter route is an alias of the document filter, omnisearch runs a global search across documents, fields, sources, and schemas, and materialize rebuilds the field-value index that powers autocomplete and field-values." },
14418
+ {
14419
+ type: "endpoint",
14420
+ method: "POST",
14421
+ path: "/v1/search/filter",
14422
+ summary: "Filter documents by field-value conditions (alias of POST /v1/documents/filter).",
14423
+ description: "Requires read scope. Same body as /v1/documents/filter.",
14424
+ blocks: []
14425
+ },
14426
+ {
14427
+ type: "endpoint",
14428
+ method: "POST",
14429
+ path: "/v1/search/omnisearch",
14430
+ summary: "Global search across documents, fields, sources, and schemas.",
14431
+ description: "Requires read scope. Body: query, limit.",
14432
+ blocks: [
14433
+ {
14434
+ type: "param-table",
14435
+ title: "Body parameters",
14436
+ params: [
14437
+ { name: "query", type: "string", description: "The search query." },
14438
+ { name: "limit", type: "integer", description: "Maximum results." }
14439
+ ]
14440
+ }
14441
+ ]
14442
+ },
14443
+ {
14444
+ type: "endpoint",
14445
+ method: "POST",
14446
+ path: "/v1/search/materialize",
14447
+ summary: "Rebuild the field-value index used by autocomplete and field-values.",
14448
+ description: "Requires write scope.",
14449
+ blocks: []
14450
+ }
14451
+ ],
14452
+ related: [
14453
+ { label: "Field Autocomplete", slug: "field-autocomplete" },
14454
+ { label: "Field Values", slug: "field-values" }
14455
+ ],
14456
+ faq: [
14457
+ { question: "Is /v1/search/filter the same as /v1/documents/filter?", answer: "Yes \u2014 it is an alias with the same request body and response, grouped under the search namespace." },
14458
+ { question: "When do I need to materialize?", answer: "The field-value index backs autocomplete and field-values. Trigger materialize to rebuild it after large ingestion if those reads look stale." }
14459
+ ],
14460
+ mentions: ["search", "omnisearch", "materialize", "filter alias", "field values"]
13998
14461
  }
13999
14462
  ];
14000
14463
 
@@ -14429,6 +14892,57 @@ echo -n '{"event":"extraction.complete","delivery_id":"dlv_test123","timestamp":
14429
14892
  { question: "Can I re-enable a deleted webhook?", answer: "No. Deletion is permanent. Create a new configuration with the same URL and settings if needed." }
14430
14893
  ],
14431
14894
  mentions: ["manage webhook", "update webhook", "delete webhook", "secret rotation"]
14895
+ },
14896
+ {
14897
+ slug: "webhook-reference-endpoints",
14898
+ parentSlug: "webhooks",
14899
+ title: "Reference Endpoints",
14900
+ seoTitle: "Webhook Reference Endpoints \u2014 Talonic Docs",
14901
+ description: "Read the webhook event catalog, delivery payload format, signature scheme, and retry policy programmatically.",
14902
+ content: [
14903
+ { type: "paragraph", text: "The webhook event catalog, delivery format, signature scheme, and retry policy are all available as live read-only endpoints, so an integration can discover them at runtime instead of hard-coding them. Each returns the same reference information described in the sections above, as JSON." },
14904
+ {
14905
+ type: "endpoint",
14906
+ method: "GET",
14907
+ path: "/v1/webhooks/events",
14908
+ summary: "List the supported webhook event types.",
14909
+ description: "Requires read scope. Returns { data: [{ event, description }] }.",
14910
+ blocks: []
14911
+ },
14912
+ {
14913
+ type: "endpoint",
14914
+ method: "GET",
14915
+ path: "/v1/webhooks/delivery",
14916
+ summary: "Get the delivery payload format (content type, method, headers, example).",
14917
+ description: "Requires read scope.",
14918
+ blocks: []
14919
+ },
14920
+ {
14921
+ type: "endpoint",
14922
+ method: "GET",
14923
+ path: "/v1/webhooks/signatures",
14924
+ summary: "Get the HMAC-SHA256 signature verification reference.",
14925
+ description: "Requires read scope.",
14926
+ blocks: []
14927
+ },
14928
+ {
14929
+ type: "endpoint",
14930
+ method: "GET",
14931
+ path: "/v1/webhooks/retries",
14932
+ summary: "Get the retry policy (max attempts, backoff schedule, timeout).",
14933
+ description: "Requires read scope.",
14934
+ blocks: []
14935
+ }
14936
+ ],
14937
+ related: [
14938
+ { label: "Events", slug: "webhook-events" },
14939
+ { label: "Signature Verification", slug: "webhook-security" },
14940
+ { label: "Retry Policy", slug: "webhook-retry" }
14941
+ ],
14942
+ faq: [
14943
+ { question: "Why read these from an endpoint?", answer: "So your integration can discover the supported events, delivery format, signature scheme, and retry policy at runtime rather than hard-coding them, and stay current if they change." }
14944
+ ],
14945
+ mentions: ["webhook events", "delivery format", "signature", "retry policy", "reference"]
14432
14946
  }
14433
14947
  ];
14434
14948
 
@@ -17692,7 +18206,7 @@ var sections31 = [
17692
18206
  ],
17693
18207
  faq: [
17694
18208
  { question: "Can a single result appear multiple times in pending approvals?", answer: "Yes. If a result fails multiple checks, each failed check outcome appears as a separate item in the pending approvals list. Approving the result clears all pending items for that result." },
17695
- { question: "How do I clear the pending approvals queue?", answer: "Approve or reject each pending result via `POST /v1/structuring/approvals/{id}/approve` or `/reject`. Alternatively, configure `auto_approve_after_hours` on the gate to auto-clear items after a timeout." }
18209
+ { question: "How do I clear the pending approvals queue?", answer: "Approve or reject each pending result via `POST /v1/structuring/approvals/{id}/approve` or `POST /v1/structuring/approvals/{id}/reject`. Alternatively, configure `auto_approve_after_hours` on the gate to auto-clear items after a timeout." }
17696
18210
  ],
17697
18211
  mentions: ["pending approvals", "approval queue", "manual review"]
17698
18212
  },
@@ -21974,6 +22488,73 @@ var sections37 = [
21974
22488
  { question: "Are all catalog entries available for use?", answer: "Most entries are live. Some deliverable types (case_snapshot, graph_relations) are registered as stubs with empty compatible_signals arrays \u2014 they appear in the catalog but cannot be used in bindings until their resolvers are implemented." }
21975
22489
  ],
21976
22490
  mentions: ["delivery catalog", "registry discovery", "compatibility"]
22491
+ },
22492
+ {
22493
+ slug: "delivery-pending-events",
22494
+ parentSlug: "delivery",
22495
+ title: "Pending & Events",
22496
+ seoTitle: "Delivery Pending & Events \u2014 Talonic Docs",
22497
+ description: "Inspect and cancel pending (debounced) deliveries, and list and replay the raw delivery events that drive bindings.",
22498
+ content: [
22499
+ { type: "paragraph", text: "Two surfaces sit underneath the delivery items and dead-letter queue: the pending (debounced) deliveries that have not fired yet, and the raw delivery events that bindings consume. Use them to see what is queued to go out, cancel a debounced delivery before it fires, and replay an event through the binding pipeline." },
22500
+ { type: "paragraph", text: "Pending deliveries are those still inside their debounce window. List them with the pending endpoint, drop a single queued job by id, or cancel all pending deliveries for one binding. Events are the upstream signals (a case resolved, a run completed) that matched a binding; list them and replay any one to re-run its delivery." },
22501
+ {
22502
+ type: "endpoint",
22503
+ method: "GET",
22504
+ path: "/v1/delivery/pending",
22505
+ summary: "List pending (debounced) deliveries that have not fired yet.",
22506
+ description: "Requires read scope.",
22507
+ blocks: []
22508
+ },
22509
+ {
22510
+ type: "endpoint",
22511
+ method: "DELETE",
22512
+ path: "/v1/delivery/pending/:jobId",
22513
+ summary: "Cancel a single pending delivery by job id.",
22514
+ description: "Requires write scope.",
22515
+ blocks: [
22516
+ { type: "param-table", title: "Path parameters", params: [{ name: "jobId", type: "string", required: true, description: "The pending delivery job id." }] }
22517
+ ]
22518
+ },
22519
+ {
22520
+ type: "endpoint",
22521
+ method: "POST",
22522
+ path: "/v1/delivery/bindings/:id/cancel-pending",
22523
+ summary: "Cancel all pending deliveries for a binding.",
22524
+ description: "Requires write scope.",
22525
+ blocks: [
22526
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "The binding id." }] }
22527
+ ]
22528
+ },
22529
+ {
22530
+ type: "endpoint",
22531
+ method: "GET",
22532
+ path: "/v1/delivery/events",
22533
+ summary: "List the raw delivery events that drive bindings.",
22534
+ description: "Requires read scope.",
22535
+ blocks: []
22536
+ },
22537
+ {
22538
+ type: "endpoint",
22539
+ method: "POST",
22540
+ path: "/v1/delivery/events/:id/replay",
22541
+ summary: "Replay a delivery event through the binding pipeline.",
22542
+ description: "Requires write scope.",
22543
+ blocks: [
22544
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "The event id to replay." }] }
22545
+ ]
22546
+ }
22547
+ ],
22548
+ related: [
22549
+ { label: "Dead Letter Queue", slug: "delivery-dlq" },
22550
+ { label: "History", slug: "delivery-history" },
22551
+ { label: "Manage Binding", slug: "manage-delivery-binding" }
22552
+ ],
22553
+ faq: [
22554
+ { question: "What is a pending delivery?", answer: "A delivery still inside its debounce window that has not fired yet. You can cancel it by job id, or cancel all pending deliveries for a binding, before it goes out." },
22555
+ { question: "How is replaying an event different from replaying an item?", answer: "Replaying an event re-runs it through binding matching and delivery from the source signal. Replaying a delivery item re-sends an already-produced delivery to its destination." }
22556
+ ],
22557
+ mentions: ["pending delivery", "debounce", "delivery events", "replay", "cancel"]
21977
22558
  }
21978
22559
  ];
21979
22560
 
@@ -22549,41 +23130,104 @@ var sections39 = [
22549
23130
  parentSlug: "cases",
22550
23131
  title: "Case Anomalies",
22551
23132
  seoTitle: "Case Anomalies Endpoint \u2014 Talonic Docs",
22552
- description: "List anomalies detected within a case with severity filtering and dismissed toggle. Dismiss individual anomalies after review.",
23133
+ description: "Detector anomalies for a case \u2014 field conflicts, divergent shared keys, suspicious value reuse, missing-document-type signals \u2014 plus open dangling-reference findings and the matched workflow template.",
22553
23134
  content: [
22554
- { type: "paragraph", text: "Anomalies are open **findings** detected across the documents in a case. The primary finding kind is the **dangling reference** \u2014 a document points at an identifier (a contract number, a parent agreement) that resolves to no document in your workspace, signalling a likely missing document." },
22555
- { type: "paragraph", text: "Findings are produced by the case-synthesis rebuild and carry a kind, the field and value that raised them, a severity, and a dismissed flag. Dismissed findings stay dismissed across rebuilds." },
22556
- { type: "paragraph", text: "On the public API, findings surface through `GET /v1/cases/:key` as `anomaly_count` (the number of open, undismissed findings on the case) and through `GET /v1/cases/:key/completeness`, whose `missing` array lists each open finding with its `kind`, `via_field`, and `value`. Reviewing and dismissing individual findings is done in the platform \u2014 the Business Cases view \u2014 rather than through dedicated public endpoints." },
22557
- { type: "callout", variant: "info", text: "There are currently no public `/v1/cases/:key/anomalies` endpoints. Use `anomaly_count` on the case detail plus the `completeness` endpoint to monitor open findings programmatically, and the platform UI to curate them." },
22558
- { type: "heading", level: 2, id: "case-anomalies-monitoring", text: "Monitoring findings programmatically" },
23135
+ { type: "paragraph", text: "The anomalies endpoint is the case **risk surface**. It runs the case's documents through the anomaly detectors and returns what looks wrong: `field_conflict` (the same key carries divergent values across documents), `duplicate_key_divergence`, `value_reuse_suspicious`, and `missing_document_type` (the matched workflow template expects a document type the case lacks). Each anomaly carries a type, severity, title, description, the `document_ids` involved, and `targets` \u2014 the (document, field, value) tuples so you can open the offending value directly." },
23136
+ { type: "paragraph", text: "Alongside the detector anomalies, the response includes the case's open **findings** \u2014 the dangling references (a document points at an identifier that resolves to no document in your workspace, signalling a likely missing document) \u2014 and the matched workflow `template`. Anomalies are computed on-read from the case's live field occurrences: no AI call is made and nothing is persisted, so the result always reflects the current documents." },
23137
+ { type: "callout", variant: "info", text: "Anomaly detection is bounded: for very large cases (more than ~120 documents) the detectors are skipped and only the open findings are returned, to keep the read fast." },
22559
23138
  {
22560
- type: "code",
22561
- title: "Open findings via the completeness endpoint",
22562
- code: `curl -s "$URL/v1/cases/5c7fa78c-4d92-4613-9f42-9fe74458d8a9/completeness" \\
22563
- -H "Authorization: Bearer $API_KEY"
22564
-
22565
- {
22566
- "case_id": "5c7fa78c-4d92-4613-9f42-9fe74458d8a9",
22567
- "document_count": 17,
22568
- "open_findings": 2,
22569
- "dismissed_findings": 1,
22570
- "completeness": 0.894,
22571
- "missing": [
22572
- { "kind": "dangling_reference", "via_field": "parent_agreement_ref", "value": "00129370" }
22573
- ]
22574
- }`
22575
- }
23139
+ type: "endpoint",
23140
+ method: "GET",
23141
+ path: "/v1/cases/:key/anomalies",
23142
+ summary: "Get the case risk surface: detector anomalies, open findings, and matched template.",
23143
+ description: "Requires read scope. Keyed on the case UUID.",
23144
+ blocks: [
23145
+ {
23146
+ type: "param-table",
23147
+ title: "Path parameters",
23148
+ params: [{ name: "key", type: "string", required: true, description: "Case UUID (the stable resource id)." }]
23149
+ }
23150
+ ]
23151
+ },
23152
+ { type: "heading", level: 2, id: "case-anomalies-response", text: "Response" },
23153
+ {
23154
+ type: "param-table",
23155
+ title: "Response fields",
23156
+ params: [
23157
+ { name: "data[].type", type: "string", description: "Anomaly type (field_conflict, duplicate_key_divergence, value_reuse_suspicious, missing_document_type)." },
23158
+ { name: "data[].severity", type: "string", description: "critical | warning | info." },
23159
+ { name: "data[].title", type: "string", description: "Short headline for the anomaly." },
23160
+ { name: "data[].description", type: "string", description: "Human-readable explanation." },
23161
+ { name: "data[].document_ids", type: "string[]", description: "Documents involved in the anomaly." },
23162
+ { name: "data[].targets", type: "object[]", description: "Offending (document_id, field, value) tuples for direct navigation." },
23163
+ { name: "findings", type: "object[]", description: "Open dangling-reference / missing-document findings (kind, via_field, value, severity)." },
23164
+ { name: "template", type: "object | null", description: "Matched workflow template (name, required_types, optional_types) or null." }
23165
+ ]
23166
+ },
23167
+ { type: "paragraph", text: "Two complementary signals also live on the case: `anomaly_count` on `GET /v1/cases/:key` (the number of open findings) and `GET /v1/cases/:key/completeness` (members \xF7 members + open findings). Use those for a cheap health number, and this endpoint when you need the detailed anomaly list." }
22576
23168
  ],
22577
23169
  related: [
23170
+ { label: "Case Evidence", slug: "case-evidence" },
22578
23171
  { label: "Get Case", slug: "get-case" },
22579
23172
  { label: "Case Operations", slug: "case-operations" }
22580
23173
  ],
22581
23174
  faq: [
22582
- { question: "What kinds of findings are detected?", answer: "The primary kind is the dangling reference: a reference-typed field whose value resolves to no document in the workspace \u2014 evidence of a likely missing document. Each finding carries the field and value that raised it." },
22583
- { question: "Can I dismiss a finding via the API?", answer: "Not on the public API today. Dismissal is done in the platform (Business Cases view); a dismissed finding stays dismissed across rebuilds and stops counting toward anomaly_count." },
22584
- { question: "How do findings relate to completeness?", answer: "The completeness score is members / (members + open findings) \u2014 each open finding lowers it. A case with no open findings reads 1.0." }
23175
+ { question: "What kinds of anomalies are detected?", answer: "Field conflicts (divergent values for a shared key), duplicate-key divergence, suspicious value reuse, and missing document types relative to the matched workflow template. Open dangling-reference findings (likely missing documents) are returned alongside them." },
23176
+ { question: "Does this endpoint cost an AI call?", answer: "No. Anomalies are computed on-read from the case's field occurrences with deterministic detectors. Nothing is persisted and no LLM is invoked." },
23177
+ { question: "Can I dismiss a finding via the API?", answer: "Not on the public API today. Dismissal is done in the platform (Business Cases view); a dismissed finding stays dismissed across rebuilds and stops counting toward anomaly_count." }
23178
+ ],
23179
+ mentions: ["anomalies", "findings", "risk", "dangling reference", "field conflict"]
23180
+ },
23181
+ {
23182
+ slug: "case-evidence",
23183
+ parentSlug: "cases",
23184
+ title: "Case Evidence",
23185
+ seoTitle: "Case Evidence Endpoint \u2014 Talonic Docs",
23186
+ description: "The connective evidence for a case: the shared-reference links that join its documents, the open gaps (dangling references / missing documents), and its member documents.",
23187
+ content: [
23188
+ { type: "paragraph", text: 'The evidence endpoint answers "why are these documents one case, and what is missing?". It returns the `connections` \u2014 the shared-reference links that join pairs of documents, each carrying the `via_field` and `value` that connect them, the link `kind`, a `confidence`, and any curator `verdict` (confirmed/rejected) \u2014 together with the open `gaps` (dangling references and missing-document findings) and the member `documents`.' },
23189
+ { type: "paragraph", text: "This is the synth case's evidence model: a case is a connected component over reference edges, so the connections ARE the evidence. Where `GET /v1/cases/:key/edges` is the raw edge list used to confirm or reject links, this endpoint is the read-oriented evidence view \u2014 connections plus the gaps that show what the case is still waiting on." },
23190
+ {
23191
+ type: "endpoint",
23192
+ method: "GET",
23193
+ path: "/v1/cases/:key/evidence",
23194
+ summary: "Get the case connective evidence: connections, gaps, and documents.",
23195
+ description: "Requires read scope. Keyed on the case UUID.",
23196
+ blocks: [
23197
+ {
23198
+ type: "param-table",
23199
+ title: "Path parameters",
23200
+ params: [{ name: "key", type: "string", required: true, description: "Case UUID (the stable resource id)." }]
23201
+ }
23202
+ ]
23203
+ },
23204
+ { type: "heading", level: 2, id: "case-evidence-response", text: "Response" },
23205
+ {
23206
+ type: "param-table",
23207
+ title: "Response fields",
23208
+ params: [
23209
+ { name: "connections[].document_a", type: "string", description: "One side of the link." },
23210
+ { name: "connections[].document_b", type: "string | null", description: "The other side (null for a single-document anchor)." },
23211
+ { name: "connections[].via_field", type: "string", description: "The field whose value joins the two documents." },
23212
+ { name: "connections[].value", type: "string", description: "The shared value." },
23213
+ { name: "connections[].kind", type: "string", description: "Link kind (e.g. reference, value-sibling, citation)." },
23214
+ { name: "connections[].confidence", type: "number | null", description: "Link confidence." },
23215
+ { name: "connections[].verdict", type: "string | null", description: "Curator verdict (confirmed / rejected) if set." },
23216
+ { name: "gaps", type: "object[]", description: "Open findings (dangling references / missing documents)." },
23217
+ { name: "documents", type: "object[]", description: "Member documents with filename, type, and date." }
23218
+ ]
23219
+ }
23220
+ ],
23221
+ related: [
23222
+ { label: "Case Anomalies", slug: "case-anomalies" },
23223
+ { label: "Case Edges", slug: "case-edges" },
23224
+ { label: "Get Case", slug: "get-case" }
23225
+ ],
23226
+ faq: [
23227
+ { question: "How is evidence different from edges?", answer: "Edges (GET /v1/cases/:key/edges) is the raw link list, used as the target for confirm/reject. Evidence is the read-oriented view: the same connections enriched with the joining field and value, plus the open gaps and the member documents." },
23228
+ { question: "What are gaps?", answer: "Open findings on the case \u2014 primarily dangling references, where a document points at an identifier that resolves to no document in your workspace, indicating a likely missing document." }
22585
23229
  ],
22586
- mentions: ["anomalies", "findings", "dangling reference", "completeness"]
23230
+ mentions: ["evidence", "connections", "links", "gaps", "shared reference"]
22587
23231
  },
22588
23232
  {
22589
23233
  slug: "case-operations",
@@ -23700,7 +24344,7 @@ var sections41 = [
23700
24344
  description: "Create a new ground truth dataset linked to a schema. The dataset defines the expected extraction output used for accuracy benchmarking.",
23701
24345
  content: [
23702
24346
  { type: "paragraph", text: "Create an empty ground truth dataset that you can populate with verified entries. Datasets serve as the baseline for benchmark runs that measure extraction accuracy. After creating a dataset, add entries individually or import them in bulk via CSV." },
23703
- { type: "paragraph", text: "The typical workflow is: create the dataset, then populate it using `POST /v1/quality/ground-truth/:id/entries` for individual entries or `POST /v1/quality/ground-truth/:id/entries/import-csv` for bulk import. Once populated, create a benchmark run with `POST /v1/quality/benchmarks`." },
24347
+ { type: "paragraph", text: "The typical workflow is: create the dataset, then populate it using `POST /v1/quality/ground-truth/:id/entries` for each entry. Once populated, create a benchmark run with `POST /v1/quality/benchmarks`." },
23704
24348
  { type: "paragraph", text: "The response returns the dataset with `document_count: 0` since it is initially empty. `user_schema_id` echoes the `schema_id` you passed in. The `links.self` URL points to the detail endpoint where you can retrieve entries or delete the dataset." },
23705
24349
  { type: "paragraph", text: "For best results, aim for at least 30-50 entries per dataset. The `schema_id` scoping ensures ground truth field names align with your extraction schema, producing more meaningful benchmark comparisons." },
23706
24350
  { type: "callout", variant: "info", text: "Field keys in `expected_data` entries should match the field names used in your extraction schema. Unmatched fields are stored but ignored during benchmark comparison." },
@@ -23967,37 +24611,6 @@ var sections41 = [
23967
24611
  "created_at": "2024-09-05T12:00:00.000Z"
23968
24612
  }`
23969
24613
  },
23970
- { type: "paragraph", text: "For bulk entry creation, use the CSV import endpoint. The CSV must have `document_id` as the first column, with remaining columns matching your schema field names." },
23971
- {
23972
- type: "endpoint",
23973
- method: "POST",
23974
- path: "/v1/quality/ground-truth/:datasetId/entries/import-csv",
23975
- summary: "Import entries from a CSV file.",
23976
- description: "Upload a CSV where each row is an entry and columns map to schema fields. The first column must be `document_id`.",
23977
- blocks: [
23978
- {
23979
- type: "param-table",
23980
- title: "Form data parameters",
23981
- params: [
23982
- { name: "file", type: "binary", required: true, description: "CSV file with ground truth values." }
23983
- ]
23984
- }
23985
- ]
23986
- },
23987
- { type: "heading", level: 2, id: "quality-entries-import-response", text: "Response (CSV import)" },
23988
- {
23989
- type: "param-table",
23990
- title: "Response fields",
23991
- params: [
23992
- { name: "imported", type: "integer", description: "Number of entries successfully imported." },
23993
- { name: "skipped", type: "integer", description: "Number of rows skipped (e.g. missing document_id or duplicate)." }
23994
- ]
23995
- },
23996
- {
23997
- type: "code",
23998
- title: "Response (CSV import)",
23999
- code: `{ "imported": 25, "skipped": 2 }`
24000
- },
24001
24614
  {
24002
24615
  type: "endpoint",
24003
24616
  method: "DELETE",
@@ -24475,7 +25088,7 @@ var sections42 = [
24475
25088
  ],
24476
25089
  related: [
24477
25090
  { label: "Create Routing Rule", slug: "create-routing-rule" },
24478
- { label: "Reorder Rules", slug: "reorder-routing-rules" }
25091
+ { label: "Manage Routing Rule", slug: "manage-routing-rule" }
24479
25092
  ],
24480
25093
  faq: [
24481
25094
  { question: "How are routing rules evaluated?", answer: "Rules are evaluated in priority order (lowest number first). The first rule whose conditions match the incoming document is applied." },
@@ -24493,7 +25106,7 @@ var sections42 = [
24493
25106
  content: [
24494
25107
  { type: "paragraph", text: 'Create a rule that automatically applies actions to incoming documents based on their metadata. Conditions define what to match (e.g. document type equals "invoice"), and actions define what to do (e.g. assign the finance schema). Rules are evaluated on every `document_classified` event.' },
24495
25108
  { type: "paragraph", text: 'The typical workflow is: create rules ordered by specificity \u2014 put narrow, high-priority rules first (e.g. "contracts from vendor X") and broader catch-all rules last. New rules are active immediately upon creation, so the next classified document will be evaluated against them.' },
24496
- { type: "paragraph", text: "The response returns the rule with `is_active: true`, a `trigger_type` of `document_classified`, and the assigned `priority` (defaults to 100 if omitted). The `action_type` is resolved from the `actions` object. Use the reorder endpoint after creation to adjust the priority relative to existing rules." },
25109
+ { type: "paragraph", text: "The response returns the rule with `is_active: true`, a `trigger_type` of `document_classified`, and the assigned `priority` (defaults to 100 if omitted). The `action_type` is resolved from the `actions` object. Adjust the priority of a rule relative to others with `PATCH /v1/routing-rules/{id}`." },
24497
25110
  { type: "paragraph", text: "Pair with `GET /v1/routing-rules` to verify the full priority chain after creating a rule. Use `source_connection_id` to scope rules to documents from a specific source \u2014 documents from other sources will skip the rule entirely. To test a rule before going live, create it and immediately disable it via `PATCH` with `is_active: false`." },
24498
25111
  { type: "callout", variant: "info", text: "New rules are created with `is_active: true` by default. If you want to test a rule before activating it, create it, then immediately disable it via `PATCH /v1/routing-rules/:id` with `is_active: false`." },
24499
25112
  {
@@ -24585,7 +25198,7 @@ var sections42 = [
24585
25198
  faq: [
24586
25199
  { question: "What condition fields are available?", answer: "Conditions can match on `document_type`, `source`, `language`, `sensitivity`, and other document metadata fields." },
24587
25200
  { question: "Can a rule have multiple actions?", answer: "Yes. Actions are executed in order. Common combinations include assigning a schema and triggering a job." },
24588
- { question: "What is the default priority?", answer: "If you omit the priority field, it defaults to 100. Use the reorder endpoint to adjust priorities after creation." }
25201
+ { question: "What is the default priority?", answer: "If you omit the priority field, it defaults to 100. Adjust priorities after creation with PATCH on each rule." }
24589
25202
  ],
24590
25203
  mentions: ["create rule", "conditions", "actions", "assign_schema", "trigger_job", "route_to"]
24591
25204
  },
@@ -24599,7 +25212,7 @@ var sections42 = [
24599
25212
  { type: "paragraph", text: "Retrieve, update, or delete a single routing rule. Updates take effect immediately \u2014 the next `document_classified` event will use the updated rule. Deleting a rule does not retroactively affect documents that were already routed by it." },
24600
25213
  { type: "paragraph", text: "Use `GET` to inspect a rule's conditions, actions, and priority. Use `PATCH` to adjust conditions, change the schema assignment, toggle `is_active`, or update the priority. Use `DELETE` when a rule is no longer needed \u2014 previously routed documents are not affected." },
24601
25214
  { type: "paragraph", text: "The `PATCH` response returns the full updated rule including the new `updated_at` timestamp. All fields are optional \u2014 only include fields you want to change. The `is_active` toggle lets you temporarily disable a rule without deleting it, which is useful for testing or during maintenance windows." },
24602
- { type: "paragraph", text: "After updating priority via `PATCH`, use `GET /v1/routing-rules` to verify the full evaluation order. For bulk priority changes, prefer the `POST /v1/routing-rules/reorder` endpoint instead of patching individual rules. Pair deletion with rule creation to replace a rule atomically." },
25215
+ { type: "paragraph", text: "After updating priority via `PATCH`, use `GET /v1/routing-rules` to verify the full evaluation order. Apply priority changes by patching the `priority` on each rule. Pair deletion with rule creation to replace a rule atomically." },
24603
25216
  { type: "callout", variant: "info", text: "Rule changes only affect future `document_classified` events. Documents already routed by a previous version of the rule retain their assigned schema and routing actions." },
24604
25217
  {
24605
25218
  type: "endpoint",
@@ -24766,81 +25379,13 @@ var sections42 = [
24766
25379
  ],
24767
25380
  related: [
24768
25381
  { label: "List Routing Rules", slug: "list-routing-rules" },
24769
- { label: "Reorder Rules", slug: "reorder-routing-rules" }
25382
+ { label: "Create Routing Rule", slug: "create-routing-rule" }
24770
25383
  ],
24771
25384
  faq: [
24772
25385
  { question: "Do updates affect already-routed documents?", answer: "No. Rule changes only affect future document_classified events. Documents that were already routed by the previous version of the rule are not retroactively updated." },
24773
25386
  { question: "Can I temporarily disable a rule without deleting it?", answer: "Yes. Use PATCH with is_active: false to disable the rule. It will be skipped during evaluation but retained for future re-activation." }
24774
25387
  ],
24775
25388
  mentions: ["update rule", "delete rule", "manage routing"]
24776
- },
24777
- {
24778
- slug: "reorder-routing-rules",
24779
- parentSlug: "routing-rules",
24780
- title: "Reorder Routing Rules",
24781
- seoTitle: "Reorder Routing Rules Endpoint \u2014 Talonic Docs",
24782
- description: "Reorder routing rules by providing an ordered array of rule IDs. Priority values are reassigned sequentially based on the new order.",
24783
- content: [
24784
- { type: "paragraph", text: "Reassign priority values for all routing rules at once. Pass an ordered array of rule IDs \u2014 the first ID receives priority 1, the second receives priority 2, and so on. This is the recommended way to change evaluation order after initial creation." },
24785
- { type: "paragraph", text: "Use this endpoint when you need to rearrange the evaluation order of multiple rules at once \u2014 for example, when promoting a new rule to the top of the chain or inserting a rule between two existing ones. This is more reliable than patching individual rule priorities, which can create gaps or collisions." },
24786
- { type: "paragraph", text: "The response returns a `reordered` array with each rule's `id` and new `priority` value. Priority 1 is evaluated first. The reorder takes effect immediately \u2014 the next `document_classified` event uses the new priority sequence." },
24787
- { type: "paragraph", text: "List all rules first via `GET /v1/routing-rules` to get the current IDs and order, then construct the reordered array. Include both active and inactive rules in the array to maintain a consistent priority sequence. Omitting any rule ID results in a validation error." },
24788
- { type: "callout", variant: "warning", text: "All active rule IDs must be included in the `rule_ids` array. Omitting any rule returns a validation error. Inactive rules should also be included to maintain a consistent priority sequence." },
24789
- {
24790
- type: "endpoint",
24791
- method: "POST",
24792
- path: "/v1/routing-rules/reorder",
24793
- summary: "Reorder rules by priority.",
24794
- blocks: [
24795
- {
24796
- type: "param-table",
24797
- title: "Body parameters",
24798
- params: [
24799
- { name: "rule_ids", type: "string[]", required: true, description: "Ordered array of rule IDs. First ID gets priority 1, second gets priority 2, and so on." }
24800
- ]
24801
- }
24802
- ]
24803
- },
24804
- { type: "heading", level: 2, id: "reorder-routing-rules-response", text: "Response" },
24805
- {
24806
- type: "param-table",
24807
- title: "Response fields",
24808
- params: [
24809
- { name: "reordered", type: "array", description: "Array of rules with their updated priority values." },
24810
- { name: "reordered[].id", type: "string", description: "Routing rule UUID." },
24811
- { name: "reordered[].priority", type: "integer", description: "New priority assigned to this rule." }
24812
- ]
24813
- },
24814
- {
24815
- type: "code",
24816
- title: "Response",
24817
- code: `{
24818
- "reordered": [
24819
- { "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "priority": 1 },
24820
- { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "priority": 2 },
24821
- { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "priority": 3 }
24822
- ]
24823
- }`
24824
- },
24825
- { type: "heading", level: 2, id: "reorder-routing-rules-errors", text: "Errors" },
24826
- {
24827
- type: "param-table",
24828
- title: "Error responses",
24829
- params: [
24830
- { name: "401", type: "unauthorized", description: "Missing or invalid API key." },
24831
- { name: "429", type: "rate_limited", description: "Too many requests. Retry after the period indicated in the Retry-After header." }
24832
- ]
24833
- }
24834
- ],
24835
- related: [
24836
- { label: "List Routing Rules", slug: "list-routing-rules" },
24837
- { label: "Manage Routing Rule", slug: "manage-routing-rule" }
24838
- ],
24839
- faq: [
24840
- { question: "Do I need to include all rule IDs?", answer: "Yes. All active rule IDs must be included in the array. Omitting a rule ID will result in an error." },
24841
- { question: "Does reordering affect currently processing documents?", answer: "No. Reordering only affects future document_classified events. Documents currently being processed continue with their already-matched rule." }
24842
- ],
24843
- mentions: ["reorder", "priority", "rule ordering"]
24844
25389
  }
24845
25390
  ];
24846
25391
 
@@ -25140,6 +25685,43 @@ X-Talonic-Cells-Resolved-AI: 1`
25140
25685
  }
25141
25686
  ],
25142
25687
  mentions: ["cost headers", "X-Talonic-Cost", "credits", "balance", "cells resolved"]
25688
+ },
25689
+ {
25690
+ slug: "billing-upgrade-link",
25691
+ parentSlug: "billing",
25692
+ title: "Upgrade Link",
25693
+ seoTitle: "Billing Upgrade Link \u2014 Talonic Docs",
25694
+ description: "Get the URL an agent hands to a human to upgrade the plan or add a payment method.",
25695
+ content: [
25696
+ { type: "paragraph", text: "The upgrade-link endpoint returns a URL an AI agent can hand to a human to upgrade the workspace or add a payment method. Plan changes and payment are deliberately a human-in-the-loop step, so this endpoint never charges anything: it returns a deep link into the dashboard plus a flag indicating a human is required." },
25697
+ {
25698
+ type: "endpoint",
25699
+ method: "GET",
25700
+ path: "/v1/billing/upgrade-link",
25701
+ summary: "Get the dashboard upgrade / add-payment link.",
25702
+ description: "Requires read scope.",
25703
+ blocks: []
25704
+ },
25705
+ { type: "heading", level: 2, id: "billing-upgrade-link-response", text: "Response" },
25706
+ {
25707
+ type: "param-table",
25708
+ title: "Response fields",
25709
+ params: [
25710
+ { name: "url", type: "string", description: "The dashboard deep link to upgrade or add payment." },
25711
+ { name: "provider", type: "string", description: "The link provider (dashboard)." },
25712
+ { name: "requires_human", type: "boolean", description: "Always true \u2014 a person must complete the upgrade." },
25713
+ { name: "message", type: "string", description: "A human-readable instruction." }
25714
+ ]
25715
+ }
25716
+ ],
25717
+ related: [
25718
+ { label: "Billing Settings", slug: "billing-settings" },
25719
+ { label: "Account Snapshot", slug: "get-account" }
25720
+ ],
25721
+ faq: [
25722
+ { question: "Does this endpoint charge anything?", answer: "No. It only returns a link a human follows to upgrade or add payment. Plan changes are human-in-the-loop by design." }
25723
+ ],
25724
+ mentions: ["upgrade link", "billing", "payment", "human-in-the-loop"]
25143
25725
  }
25144
25726
  ];
25145
25727
 
@@ -25277,11 +25859,659 @@ var sections44 = [
25277
25859
  "zero re-extraction",
25278
25860
  "resolved_document_values"
25279
25861
  ]
25862
+ },
25863
+ {
25864
+ slug: "registry-health",
25865
+ parentSlug: "registry",
25866
+ title: "Registry Health",
25867
+ seoTitle: "Field Registry Health \u2014 Talonic Docs",
25868
+ description: "Read-only health snapshot of your field registry: tier and admission counts, singleton and weak-name rates, occurrence and resolution stats, transfer hit rate, and coherence/atomicity metrics.",
25869
+ content: [
25870
+ { type: "paragraph", text: "The registry health endpoint returns a read-only snapshot of your workspace's field registry \u2014 the same view the in-app registry dashboard reads. It is how you monitor the health of the deduplicated knowledge graph that extraction builds over time: how many canonical concepts exist, how many fields are still provisional, how often deterministic transfer fills cells without an AI call, and where naming or coherence is degrading. No lifecycle or cleanup actions are exposed; this is a metrics read only." },
25871
+ { type: "paragraph", text: "The response is grouped into sections. `registry` carries tier counts (tier 1 canonical, tier 2 provisional, tier 3 candidate), admission counts, and the singleton rate. `occurrences` and `resolution_last_24h` track how values bind into the graph. `transfer` reports the deterministic transfer hit rate (the share of cells filled with no AI call). `name_quality`, `coherence`, and `atomicity` surface quality signals \u2014 weak names, incoherent canonicals, non-atomic values \u2014 and `junk_classes` flags clusters that look like noise." },
25872
+ { type: "callout", variant: "info", text: "Use this to track registry health as your corpus grows. A rising transfer hit rate means more cells resolve for free; a rising singleton or weak-name rate is an early signal that naming or clustering needs attention." },
25873
+ {
25874
+ type: "endpoint",
25875
+ method: "GET",
25876
+ path: "/v1/registry/health",
25877
+ summary: "Read a health snapshot of the field registry.",
25878
+ description: "Requires read scope. Read-only \u2014 no lifecycle or cleanup actions.",
25879
+ blocks: []
25880
+ },
25881
+ { type: "heading", level: 2, id: "registry-health-response", text: "Response" },
25882
+ {
25883
+ type: "param-table",
25884
+ title: "Response sections",
25885
+ params: [
25886
+ { name: "registry", type: "object", description: "total_fields, tier_1/2/3, singleton_count, singleton_rate, admission_canonical, admission_provisional, admission_provisional_rate." },
25887
+ { name: "embeddings", type: "object", description: "provisional_backlog and oldest_provisional_minutes." },
25888
+ { name: "occurrences", type: "object", description: "total, unresolved, needs_confirmation." },
25889
+ { name: "resolution_last_24h", type: "object", description: "total resolutions in the last 24h and their confidence-band breakdown." },
25890
+ { name: "convergence", type: "object", description: "convergence_rate and confirm_rate (nullable)." },
25891
+ { name: "name_quality", type: "object", description: "weak_name_count and weak_name_rate." },
25892
+ { name: "coherence", type: "object", description: "multi_occ_fields, incoherent_canonical_count, incoherent_rate." },
25893
+ { name: "transfer", type: "object", description: "transfer_cells, extraction_cells, transfer_hit_rate." },
25894
+ { name: "atomicity", type: "object", description: "total_values, non_atomic_count, non_atomic_rate." },
25895
+ { name: "junk_classes", type: "object", description: "Counts of clusters flagged as likely noise." }
25896
+ ]
25897
+ },
25898
+ {
25899
+ type: "code",
25900
+ title: "curl",
25901
+ language: "bash",
25902
+ code: `curl -s https://api.talonic.ai/v1/registry/health \\
25903
+ -H "Authorization: Bearer tlnc_your_api_key"`
25904
+ },
25905
+ {
25906
+ type: "code",
25907
+ title: "Response (truncated)",
25908
+ code: `{
25909
+ "registry": {
25910
+ "total_fields": 8174,
25911
+ "tier_1": 162,
25912
+ "tier_2": 724,
25913
+ "tier_3": 7288,
25914
+ "singleton_count": 6099,
25915
+ "singleton_rate": 0.746,
25916
+ "admission_canonical": 6449,
25917
+ "admission_provisional": 1725,
25918
+ "admission_provisional_rate": 0.211
25919
+ },
25920
+ "transfer": { "transfer_cells": 12044, "extraction_cells": 5310, "transfer_hit_rate": 0.69 },
25921
+ "name_quality": { "weak_name_count": 88, "weak_name_rate": 0.011 },
25922
+ "coherence": { "multi_occ_fields": 2075, "incoherent_canonical_count": 12, "incoherent_rate": 0.006 }
25923
+ }`
25924
+ }
25925
+ ],
25926
+ related: [
25927
+ { label: "Query the Registry", slug: "registry-query" },
25928
+ { label: "Field Registry", slug: "list-fields" }
25929
+ ],
25930
+ faq: [
25931
+ { question: "Does this endpoint change anything?", answer: "No. It is read-only \u2014 a metrics snapshot. Lifecycle, promotion, and cleanup actions are not exposed on the public API." },
25932
+ { question: "What is the transfer hit rate?", answer: "The share of cells filled deterministically from the registry with no AI call. A higher rate means more of your extraction resolves for free as the registry grows." },
25933
+ { question: "What do the tiers mean?", answer: "Tier 1 is canonical (proven) concepts, tier 2 provisional, tier 3 candidate. The mix tells you how much of the registry is established versus still being learned." }
25934
+ ],
25935
+ mentions: ["registry health", "field registry", "tiers", "transfer hit rate", "coherence"]
25280
25936
  }
25281
25937
  ];
25282
25938
 
25283
- // src/content/api/reference-data.ts
25939
+ // src/content/api/customer-ontologies.ts
25284
25940
  var sections45 = [
25941
+ {
25942
+ slug: "customer-ontologies-overview",
25943
+ parentSlug: "customer-ontologies",
25944
+ title: "Customer Ontologies",
25945
+ seoTitle: "Customer Ontologies \u2014 Overlay API \u2014 Talonic Docs",
25946
+ description: "A versioned overlay of your own document types and field concepts that augments the built-in Talonic ontology. List and create ontologies, then author, publish, and project them.",
25947
+ content: [
25948
+ { type: "paragraph", text: 'A customer ontology is your own overlay on top of the built-in Talonic ontology: a versioned container of custom document types and field concepts specific to your domain. You define the doctypes you care about (a "Lieferschein", a "Rahmenvertrag") and the fields that matter on them, anchor each doctype to a Talonic type, and publish. Publishing projects your authored fields and doctypes into the field registry and the classifier, so extraction and classification start recognizing your vocabulary.' },
25949
+ { type: "paragraph", text: "Ontologies are versioned with a draft/publish lifecycle. Editing a published ontology auto-forks a new draft, so the published overlay your pipelines read never changes underneath them mid-run. The overlay is additive: an empty or unpublished ontology leaves capture and classification byte-identical to the Talonic-only path, so adopting ontologies never regresses existing behavior." },
25950
+ { type: "callout", variant: "info", text: "Publishing is a write operation with side effects: it pins fields into your registry (protected from cleanup) and adds custom doctypes the classifier will assign. Unpublishing or deleting reverses the projection." },
25951
+ {
25952
+ type: "endpoint",
25953
+ method: "GET",
25954
+ path: "/v1/customer-ontologies",
25955
+ summary: "List your ontologies with their latest version status.",
25956
+ description: "Requires read scope.",
25957
+ blocks: []
25958
+ },
25959
+ {
25960
+ type: "endpoint",
25961
+ method: "POST",
25962
+ path: "/v1/customer-ontologies",
25963
+ summary: "Create an ontology and its empty draft v1.",
25964
+ description: "Requires write scope.",
25965
+ blocks: [
25966
+ {
25967
+ type: "param-table",
25968
+ title: "Body parameters",
25969
+ params: [
25970
+ { name: "name", type: "string", required: true, description: "A name for the ontology." },
25971
+ { name: "description", type: "string", description: "Optional description." }
25972
+ ]
25973
+ }
25974
+ ]
25975
+ },
25976
+ {
25977
+ type: "code",
25978
+ title: "curl",
25979
+ language: "bash",
25980
+ code: `curl -s -X POST https://api.talonic.ai/v1/customer-ontologies \\
25981
+ -H "Authorization: Bearer tlnc_your_api_key" \\
25982
+ -H "Content-Type: application/json" \\
25983
+ -d '{"name":"Logistics","description":"Delivery + freight doctypes"}'`
25984
+ }
25985
+ ],
25986
+ related: [
25987
+ { label: "Import an Ontology", slug: "import-customer-ontology" },
25988
+ { label: "Map to Talonic Types", slug: "customer-ontology-mappings" }
25989
+ ],
25990
+ faq: [
25991
+ { question: "What does an ontology change?", answer: "Once published, your custom doctypes are assigned by the classifier (a customer axis alongside the always-on Talonic axis) and your authored fields are pinned into the registry so extraction targets them. Unpublished ontologies change nothing." },
25992
+ { question: "Is the overlay additive?", answer: "Yes. An empty or unpublished overlay leaves capture and classification identical to the Talonic-only path, so adopting ontologies never regresses existing extractions." }
25993
+ ],
25994
+ mentions: ["customer ontology", "overlay", "doctypes", "field concepts", "classification"]
25995
+ },
25996
+ {
25997
+ slug: "import-customer-ontology",
25998
+ parentSlug: "customer-ontologies",
25999
+ title: "Import an Ontology",
26000
+ seoTitle: "Import Customer Ontology \u2014 Talonic Docs",
26001
+ description: "Top-down import of a complete ontology \u2014 doctypes and fields in one call, optionally published immediately. Blank doctype anchors are auto-mapped to Talonic types.",
26002
+ content: [
26003
+ { type: "paragraph", text: "Import is the fast path for standing up an ontology from a definition you already have. You post the whole thing \u2014 name, doctypes, and fields \u2014 in one call, and optionally publish it immediately with `publish: true`. Any doctype you leave without a `maps_to` anchor is auto-mapped to the best-matching Talonic type by embedding similarity before it is saved, so you do not have to hand-anchor every doctype." },
26004
+ { type: "paragraph", text: "Each doctype carries a `key`, a `name`, an optional `category`, classification `signals`, and the `maps_to` Talonic anchor. Each field carries a `key` plus optional `canonical_name`, `display_name`, `data_type`, `synonyms`, a `doctype_scope` (a doctype key, or null/empty/* for a global field), `required`, `format`, `enum_values`, and `examples`." },
26005
+ { type: "callout", variant: "info", text: "Auto-mapping is advisory and never overwrites a maps_to you supplied. A doctype whose top match is ambiguous is left blank for you to resolve with the suggest-mappings endpoint." },
26006
+ {
26007
+ type: "endpoint",
26008
+ method: "POST",
26009
+ path: "/v1/customer-ontologies/import",
26010
+ summary: "Create or replace the draft from a full definition, optionally publishing.",
26011
+ description: "Requires write scope.",
26012
+ blocks: [
26013
+ {
26014
+ type: "param-table",
26015
+ title: "Body parameters",
26016
+ params: [
26017
+ { name: "name", type: "string", required: true, description: "Ontology name." },
26018
+ { name: "description", type: "string", description: "Optional description." },
26019
+ { name: "doctypes", type: "object[]", description: "Custom doctypes (key, name, category?, maps_to?, signals?)." },
26020
+ { name: "fields", type: "object[]", description: "Field concepts (key, canonical_name?, data_type?, doctype_scope?, \u2026)." },
26021
+ { name: "publish", type: "boolean", description: "Publish immediately after import." }
26022
+ ]
26023
+ }
26024
+ ]
26025
+ }
26026
+ ],
26027
+ related: [
26028
+ { label: "Customer Ontologies", slug: "customer-ontologies-overview" },
26029
+ { label: "Publish & Lifecycle", slug: "publish-customer-ontology" }
26030
+ ],
26031
+ faq: [
26032
+ { question: "Do I have to anchor every doctype?", answer: "No. A doctype with no maps_to is auto-mapped to the closest Talonic type by embedding similarity before saving \u2014 unless the match is ambiguous, in which case it is left blank for you to pick." },
26033
+ { question: "Does import replace my draft?", answer: "Import creates or replaces the draft version. Publish it (or pass publish: true) to make it live; the previously published version stays in history." }
26034
+ ],
26035
+ mentions: ["import", "doctypes", "fields", "auto-mapping", "publish"]
26036
+ },
26037
+ {
26038
+ slug: "get-customer-ontology",
26039
+ parentSlug: "customer-ontologies",
26040
+ title: "Get & Version History",
26041
+ seoTitle: "Get Customer Ontology \u2014 Talonic Docs",
26042
+ description: "Read an ontology header with its latest version content, and list its full version history newest-first.",
26043
+ content: [
26044
+ { type: "paragraph", text: "Read a single ontology by id to get its header plus the content of its latest version (its doctypes and fields). List its versions to see the full draft/publish history, newest first \u2014 each publish and each auto-fork is a version, so the history is the audit trail of how the overlay evolved." },
26045
+ {
26046
+ type: "endpoint",
26047
+ method: "GET",
26048
+ path: "/v1/customer-ontologies/{id}",
26049
+ summary: "Ontology header + latest version content.",
26050
+ description: "Requires read scope.",
26051
+ blocks: [
26052
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "Ontology UUID." }] }
26053
+ ]
26054
+ },
26055
+ {
26056
+ type: "endpoint",
26057
+ method: "GET",
26058
+ path: "/v1/customer-ontologies/{id}/versions",
26059
+ summary: "Version history, newest first.",
26060
+ description: "Requires read scope.",
26061
+ blocks: []
26062
+ }
26063
+ ],
26064
+ related: [
26065
+ { label: "Customer Ontologies", slug: "customer-ontologies-overview" },
26066
+ { label: "Publish & Lifecycle", slug: "publish-customer-ontology" }
26067
+ ],
26068
+ faq: [
26069
+ { question: "What is in a version?", answer: "Each version snapshots the ontology's doctypes and fields and its status (draft or published). Editing a published ontology auto-forks a new draft version, so the published one your pipelines read is immutable." }
26070
+ ],
26071
+ mentions: ["ontology version", "history", "draft", "published"]
26072
+ },
26073
+ {
26074
+ slug: "publish-customer-ontology",
26075
+ parentSlug: "customer-ontologies",
26076
+ title: "Update, Publish & Lifecycle",
26077
+ seoTitle: "Publish Customer Ontology \u2014 Talonic Docs",
26078
+ description: "Update the draft, publish it to project into the registry and classifier, unpublish to retract, or delete the ontology \u2014 each reversing the projection as needed.",
26079
+ content: [
26080
+ { type: "paragraph", text: "Updating an ontology edits its draft; if the ontology is already published, the update auto-forks a fresh draft so the live overlay is untouched until you publish again. Publishing projects the latest version into your workspace: authored fields become pinned registry rows (protected from cleanup), and custom doctypes become classifier types on the customer axis. Unpublishing flips the published version back to a draft and reverses the projection; deleting removes the ontology and reverses the projection too." },
26081
+ { type: "callout", variant: "warning", text: "Publish, unpublish, and delete change extraction and classification behavior for your workspace. They are write-scope operations; treat publishing an ontology like a configuration change, not a read." },
26082
+ {
26083
+ type: "endpoint",
26084
+ method: "PATCH",
26085
+ path: "/v1/customer-ontologies/{id}",
26086
+ summary: "Update the draft (auto-forks if published).",
26087
+ description: "Requires write scope.",
26088
+ blocks: [
26089
+ {
26090
+ type: "param-table",
26091
+ title: "Body parameters",
26092
+ params: [
26093
+ { name: "name", type: "string", description: "Rename the ontology." },
26094
+ { name: "description", type: "string", description: "Update the description." },
26095
+ { name: "doctypes", type: "object[]", description: "Replace the doctype set." },
26096
+ { name: "fields", type: "object[]", description: "Replace the field set." }
26097
+ ]
26098
+ }
26099
+ ]
26100
+ },
26101
+ {
26102
+ type: "endpoint",
26103
+ method: "POST",
26104
+ path: "/v1/customer-ontologies/{id}/publish",
26105
+ summary: "Publish the latest version and project it.",
26106
+ description: "Requires write scope.",
26107
+ blocks: []
26108
+ },
26109
+ {
26110
+ type: "endpoint",
26111
+ method: "POST",
26112
+ path: "/v1/customer-ontologies/{id}/unpublish",
26113
+ summary: "Retract the published version to a draft and reverse projection.",
26114
+ description: "Requires write scope.",
26115
+ blocks: []
26116
+ },
26117
+ {
26118
+ type: "endpoint",
26119
+ method: "DELETE",
26120
+ path: "/v1/customer-ontologies/{id}",
26121
+ summary: "Delete the ontology (versions cascade) and reverse projection.",
26122
+ description: "Requires write scope.",
26123
+ blocks: []
26124
+ }
26125
+ ],
26126
+ related: [
26127
+ { label: "Customer Ontologies", slug: "customer-ontologies-overview" },
26128
+ { label: "Import an Ontology", slug: "import-customer-ontology" }
26129
+ ],
26130
+ faq: [
26131
+ { question: "What happens to my registry when I unpublish?", answer: "Unpublish reverses the projection: it un-pins the authored registry rows (they are kept, not deleted, since occurrences may bind) and drops custom doctypes no document is classified under. Concepts another still-published ontology declares are left intact." },
26132
+ { question: "Is publishing idempotent?", answer: "Yes. Re-publishing upserts the pinned registry rows and matches doctypes by name, so publishing the same content twice is a no-op." }
26133
+ ],
26134
+ mentions: ["publish", "unpublish", "projection", "registry", "lifecycle"]
26135
+ },
26136
+ {
26137
+ slug: "customer-ontology-mappings",
26138
+ parentSlug: "customer-ontologies",
26139
+ title: "Map to Talonic Types",
26140
+ seoTitle: "Customer Ontology Mappings \u2014 Talonic Docs",
26141
+ description: "List the full Talonic type catalog, embedding-match your custom doctypes onto Talonic types, and preview the published doctypes the classifier will see.",
26142
+ content: [
26143
+ { type: "paragraph", text: "Every custom doctype anchors to a Talonic ontology type so the dual-axis classifier always has a built-in anchor alongside your custom label. These endpoints help you build and verify those anchors. List the full Talonic type catalog to populate a picker, embedding-match a set of your doctypes to get ranked suggestions, and preview the published custom doctypes the classifier will actually apply." },
26144
+ { type: "paragraph", text: "Suggest-mappings is stateless \u2014 it runs over the doctypes you post and persists nothing, so an authoring UI can call it on unsaved edits. For each doctype it returns a confident `auto` target (or null), an `ambiguous` flag, and ranked `candidates` for the user to choose from." },
26145
+ {
26146
+ type: "endpoint",
26147
+ method: "GET",
26148
+ path: "/v1/customer-ontologies/talonic-types",
26149
+ summary: "The full Talonic ontology type list (id + name + breadcrumb).",
26150
+ description: "Requires read scope. A global catalog, not tenant data.",
26151
+ blocks: []
26152
+ },
26153
+ {
26154
+ type: "endpoint",
26155
+ method: "POST",
26156
+ path: "/v1/customer-ontologies/suggest-mappings",
26157
+ summary: "Embedding-match posted doctypes onto Talonic types.",
26158
+ description: "Requires read scope. Stateless \u2014 persists nothing.",
26159
+ blocks: [
26160
+ {
26161
+ type: "param-table",
26162
+ title: "Body parameters",
26163
+ params: [
26164
+ { name: "doctypes", type: "object[]", required: true, description: "Doctypes to match (key, name, \u2026). Returns per-doctype auto/ambiguous/candidates." }
26165
+ ]
26166
+ }
26167
+ ]
26168
+ },
26169
+ {
26170
+ type: "endpoint",
26171
+ method: "GET",
26172
+ path: "/v1/customer-ontologies/overlay/doctypes",
26173
+ summary: "The published custom doctypes the classifier will apply.",
26174
+ description: "Requires read scope.",
26175
+ blocks: []
26176
+ }
26177
+ ],
26178
+ related: [
26179
+ { label: "Customer Ontologies", slug: "customer-ontologies-overview" },
26180
+ { label: "Import an Ontology", slug: "import-customer-ontology" }
26181
+ ],
26182
+ faq: [
26183
+ { question: "Why does every doctype need a Talonic anchor?", answer: "The classifier runs a Talonic axis that is always on; anchoring your custom doctype to a Talonic type preserves that built-in signal, so a custom-classified document still carries a Talonic type for routing and downstream logic." },
26184
+ { question: "Is suggest-mappings free of side effects?", answer: "Yes. It runs over the doctypes you post and persists nothing, so you can call it on unsaved edits while authoring." }
26185
+ ],
26186
+ mentions: ["talonic types", "suggest mappings", "embedding match", "maps_to", "overlay doctypes"]
26187
+ }
26188
+ ];
26189
+
26190
+ // src/content/api/account.ts
26191
+ var sections46 = [
26192
+ {
26193
+ slug: "get-account",
26194
+ parentSlug: "account",
26195
+ title: "Account Snapshot",
26196
+ seoTitle: "Account Snapshot Endpoint \u2014 Talonic Docs",
26197
+ description: "Read your workspace account snapshot: tier, status, credit balance and monthly allowance, daily limits, and usage so far today.",
26198
+ content: [
26199
+ { type: "paragraph", text: "The account endpoint returns a self-serve snapshot of your workspace: its plan `tier` and `status`, your `credits` (balance, currency, and \u2014 for the free tier \u2014 the monthly allowance and reset date), the per-namespace `daily_limits`, and `usage_today`. It is the single call an agent or dashboard reads to understand how much headroom is left before hitting a limit or running out of credits." },
26200
+ { type: "paragraph", text: "Credits are the unit of spend. The `credits.balance` is what remains; `monthly_allowance` and `tier_resets_at` are populated for metered free-tier workspaces and null for paid tiers. The `daily_limits` are abuse ceilings per namespace (extract, platform, ingest, operations), and `usage_today` is how much of each you have consumed since midnight UTC." },
26201
+ {
26202
+ type: "endpoint",
26203
+ method: "GET",
26204
+ path: "/v1/account",
26205
+ summary: "Read the workspace account snapshot.",
26206
+ description: "Requires read scope.",
26207
+ blocks: []
26208
+ },
26209
+ { type: "heading", level: 2, id: "get-account-response", text: "Response" },
26210
+ {
26211
+ type: "param-table",
26212
+ title: "Response fields",
26213
+ params: [
26214
+ { name: "tier", type: "string", description: "Plan tier (e.g. free, pro)." },
26215
+ { name: "status", type: "string", description: "Account status (e.g. active)." },
26216
+ { name: "credits.balance", type: "integer", description: "Remaining credit balance." },
26217
+ { name: "credits.currency", type: "string", description: "Credit currency (EUR)." },
26218
+ { name: "credits.monthly_allowance", type: "integer | null", description: "Free-tier monthly credit grant (null for paid tiers)." },
26219
+ { name: "credits.tier_resets_at", type: "string | null", description: "When the free-tier allowance resets (null for paid)." },
26220
+ { name: "daily_limits", type: "object", description: "Per-namespace daily ceilings (extract, platform, ingest, operations)." },
26221
+ { name: "usage_today", type: "object", description: "Per-namespace usage since midnight UTC." }
26222
+ ]
26223
+ },
26224
+ {
26225
+ type: "code",
26226
+ title: "curl",
26227
+ language: "bash",
26228
+ code: `curl -s https://api.talonic.ai/v1/account \\
26229
+ -H "Authorization: Bearer tlnc_your_api_key"`
26230
+ }
26231
+ ],
26232
+ related: [
26233
+ { label: "Manage API Keys", slug: "account-keys" },
26234
+ { label: "Credit Balance", slug: "credits-balance" }
26235
+ ],
26236
+ faq: [
26237
+ { question: "What are credits?", answer: "Credits are the unit of spend for metered operations. The account snapshot shows your remaining balance, and (on the free tier) the monthly allowance and its reset date." },
26238
+ { question: "What do daily_limits mean?", answer: "They are per-namespace abuse ceilings, not billing limits. Spend is governed by the credit balance; the daily limits only stop pathological request volume." }
26239
+ ],
26240
+ mentions: ["account", "tier", "credits", "daily limits", "usage"]
26241
+ },
26242
+ {
26243
+ slug: "account-keys",
26244
+ parentSlug: "account",
26245
+ title: "Manage API Keys",
26246
+ seoTitle: "Manage API Keys \u2014 Talonic Docs",
26247
+ description: "List your workspace API keys (masked), mint a new scoped key (the raw secret is returned once), and revoke a key.",
26248
+ content: [
26249
+ { type: "paragraph", text: "These endpoints let a workspace manage its own API keys programmatically. List your keys (always masked \u2014 the raw secret is never returned again), mint a new key with a name and a chosen scope set, and revoke a key you no longer need. The raw secret of a new key is returned exactly once, on creation; store it immediately." },
26250
+ { type: "paragraph", text: "A key carries a subset of the valid scopes \u2014 `extract`, `read`, `write`, `operations`, `billing`, `delivery`. If you omit `scopes` on create, the key defaults to `extract, read, write, operations`. Revocation is scoped to your own workspace, so you can never revoke another tenant's key." },
26251
+ {
26252
+ type: "endpoint",
26253
+ method: "GET",
26254
+ path: "/v1/account/keys",
26255
+ summary: "List the workspace API keys (masked).",
26256
+ description: "Requires read scope. The raw secret is never returned.",
26257
+ blocks: []
26258
+ },
26259
+ {
26260
+ type: "endpoint",
26261
+ method: "POST",
26262
+ path: "/v1/account/keys",
26263
+ summary: "Mint a scoped API key. Returns the raw secret once.",
26264
+ description: "Requires write scope.",
26265
+ blocks: [
26266
+ {
26267
+ type: "param-table",
26268
+ title: "Body parameters",
26269
+ params: [
26270
+ { name: "name", type: "string", required: true, description: "A label for the key." },
26271
+ { name: "scopes", type: "string[]", description: "Requested scopes (subset of extract, read, write, operations, billing, delivery). Defaults to extract, read, write, operations." }
26272
+ ]
26273
+ }
26274
+ ]
26275
+ },
26276
+ {
26277
+ type: "endpoint",
26278
+ method: "DELETE",
26279
+ path: "/v1/account/keys/:keyId",
26280
+ summary: "Revoke an API key.",
26281
+ description: "Requires write scope. Scoped to your own workspace.",
26282
+ blocks: [
26283
+ { type: "param-table", title: "Path parameters", params: [{ name: "keyId", type: "string", required: true, description: "The API key id to revoke." }] }
26284
+ ]
26285
+ },
26286
+ { type: "callout", variant: "warning", text: "The raw key secret is shown only once, in the create response. If you lose it, revoke the key and mint a new one \u2014 it cannot be retrieved again." }
26287
+ ],
26288
+ related: [
26289
+ { label: "Account Snapshot", slug: "get-account" },
26290
+ { label: "Authentication", slug: "authentication" }
26291
+ ],
26292
+ faq: [
26293
+ { question: "Can I retrieve a key secret later?", answer: "No. The raw secret is returned only once on creation. Listing keys returns masked values. If lost, revoke and mint a new key." },
26294
+ { question: "What scopes can a key have?", answer: "A subset of extract, read, write, operations, billing, and delivery. Omitting scopes on create defaults to extract, read, write, operations." }
26295
+ ],
26296
+ mentions: ["api keys", "scopes", "mint key", "revoke", "self-serve"]
26297
+ }
26298
+ ];
26299
+
26300
+ // src/content/api/nodes.ts
26301
+ var sections47 = [
26302
+ {
26303
+ slug: "node-jobs",
26304
+ parentSlug: "nodes",
26305
+ title: "Node Jobs",
26306
+ seoTitle: "Node Jobs \u2014 Per-Stage Primitives \u2014 Talonic Docs",
26307
+ description: "Run a single pipeline stage as a standalone async job \u2014 transfer, extract, resolve, validate, or assemble \u2014 chained over one record set.",
26308
+ content: [
26309
+ { type: "paragraph", text: "Node jobs expose the individual stages of the structuring pipeline as standalone primitives. Where a Spec/pipeline runs the whole rail end to end, node jobs let you run one stage at a time \u2014 Transfer, Extraction, Resolution, Validation, or Assembly \u2014 and chain them yourself over a shared `record_set_id`. Each call is asynchronous: it returns a node-run id you poll for status and results." },
26310
+ { type: "paragraph", text: "The typical chain starts with `POST /v1/nodes/transfer` (or `extract`) against a set of `document_ids` and a `schema_id`, which creates a record set and fills cells. Each subsequent stage takes the `record_set_id` from the previous one: `extract` fills gaps the registry could not, `resolve` applies Data Policies, `validate` runs your gates, and `assemble` composes grouped documents. This gives you fine-grained control over the structuring flow when a single Spec run is too coarse." },
26311
+ { type: "callout", variant: "info", text: "Node jobs are an advanced primitive. Most integrations should run a configured Spec via POST /v1/pipelines, which compiles and runs the whole rail in one call. Reach for node jobs when you need to drive stages individually." },
26312
+ {
26313
+ type: "endpoint",
26314
+ method: "POST",
26315
+ path: "/v1/nodes/transfer",
26316
+ summary: "Fill cells deterministically from the field registry (creates a record set).",
26317
+ description: "Requires write scope. Body: schema_id, document_ids.",
26318
+ blocks: [
26319
+ {
26320
+ type: "param-table",
26321
+ title: "Body parameters",
26322
+ params: [
26323
+ { name: "schema_id", type: "string", required: true, description: "The schema to structure against." },
26324
+ { name: "document_ids", type: "string[]", required: true, description: "Documents to process." }
26325
+ ]
26326
+ }
26327
+ ]
26328
+ },
26329
+ {
26330
+ type: "endpoint",
26331
+ method: "POST",
26332
+ path: "/v1/nodes/extract",
26333
+ summary: "Run extraction for gap fields over a record set.",
26334
+ description: "Requires write scope. Body: schema_id, document_ids, record_set_id.",
26335
+ blocks: []
26336
+ },
26337
+ {
26338
+ type: "endpoint",
26339
+ method: "POST",
26340
+ path: "/v1/nodes/resolve",
26341
+ summary: "Apply Data Policies to a record set.",
26342
+ description: "Requires write scope. Body: record_set_id (+ the policies to apply).",
26343
+ blocks: []
26344
+ },
26345
+ {
26346
+ type: "endpoint",
26347
+ method: "POST",
26348
+ path: "/v1/nodes/validate",
26349
+ summary: "Run validation gates over a record set.",
26350
+ description: "Requires write scope. Body: record_set_id (+ the validation stage to run).",
26351
+ blocks: []
26352
+ },
26353
+ {
26354
+ type: "endpoint",
26355
+ method: "POST",
26356
+ path: "/v1/nodes/assemble",
26357
+ summary: "Compose grouped documents within a record set.",
26358
+ description: "Requires write scope. Body: record_set_id, grouping_field, anchor_field.",
26359
+ blocks: []
26360
+ },
26361
+ { type: "heading", level: 2, id: "node-jobs-poll", text: "Polling a node run" },
26362
+ { type: "paragraph", text: "Each stage call returns a node-run id. Poll `GET /v1/nodes/:id` for its status and progress, and `GET /v1/nodes/:id/results` for its output once it completes. Carry the `record_set_id` forward to the next stage to chain the pipeline." },
26363
+ {
26364
+ type: "endpoint",
26365
+ method: "GET",
26366
+ path: "/v1/nodes/:id",
26367
+ summary: "Get a node run's status and progress.",
26368
+ description: "Requires read scope.",
26369
+ blocks: []
26370
+ },
26371
+ {
26372
+ type: "endpoint",
26373
+ method: "GET",
26374
+ path: "/v1/nodes/:id/results",
26375
+ summary: "Get a node run's output.",
26376
+ description: "Requires read scope.",
26377
+ blocks: []
26378
+ }
26379
+ ],
26380
+ related: [
26381
+ { label: "Run a Pipeline", slug: "run-pipeline" },
26382
+ { label: "List Schemas", slug: "list-schemas" }
26383
+ ],
26384
+ faq: [
26385
+ { question: "When should I use node jobs instead of a pipeline?", answer: "Use a pipeline (POST /v1/pipelines) for the normal end-to-end run. Use node jobs when you need to drive individual stages \u2014 for example to inspect or branch between transfer, extraction, and resolution." },
26386
+ { question: "How do stages share state?", answer: "Through the record_set_id. The first stage creates a record set; pass that id to each subsequent stage so they operate on the same cells." }
26387
+ ],
26388
+ mentions: ["node jobs", "record set", "transfer", "extract", "resolve", "assemble"]
26389
+ }
26390
+ ];
26391
+
26392
+ // src/content/api/process.ts
26393
+ var sections48 = [
26394
+ {
26395
+ slug: "submit-process",
26396
+ parentSlug: "process",
26397
+ title: "Submit for Processing",
26398
+ seoTitle: "Submit for Processing \u2014 Named Operations \u2014 Talonic Docs",
26399
+ description: "Submit a document against a named processing configuration and have results delivered via webhook. Idempotent per (config, batch, file).",
26400
+ content: [
26401
+ { type: "paragraph", text: "The process endpoint is the named-operations front door: upload a document, name the processing configuration to run it through, and get results delivered asynchronously via the `process.completed` webhook. It always returns immediately (202 Accepted) with a `run_id` and a `poll_url`, so you never block on the pipeline." },
26402
+ { type: "paragraph", text: "Idempotency is automatic. The same `(config_id, batch_id, file)` combination returns the existing run instead of re-processing, so a retried upload is safe. If `batch_id` is absent, dedup applies within a 24-hour window. This makes the endpoint robust for at-least-once delivery from upstream systems." },
26403
+ { type: "callout", variant: "info", text: "This surface uses the operations scope. Configure the processing configurations (and discover their IDs) with GET /v1/configs." },
26404
+ {
26405
+ type: "endpoint",
26406
+ method: "POST",
26407
+ path: "/v1/process",
26408
+ summary: "Submit a document for processing through a named configuration.",
26409
+ description: "Requires operations scope. Multipart upload. Always returns 202; results via webhook.",
26410
+ blocks: [
26411
+ {
26412
+ type: "param-table",
26413
+ title: "Multipart form fields",
26414
+ params: [
26415
+ { name: "config_id", type: "string", required: true, description: "Processing configuration ID (from GET /v1/configs)." },
26416
+ { name: "file", type: "file", required: true, description: "Document to process. Max 500 MB." },
26417
+ { name: "batch_id", type: "string", description: "Optional batch identifier. Enables permanent dedup for this (config, batch, file) triple." },
26418
+ { name: "metadata", type: "string", description: "Optional JSON string with additional metadata." }
26419
+ ]
26420
+ }
26421
+ ]
26422
+ },
26423
+ { type: "heading", level: 2, id: "submit-process-response", text: "Response" },
26424
+ {
26425
+ type: "param-table",
26426
+ title: "Response fields (202)",
26427
+ params: [
26428
+ { name: "request_id", type: "string", description: "Identifier for this submission." },
26429
+ { name: "run_id", type: "string", description: "The run UUID to poll." },
26430
+ { name: "config_id", type: "string", description: "The configuration used." },
26431
+ { name: "status", type: "string", description: "processing." },
26432
+ { name: "poll_url", type: "string", description: "URL to poll for the run result." }
26433
+ ]
26434
+ },
26435
+ { type: "callout", variant: "info", text: "A 200 (instead of 202) means an idempotency hit \u2014 the existing run result is returned without re-processing." }
26436
+ ],
26437
+ related: [
26438
+ { label: "Get Run", slug: "get-run" },
26439
+ { label: "List Configs", slug: "list-configs" }
26440
+ ],
26441
+ faq: [
26442
+ { question: "How is idempotency handled?", answer: "The same (config_id, batch_id, file) returns the existing run rather than re-processing. Without batch_id, dedup applies within a 24-hour window." },
26443
+ { question: "How do I get results?", answer: "Results are delivered via the process.completed webhook, or poll the run via the poll_url / GET /v1/runs/:id." }
26444
+ ],
26445
+ mentions: ["process", "named operations", "idempotency", "webhook", "config"]
26446
+ },
26447
+ {
26448
+ slug: "get-run",
26449
+ parentSlug: "process",
26450
+ title: "Get Run",
26451
+ seoTitle: "Get Run \u2014 Named Operations \u2014 Talonic Docs",
26452
+ description: "Poll a processing run by id for its status and result.",
26453
+ content: [
26454
+ { type: "paragraph", text: "Retrieve a processing run by its id to read its status and, once complete, its structured result. This is the polling target for the `poll_url` returned by the process submission, for callers that prefer polling over the completion webhook." },
26455
+ {
26456
+ type: "endpoint",
26457
+ method: "GET",
26458
+ path: "/v1/runs/:id",
26459
+ summary: "Get a processing run by id.",
26460
+ description: "Requires operations scope.",
26461
+ blocks: [
26462
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "The run UUID." }] }
26463
+ ]
26464
+ }
26465
+ ],
26466
+ related: [
26467
+ { label: "Submit for Processing", slug: "submit-process" },
26468
+ { label: "List Configs", slug: "list-configs" }
26469
+ ],
26470
+ faq: [
26471
+ { question: "Should I poll or use the webhook?", answer: "Either. The process.completed webhook pushes results; polling GET /v1/runs/:id pulls them. Webhooks are preferred for scale; polling is simpler for one-off scripts." }
26472
+ ],
26473
+ mentions: ["run", "poll", "process", "status"]
26474
+ },
26475
+ {
26476
+ slug: "list-configs",
26477
+ parentSlug: "process",
26478
+ title: "Processing Configs",
26479
+ seoTitle: "Processing Configs \u2014 Named Operations \u2014 Talonic Docs",
26480
+ description: "List the named processing configurations available to your workspace and read one by id.",
26481
+ content: [
26482
+ { type: "paragraph", text: "Processing configurations are the named pipelines the process endpoint runs documents through. List them to discover the `config_id` values you pass to `POST /v1/process`, and read one by id to inspect its settings." },
26483
+ {
26484
+ type: "endpoint",
26485
+ method: "GET",
26486
+ path: "/v1/configs",
26487
+ summary: "List processing configurations.",
26488
+ description: "Requires operations scope.",
26489
+ blocks: []
26490
+ },
26491
+ {
26492
+ type: "endpoint",
26493
+ method: "GET",
26494
+ path: "/v1/configs/:id",
26495
+ summary: "Get a processing configuration by id.",
26496
+ description: "Requires operations scope.",
26497
+ blocks: [
26498
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "The configuration id." }] }
26499
+ ]
26500
+ }
26501
+ ],
26502
+ related: [
26503
+ { label: "Submit for Processing", slug: "submit-process" },
26504
+ { label: "Get Run", slug: "get-run" }
26505
+ ],
26506
+ faq: [
26507
+ { question: "Where do config IDs come from?", answer: "From GET /v1/configs. Each entry has an id you pass as config_id when submitting a document to POST /v1/process." }
26508
+ ],
26509
+ mentions: ["configs", "processing configuration", "config_id", "named operations"]
26510
+ }
26511
+ ];
26512
+
26513
+ // src/content/api/reference-data.ts
26514
+ var sections49 = [
25285
26515
  {
25286
26516
  slug: "list-reference-data",
25287
26517
  parentSlug: "reference-data",
@@ -25713,7 +26943,7 @@ V003,Initech Ltd,DE,DE987654321`
25713
26943
  ];
25714
26944
 
25715
26945
  // src/content/api/errors-rate-limits.ts
25716
- var sections46 = [
26946
+ var sections50 = [
25717
26947
  {
25718
26948
  slug: "error-format",
25719
26949
  parentSlug: "errors-rate-limits",
@@ -25865,7 +27095,7 @@ X-RateLimit-Reset: 2026-04-26T00:00:00.000Z`
25865
27095
  ];
25866
27096
 
25867
27097
  // src/content/api/usage.ts
25868
- var sections47 = [
27098
+ var sections51 = [
25869
27099
  {
25870
27100
  slug: "usage-overview",
25871
27101
  parentSlug: "usage",
@@ -26138,7 +27368,7 @@ var sections47 = [
26138
27368
  ];
26139
27369
 
26140
27370
  // src/content/api/pricing.ts
26141
- var sections48 = [
27371
+ var sections52 = [
26142
27372
  {
26143
27373
  slug: "pricing-overview",
26144
27374
  parentSlug: "pricing",
@@ -26250,7 +27480,7 @@ console.log(\`Batch: \${batchCredits} credits (\${batchCredits / pricing.cred
26250
27480
  ];
26251
27481
 
26252
27482
  // src/content/api/document-types.ts
26253
- var sections49 = [
27483
+ var sections53 = [
26254
27484
  {
26255
27485
  slug: "list-document-types",
26256
27486
  parentSlug: "document-types",
@@ -26407,7 +27637,7 @@ var sections49 = [
26407
27637
  ];
26408
27638
 
26409
27639
  // src/content/api/dialects.ts
26410
- var sections50 = [
27640
+ var sections54 = [
26411
27641
  {
26412
27642
  slug: "list-dialects",
26413
27643
  parentSlug: "dialects",
@@ -26936,7 +28166,7 @@ var sections50 = [
26936
28166
  ];
26937
28167
 
26938
28168
  // src/content/api/fields.ts
26939
- var sections51 = [
28169
+ var sections55 = [
26940
28170
  {
26941
28171
  slug: "list-fields",
26942
28172
  parentSlug: "fields",
@@ -27355,7 +28585,7 @@ var sections51 = [
27355
28585
  ];
27356
28586
 
27357
28587
  // src/content/api/data-products.ts
27358
- var sections52 = [
28588
+ var sections56 = [
27359
28589
  {
27360
28590
  slug: "list-data-products",
27361
28591
  parentSlug: "data-products",
@@ -27843,11 +29073,131 @@ var sections52 = [
27843
29073
  { question: "What does the public share link expose?", answer: "A read-only view of the assembled data product: its result rows and metadata. Visitors cannot edit the product, access other products, or authenticate into your workspace. If the share has a password, visitors must enter it to view the data." }
27844
29074
  ],
27845
29075
  mentions: ["share token", "public link", "data product", "read-only", "rotate"]
29076
+ },
29077
+ {
29078
+ slug: "create-data-product",
29079
+ parentSlug: "data-products",
29080
+ title: "Create Data Product",
29081
+ seoTitle: "Create Data Product Endpoint \u2014 Talonic Docs",
29082
+ description: "Assemble a data product from one or more completed job or resolution runs and mint its share token in a single call.",
29083
+ content: [
29084
+ { type: "paragraph", text: "Create a data product from one or more completed runs. A data product is the platform's final deliverable: an assembled, canonical dataset wrapped for handover, with quality thresholds, a share link, and CSV export. This endpoint is the run-backed create path \u2014 pass the run IDs that feed the product and it assembles them and mints a share token in one call." },
29085
+ { type: "paragraph", text: "The body requires a `name` and a non-empty `run_ids` array (the completed job or resolution runs whose results the product assembles). You may optionally pass `thresholds` to filter rows by quality \u2014 `min_confidence`, `require_validation_pass`, and `require_approval`. The response is the created product with its `id`, `run_ids`, `status`, and `links`." },
29086
+ { type: "callout", variant: "info", text: "This is the run-backed create path. Products built from a validation session or a pipeline are created through their own surfaces (the pipeline's `POST /v1/pipelines/{id}/data-product`)." },
29087
+ {
29088
+ type: "endpoint",
29089
+ method: "POST",
29090
+ path: "/v1/data-products",
29091
+ summary: "Create a run-backed data product and mint its share token.",
29092
+ description: "Requires write scope.",
29093
+ blocks: [
29094
+ {
29095
+ type: "param-table",
29096
+ title: "Body parameters",
29097
+ params: [
29098
+ { name: "name", type: "string", required: true, description: "A name for the data product." },
29099
+ { name: "run_ids", type: "string[]", required: true, description: "Completed job/resolution run UUIDs to assemble." },
29100
+ { name: "description", type: "string", description: "Optional description." },
29101
+ { name: "thresholds", type: "object", description: "Optional quality filters: min_confidence (0\u20131), require_validation_pass, require_approval." }
29102
+ ]
29103
+ }
29104
+ ]
29105
+ },
29106
+ {
29107
+ type: "code",
29108
+ title: "curl",
29109
+ language: "bash",
29110
+ code: `curl -s -X POST https://api.talonic.ai/v1/data-products \\
29111
+ -H "Authorization: Bearer tlnc_your_api_key" \\
29112
+ -H "Content-Type: application/json" \\
29113
+ -d '{
29114
+ "name": "Q2 Deliveries",
29115
+ "run_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
29116
+ "thresholds": { "min_confidence": 0.8 }
29117
+ }'`
29118
+ },
29119
+ {
29120
+ type: "code",
29121
+ title: "Response",
29122
+ code: `{
29123
+ "id": "6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655",
29124
+ "name": "Q2 Deliveries",
29125
+ "description": null,
29126
+ "run_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
29127
+ "status": "active",
29128
+ "created_at": "2026-06-27T10:00:00.000Z",
29129
+ "links": {
29130
+ "self": "/v1/data-products/6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655",
29131
+ "results": "/v1/data-products/6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655/results",
29132
+ "export_plain": "/v1/data-products/6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655/export/plain",
29133
+ "export_audit": "/v1/data-products/6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655/export/audit"
29134
+ }
29135
+ }`
29136
+ }
29137
+ ],
29138
+ related: [
29139
+ { label: "List Data Products", slug: "list-data-products" },
29140
+ { label: "Export Data Product", slug: "export-data-product" },
29141
+ { label: "Data Product Share Link", slug: "data-product-share" }
29142
+ ],
29143
+ faq: [
29144
+ { question: "What runs can feed a data product?", answer: "Completed job or resolution runs. Pass their UUIDs in run_ids. The product assembles their results into one canonical dataset." },
29145
+ { question: "Does creating a product make it public?", answer: "A share token is minted on create, but the public link is governed by the share endpoints and is password-protected. Use GET /v1/data-products/{id}/share to read it." }
29146
+ ],
29147
+ mentions: ["create data product", "run-backed", "assemble", "thresholds", "share token"]
29148
+ },
29149
+ {
29150
+ slug: "export-data-product",
29151
+ parentSlug: "data-products",
29152
+ title: "Export Data Product",
29153
+ seoTitle: "Export Data Product CSV \u2014 Talonic Docs",
29154
+ description: "Download a data product as CSV \u2014 a plain values export, or an audit export with PENDING_REVIEW markers for held pipeline cells.",
29155
+ content: [
29156
+ { type: "paragraph", text: "Export a data product's rows as a CSV file. Two variants are available: a **plain** export of the values, and an **audit** export that preserves the handover trail. Both stream the file as an attachment." },
29157
+ { type: "paragraph", text: "For a pipeline-backed product, cells still held in the review queue (`pending_approval`) are treated carefully: the plain export writes them as empty, and the audit export writes a `PENDING_REVIEW` marker so the held value is never serialized into the file. This keeps an in-flight review from leaking an unapproved value while still letting you export everything that has cleared." },
29158
+ {
29159
+ type: "endpoint",
29160
+ method: "GET",
29161
+ path: "/v1/data-products/:id/export/plain",
29162
+ summary: "Download the plain-values CSV.",
29163
+ description: "Requires read scope. Held pipeline cells are written empty.",
29164
+ blocks: [
29165
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "Data product UUID." }] }
29166
+ ]
29167
+ },
29168
+ {
29169
+ type: "endpoint",
29170
+ method: "GET",
29171
+ path: "/v1/data-products/:id/export/audit",
29172
+ summary: "Download the audit CSV (handover trail).",
29173
+ description: "Requires read scope. Held pipeline cells are written as PENDING_REVIEW.",
29174
+ blocks: [
29175
+ { type: "param-table", title: "Path parameters", params: [{ name: "id", type: "string", required: true, description: "Data product UUID." }] }
29176
+ ]
29177
+ },
29178
+ {
29179
+ type: "code",
29180
+ title: "curl",
29181
+ language: "bash",
29182
+ code: `curl -s https://api.talonic.ai/v1/data-products/6ecb46fa-24ba-4b5d-93ee-b8b6c6b97655/export/plain \\
29183
+ -H "Authorization: Bearer tlnc_your_api_key" -o data-product.csv`
29184
+ },
29185
+ { type: "callout", variant: "info", text: "The response is a CSV attachment (`text/csv`), not JSON. Both variants accept the product UUID as the path parameter." }
29186
+ ],
29187
+ related: [
29188
+ { label: "Get Data Product Results", slug: "get-data-product-results" },
29189
+ { label: "Create Data Product", slug: "create-data-product" }
29190
+ ],
29191
+ faq: [
29192
+ { question: "What is the difference between the plain and audit exports?", answer: "Plain is the values only. Audit preserves the handover trail and marks held pipeline cells as PENDING_REVIEW instead of writing them empty." },
29193
+ { question: "Why is a cell empty (plain) or PENDING_REVIEW (audit)?", answer: "That cell is still held in the field-review queue. Its value is never serialized into an export until a reviewer resolves it, so an in-flight review cannot leak an unapproved value." }
29194
+ ],
29195
+ mentions: ["export", "CSV", "audit trail", "pending review", "data product"]
27846
29196
  }
27847
29197
  ];
27848
29198
 
27849
29199
  // src/content/api/data-policies.ts
27850
- var sections53 = [
29200
+ var sections57 = [
27851
29201
  {
27852
29202
  slug: "list-data-policies",
27853
29203
  parentSlug: "data-policies",
@@ -28432,9 +29782,10 @@ var sections53 = [
28432
29782
  ],
28433
29783
  faq: [
28434
29784
  { question: "What happens to values not declared as fields?", answer: "Values computed by rules but not declared as policy fields are treated as intermediate variables and discarded from the final output. Only declared fields appear in resolution results. You can enable `emit_undeclared_cells` in pipeline config for debugging." },
28435
- { question: "Can Lua scripts write to undeclared field keys?", answer: "Yes. Lua scripts can write to any cell name as a temporary variable for intermediate computation. However, only values written to declared field keys will appear in the resolution output. Undeclared keys are silently dropped unless debugging is enabled." }
29785
+ { question: "Can Lua scripts write to undeclared field keys?", answer: "Yes. Lua scripts can write to any cell name as a temporary variable for intermediate computation. However, only values written to declared field keys will appear in the resolution output. Undeclared keys are silently dropped unless debugging is enabled." },
29786
+ { question: "Can Lua rules use regular expressions?", answer: "Yes. Lua scripts have a built-in `regex` helper \u2014 `regex.test(subject, pattern)`, `regex.match(subject, pattern)`, and `regex.replace(subject, pattern, replacement)` \u2014 backed by the RE2 engine, so matching is linear-time and immune to catastrophic backtracking (ReDoS). Pattern and subject length are capped (configurable), and an invalid pattern fails the rule loudly rather than silently." }
28436
29787
  ],
28437
- mentions: ["data policy", "transformation", "normalization", "Lua"]
29788
+ mentions: ["data policy", "transformation", "normalization", "Lua", "regex"]
28438
29789
  },
28439
29790
  {
28440
29791
  slug: "list-data-policy-rules",
@@ -28535,7 +29886,7 @@ var sections53 = [
28535
29886
  ];
28536
29887
 
28537
29888
  // src/content/api/record-sets.ts
28538
- var sections54 = [
29889
+ var sections58 = [
28539
29890
  {
28540
29891
  slug: "list-record-sets",
28541
29892
  parentSlug: "record-sets",
@@ -28995,7 +30346,7 @@ var sections54 = [
28995
30346
  ];
28996
30347
 
28997
30348
  // src/content/api/pipelines.ts
28998
- var sections55 = [
30349
+ var sections59 = [
28999
30350
  {
29000
30351
  slug: "run-pipeline",
29001
30352
  parentSlug: "pipelines",
@@ -29258,6 +30609,7 @@ var sections55 = [
29258
30609
  { type: "paragraph", text: 'Each document moves through the per-document phases sequentially: transfer, extraction, resolution, then validation checkpoints. Pipeline-scoped phases such as assembly run once after every document is terminal, so they report a single `status` and `scope: "pipeline"` instead of per-document counters. The `errors` array surfaces per-document failures, including documents that errored before reaching any phase, so no failure is invisible.' },
29259
30610
  { type: "paragraph", text: "This endpoint is the polling target after `POST /v1/pipelines`. Poll every few seconds and watch the per-phase counters advance as documents flow through the rail. When `completedDocuments` plus `errorDocuments` reaches `totalDocuments`, the run is terminal and you can produce a data product. This endpoint requires an API key with the `read` scope." },
29260
30611
  { type: "callout", variant: "info", text: 'A phase row with `scope: "pipeline"` (such as assembly) reports its own `status` field. Its per-document counters are approximations for older consumers; trust the `status` value for pipeline-scoped phases.' },
30612
+ { type: "paragraph", text: "A Spec can place Resolution or Validation **after** Assembly in the rail. These compile to pipeline-scoped tail phases that run once over the composed record: a post-assembly resolution normalizes the assembled row (and mirrors the resolved values back so the data product ships them), and a post-assembly validation gate checks the full composed row, resolution first. Like assembly, these tail phases run after every document is terminal and are not surfaced as per-document progress rows, so do not wait on them in the counters \u2014 the run is terminal once `completedDocuments` plus `errorDocuments` reaches `totalDocuments`." },
29261
30613
  {
29262
30614
  type: "endpoint",
29263
30615
  method: "GET",
@@ -29552,7 +30904,7 @@ var sections55 = [
29552
30904
  ];
29553
30905
 
29554
30906
  // src/content/api/field-reviews.ts
29555
- var sections56 = [
30907
+ var sections60 = [
29556
30908
  {
29557
30909
  slug: "list-field-reviews",
29558
30910
  parentSlug: "field-reviews",
@@ -30163,11 +31515,124 @@ dec_uuid_1,a1b2c3d4-e5f6-7890-abcd-ef1234567890,lease_oct.pdf,total_amount,gate,
30163
31515
  { question: "Is the CSV export paginated?", answer: "No. Unlike the JSON decisions list, the CSV returns the full decision history for the requested scope in one download, which is what a handover audit needs." }
30164
31516
  ],
30165
31517
  mentions: ["decision export", "CSV", "handover", "audit trail", "RFC 4180"]
31518
+ },
31519
+ {
31520
+ slug: "field-review-triage",
31521
+ parentSlug: "field-reviews",
31522
+ title: "Triage Breakdown",
31523
+ seoTitle: "Field Review Triage \u2014 Talonic Docs",
31524
+ description: "A classification-backed breakdown of the review queue: counts by reason, field, document, assignee, and tag, each group carrying a per-group reason mix.",
31525
+ content: [
31526
+ { type: "paragraph", text: "The triage endpoint returns the review queue's shape rather than its items: how the held fields break down `byReason`, `byField`, `byDocument`, `byAssignee`, and `byTag`. Each group carries a per-group reason `mix`, so you can see not just how many items a field or document has, but why they are held \u2014 disagreement, low confidence, a failed check, a missing value. It is what a review dashboard reads to draw section headers and prioritize work." },
31527
+ { type: "paragraph", text: "Triage honors the same scope filters as the queue list (`pipeline_id`, `schema_id`, `document_id`, `stage_id`, `trigger`, `older_than_hours`, `include_held`, `assignee`, and the free-text `q`), so you can scope the breakdown to one pipeline or one schema. It intentionally does NOT honor `reason` or tag filters \u2014 those facets stay the full switchable vocabulary so every reason and tag is represented." },
31528
+ { type: "callout", variant: "info", text: "Use triage to power a queue header or routing logic: the reason mix tells you which fields are mostly safe to bulk-approve versus which need a careful human look." },
31529
+ {
31530
+ type: "endpoint",
31531
+ method: "GET",
31532
+ path: "/v1/field-reviews/triage",
31533
+ summary: "Get the review-queue breakdown by reason, field, document, assignee, and tag.",
31534
+ description: "Requires read scope.",
31535
+ blocks: [
31536
+ {
31537
+ type: "param-table",
31538
+ title: "Query parameters",
31539
+ params: [
31540
+ { name: "pipeline_id", type: "string", description: "Scope to one pipeline." },
31541
+ { name: "schema_id", type: "string", description: "Scope to one schema." },
31542
+ { name: "document_id", type: "string", description: "Scope to one pipeline document." },
31543
+ { name: "stage_id", type: "string", description: "Scope to one validation stage." },
31544
+ { name: "trigger", type: "string", description: "gate | declarative." },
31545
+ { name: "assignee", type: "string", description: "me | unassigned." },
31546
+ { name: "q", type: "string", description: "Free-text filter over field key, display name, filename, assignee." },
31547
+ { name: "older_than_hours", type: "integer", description: "Only items requested before this many hours ago." },
31548
+ { name: "include_held", type: "boolean", description: "Include hold-routed items (default false)." }
31549
+ ]
31550
+ }
31551
+ ]
31552
+ },
31553
+ { type: "heading", level: 2, id: "field-review-triage-response", text: "Response" },
31554
+ {
31555
+ type: "param-table",
31556
+ title: "Response fields",
31557
+ params: [
31558
+ { name: "byReason", type: "object", description: "Counts per reason bucket (missing_value, disagreement, failed_check, low_confidence, likely_fine, other)." },
31559
+ { name: "byField", type: "object[]", description: "Per-field groups with count and reason mix." },
31560
+ { name: "byDocument", type: "object[]", description: "Per-document groups with count and reason mix." },
31561
+ { name: "byAssignee", type: "object[]", description: "Per-assignee groups (null owners folded into one unassigned bucket)." },
31562
+ { name: "byTag", type: "object[]", description: "Per-tag groups (an item joins every tag on its schema field)." }
31563
+ ]
31564
+ }
31565
+ ],
31566
+ related: [
31567
+ { label: "List Field Reviews", slug: "list-field-reviews" },
31568
+ { label: "Value Clusters", slug: "field-review-value-clusters" }
31569
+ ],
31570
+ faq: [
31571
+ { question: "How is triage different from the summary endpoint?", answer: "Summary is cheap top-level counts for a nav badge. Triage loads cells and verdicts to classify each item, so it can return the full reason mix per field, document, assignee, and tag." },
31572
+ { question: "Why does triage ignore the reason and tag filters?", answer: "Those facets are the switchable vocabulary the breakdown itself produces, so they must reflect the scope before a selection \u2014 otherwise selecting one reason would hide the others from the breakdown." }
31573
+ ],
31574
+ mentions: ["triage", "review queue", "reason mix", "by field", "by tag"]
31575
+ },
31576
+ {
31577
+ slug: "field-review-value-clusters",
31578
+ parentSlug: "field-reviews",
31579
+ title: "Value Clusters",
31580
+ seoTitle: "Field Review Value Clusters \u2014 Talonic Docs",
31581
+ description: "Distinct current values among the filtered review set (typically one field), with counts and member items \u2014 drives identical-value cluster bulk-approve.",
31582
+ content: [
31583
+ { type: "paragraph", text: 'The value-clusters endpoint groups held review items by their current value. When a single field is held across many documents with the same value, those items form one cluster you can review once and approve together. This is the data behind a "approve all 40 documents where vendor_name is Meridian Energy AG" action: one decision instead of forty.' },
31584
+ { type: "paragraph", text: "Filter to the set you want to cluster \u2014 typically a single `field_key`, optionally scoped by `pipeline_id`, `schema_id`, `document_id`, `stage_id`, `trigger`, `reason`, `assignee`, or age. Each returned cluster carries the distinct value, a count, and its member items (the document + field-key pairs), which you can hand straight to `POST /v1/field-reviews/bulk-resolve`." },
31585
+ { type: "callout", variant: "info", text: "Pair value clusters with bulk-resolve: cluster a field's held values, then bulk-approve the clusters you trust and inspect only the outliers." },
31586
+ {
31587
+ type: "endpoint",
31588
+ method: "GET",
31589
+ path: "/v1/field-reviews/value-clusters",
31590
+ summary: "Group held review items by their current value.",
31591
+ description: "Requires read scope.",
31592
+ blocks: [
31593
+ {
31594
+ type: "param-table",
31595
+ title: "Query parameters",
31596
+ params: [
31597
+ { name: "field_key", type: "string", description: "Cluster a single field (the common case)." },
31598
+ { name: "pipeline_id", type: "string", description: "Scope to one pipeline." },
31599
+ { name: "schema_id", type: "string", description: "Scope to one schema." },
31600
+ { name: "document_id", type: "string", description: "Scope to one pipeline document." },
31601
+ { name: "stage_id", type: "string", description: "Scope to one validation stage." },
31602
+ { name: "trigger", type: "string", description: "gate | declarative." },
31603
+ { name: "reason", type: "string", description: "Restrict to one reason bucket." },
31604
+ { name: "assignee", type: "string", description: "me | unassigned." },
31605
+ { name: "older_than_hours", type: "integer", description: "Only items requested before this many hours ago." },
31606
+ { name: "include_held", type: "boolean", description: "Include hold-routed items (default false)." }
31607
+ ]
31608
+ }
31609
+ ]
31610
+ },
31611
+ { type: "heading", level: 2, id: "field-review-value-clusters-response", text: "Response" },
31612
+ {
31613
+ type: "param-table",
31614
+ title: "Response fields",
31615
+ params: [
31616
+ { name: "clusters[].value", type: "string", description: "The distinct current value shared by the cluster." },
31617
+ { name: "clusters[].count", type: "integer", description: "Number of held items with this value." },
31618
+ { name: "clusters[].items", type: "object[]", description: "Member items (pipeline document id + field key) for bulk-resolve." }
31619
+ ]
31620
+ }
31621
+ ],
31622
+ related: [
31623
+ { label: "Bulk Resolve Field Reviews", slug: "bulk-resolve-field-reviews" },
31624
+ { label: "Triage Breakdown", slug: "field-review-triage" }
31625
+ ],
31626
+ faq: [
31627
+ { question: "How do I act on a cluster?", answer: "Pass the cluster's member items to POST /v1/field-reviews/bulk-resolve with an action (approve, correct, or override). Each item runs through the single-field resolve path, so the decision log and promotion behave identically." },
31628
+ { question: "Should I cluster across multiple fields at once?", answer: "Usually no. Clustering is most useful per field_key \u2014 identical values mean the same thing within one field. Across fields, the same string can mean different things." }
31629
+ ],
31630
+ mentions: ["value clusters", "bulk approve", "review queue", "identical values"]
30166
31631
  }
30167
31632
  ];
30168
31633
 
30169
31634
  // src/content/api/spec-config.ts
30170
- var sections57 = [
31635
+ var sections61 = [
30171
31636
  {
30172
31637
  slug: "get-spec-rail",
30173
31638
  parentSlug: "spec-config",
@@ -31159,7 +32624,7 @@ var sections57 = [
31159
32624
  ];
31160
32625
 
31161
32626
  // src/content/api/reconciliation.ts
31162
- var sections58 = [
32627
+ var sections62 = [
31163
32628
  {
31164
32629
  slug: "analyze-reconciliation",
31165
32630
  parentSlug: "reconciliation",
@@ -31731,7 +33196,7 @@ var sections58 = [
31731
33196
  ];
31732
33197
 
31733
33198
  // src/content/api/matching-packages.ts
31734
- var sections59 = [
33199
+ var sections63 = [
31735
33200
  {
31736
33201
  slug: "create-matching-package-config",
31737
33202
  parentSlug: "matching-packages",
@@ -32624,7 +34089,7 @@ talonic --help` }
32624
34089
  ];
32625
34090
 
32626
34091
  // src/content/sdk/index.ts
32627
- var sections60 = sections_default;
34092
+ var sections64 = sections_default;
32628
34093
 
32629
34094
  // src/content/mcp/sections.json
32630
34095
  var sections_default2 = [
@@ -35119,7 +36584,7 @@ var sections_default2 = [
35119
36584
  ];
35120
36585
 
35121
36586
  // src/content/mcp/index.ts
35122
- var sections61 = sections_default2;
36587
+ var sections65 = sections_default2;
35123
36588
 
35124
36589
  // src/content/index.ts
35125
36590
  var ALL_PLATFORM_RAW = [
@@ -35170,10 +36635,10 @@ var ALL_API_RAW = [
35170
36635
  ...sections43,
35171
36636
  ...sections44,
35172
36637
  ...sections45,
36638
+ ...sections46,
35173
36639
  ...sections47,
35174
36640
  ...sections48,
35175
36641
  ...sections49,
35176
- ...sections50,
35177
36642
  ...sections51,
35178
36643
  ...sections52,
35179
36644
  ...sections53,
@@ -35183,10 +36648,14 @@ var ALL_API_RAW = [
35183
36648
  ...sections57,
35184
36649
  ...sections58,
35185
36650
  ...sections59,
35186
- ...sections46
36651
+ ...sections60,
36652
+ ...sections61,
36653
+ ...sections62,
36654
+ ...sections63,
36655
+ ...sections50
35187
36656
  ];
35188
- var ALL_SDK_RAW = [...sections60];
35189
- var ALL_MCP_RAW = [...sections61];
36657
+ var ALL_SDK_RAW = [...sections64];
36658
+ var ALL_MCP_RAW = [...sections65];
35190
36659
  function enrich(raw, navSections, domain) {
35191
36660
  return raw.map((r) => {
35192
36661
  const { prev, next } = derivePrevNext(navSections, r.slug);