Skip to main content

Reference Tables

Reference tables map human-readable values to system codes. Each table is a list of key-value pairs where key = output code and value = label. For example, a country reference table might map "United States" to US, "Germany" to DE, and "United Kingdom" to GB. During extraction, a 3-step lookup cascade runs automatically against the table to normalize extracted values to your canonical codes:

Lookup cascade

ParameterTypeDescription
Step 1normalizationExact match after lowercasing, stripping punctuation and legal suffixes (Ltd, GmbH, Inc, LLC and similar), then a retry with parenthetical text removed. Payment terms also match on day count, so "Net 30" finds "30 days". Free, instant. Confidence: 0.95.
Step 2fuzzyToken-overlap matching for name variations (e.g., "Northwind Energy UK Ltd" vs "Northwind Energy Trading"). Accepts a Jaccard score at or above the threshold (default 0.5), strong containment, or a multi-word key fully contained in the value. Free. Confidence: match score × 0.85.
Step 3AI fallbackAI maps value to code when fuzzy matching fails. Last resort. Confidence: the model's rating × 0.75.

To set up a reference table, upload a CSV or manually enter key-value pairs where the key is the code you want in your output and the value is the human-readable label found in documents. During extraction, the system tries each step in order — most values resolve instantly at Step 1, so keeping your labels clean and consistent dramatically improves both speed and accuracy.

Reference tables are used in two Job phases. In Phase 1, the lookup cascade runs as part of the resolve step, mapping extracted labels to codes without any AI calls (Step 1 and Step 2). In Phase 3, the cascade runs again on values produced by Phase 2's AI extraction, normalizing free-text AI output to your canonical codes. This two-pass approach ensures maximum code coverage across the entire Job.

For example, consider a "Contract Type" field with a reference table mapping codes to labels: std_master = "Master Agreement", std_service = "Service Agreement", std_nda = "Non-Disclosure Agreement". When the AI extracts "Frame Agreement" from a document, the Phase 3 lookup cascade normalizes it: Step 1 finds no exact match, Step 2 fuzzy matching shares only the token "agreement" with "Master Agreement", a Jaccard score of 0.33, below the default 0.5 threshold, so Step 3 AI fallback can map it to std_master at a lower confidence. Adding "Frame Agreement" as a synonym pointing to std_master would promote this to a Step 1 match (0.95 confidence) in future runs.

For best results, include common variations and abbreviations as separate value entries all pointing to the same key. For example, if your code is US, add values for "United States", "USA", "U.S.A.", and "United States of America". The more variations you cover, the more values resolve at Step 1 (highest confidence) without falling through to fuzzy or AI matching.

Example reference table CSV (upload in the schema field editor)
key,value
std_master,Master Agreement
std_master,Frame Agreement
std_service,Service Agreement
std_service,Service Contract
std_nda,Non-Disclosure Agreement
std_nda,NDA
std_nda,Confidentiality Agreement
  1. Open the field in the schema editor and choose Reference table.
  2. Upload the CSV, or enter the key-value pairs manually.
  3. Run a Test Extraction on a few documents and check that extracted labels resolve to the expected codes.
  4. After production runs, review values flagged lookup_failed and add them as new value rows pointing to the right key.

Reference fields also support multi-hop resolution chains. Each step names a reference, a join_key column to look the current value up in, and an output_key column whose value is passed to the next step. This is useful when your data needs to traverse multiple mapping layers, such as resolving a local product code to a regional code and then to a global SKU. Chain steps are case-insensitive exact lookups, not the fuzzy cascade, and the chain stops with no value as soon as one step misses. The reference versions used are pinned when the run starts, so a run is not affected by edits made while it executes.

Reference table quality directly determines lookup accuracy. Review the lookup_failed validation flag in Phase 3 results to identify values that could not be mapped — these are candidates for adding new entries to your table.
Large reference tables are keyed-access only in scripts. A table over 20,000 rows or 64 MiB is never loaded into a resolution script whole: lookup[name]:exact(col, val) and :where(col, val) still work and return up to 100 rows per call, while whole-table operations such as :fuzzy() and :all() raise a "keyed-access only" error. See the data-policy rules reference in the API docs for details.

Inline tables and linked reference datasets

A field's lookup table can live in two places. An inline table is stored on the field itself and is frozen into the run snapshot when a run is created, so re-running that run reproduces exactly the same lookups. A linked table points at a reference dataset by ID and is re-read from the dataset on every execution, so a re-run picks up rows you added since. Linked datasets suit lists that change often, such as supplier or product masters, and let you correct a mapping and re-run resolution without re-extracting documents. Data Policy lookup rules use the same linked datasets through their reference_table_id setting.

Reference datasets can be created from the app or through the public API with POST /v1/reference-data, which takes a name and a non-empty data array of row objects. The column list is derived from the keys of the first row. To use a dataset as a field lookup table, give every row a key column (the output code) and a value column (the label to match). Rows without a key are skipped, and a row without a value uses its key as the label. Extra columns are kept and can be used by a row filter.

Create a reference dataset through the API
curl -X POST https://api.talonic.com/v1/reference-data \
  -H "Authorization: Bearer $TALONIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Contract types",
    "data": [
      { "key": "std_master",  "value": "Master Agreement" },
      { "key": "std_master",  "value": "Frame Agreement" },
      { "key": "std_service", "value": "Service Agreement" },
      { "key": "std_nda",     "value": "Non-Disclosure Agreement" }
    ]
  }'

# Response
{
  "id": "5f0c2a9e-3b1d-4c7a-9e2f-8d6b1a0c4e77",
  "name": "Contract types",
  "source_type": "api",
  "row_count": 4,
  "columns": [
    { "name": "key", "type": "string" },
    { "name": "value", "type": "string" }
  ],
  "created_at": "2026-10-06T09:12:00.000Z",
  "links": {
    "self": "/v1/reference-data/5f0c2a9e-3b1d-4c7a-9e2f-8d6b1a0c4e77",
    "rows": "/v1/reference-data/5f0c2a9e-3b1d-4c7a-9e2f-8d6b1a0c4e77/rows"
  }
}

To check what a dataset holds, GET /v1/reference-data/{id}/rows returns rows in upload order, 100 per page by default and at most 500 per page via limit, and GET /v1/reference-data/{id}/csv downloads the whole dataset as a CSV file. There is no row-level edit endpoint: to change a dataset through the API, create a new one and point the field at it, or edit it in the app. Deleting a dataset with DELETE /v1/reference-data/{id} removes it for every field and policy that links to it, so check those first.

Two field settings control how strict a lookup is. A minimum lookup confidence rejects matches whose raw score falls below it; the gate compares the raw match score rather than the stored confidence, so a strict setting such as 0.85 still lets a strong fuzzy match through. A row filter narrows a large table per document before the cascade runs: it names a column in the dataset and a value, either a literal such as DE or a placeholder like {field.country} read from the same document. If the placeholder resolves to an empty value, the filtered table is empty and the lookup returns nothing, which is safer than picking a code from the wrong country.

Frequently asked questions

What are reference tables?+
Reference tables map human-readable values to system codes using key-value pairs. For example, mapping country names to ISO codes. They use a 3-step lookup cascade: normalization, fuzzy matching, and AI fallback.
How accurate are reference table lookups?+
Accuracy depends on how completely the table covers the variants in your documents. The cascade provides confidence scores: 0.95 for an exact normalized match, the token-overlap score times 0.85 for a fuzzy match, and the model's rating times 0.75 for the AI fallback.
How should I format my reference table CSV?+
Use two columns: the first column is the key (output code) and the second is the value (human-readable label). Include common variations and abbreviations as separate rows pointing to the same key for maximum Step 1 hit rate.
Can I use multiple reference tables in a resolution chain?+
Yes. Resolution chains allow multi-hop lookups where the output of one reference table becomes the input for the next. This is useful for complex mapping scenarios — for example, resolving a local product code to a regional code and then to a global SKU. Each hop is a case-insensitive exact lookup on the step's join_key column, and the chain returns no value if any hop misses.
What is the difference between an inline table and a linked reference dataset?+
An inline table is stored on the field and frozen into each run snapshot, so re-runs reproduce the same lookups. A linked dataset is referenced by ID and re-read on every execution, so updates apply to the next run or re-resolution without re-extracting documents.
Can I limit a lookup to rows that fit the current document?+
Yes. A row filter names a dataset column and a value, either a literal or a {field.X} placeholder read from the same document. Only matching rows (compared case-insensitively) take part in the lookup, and an empty placeholder value means no row is eligible.