Extraction Overrides
Adjust column instructions, table instructions, and cell values for a single extraction request.
Every document processing request accepts three optional body fields that change how that one extraction behaves, without touching the deployment's saved schema. They are the way to handle a document that does not match the general case — a supplier who labels a field differently, a value you already hold in your own system, a single bad document worth re-running with a sharper instruction.
Overrides apply to the request that carries them. The deployment's columns, descriptions and instructions are unchanged, and every other document keeps extracting as before.
They are accepted by all three processing endpoints:
All three fields are optional, and a request body of {} is still valid.
When to use them
- A batch from one supplier labels a field differently. Their invoices print
Tax Point Datewhere every other supplier printsInvoice Date. Sharpen that column's description for their files only, instead of writing supplier-specific wording into the schema everyone shares. - You already know a value. Your own system knows which supplier, contract or site a document belongs to. Supply it directly rather than asking the model to find it — it is exact, and the column is dropped from the extraction prompt, so the model spends its attention elsewhere.
- One document came back wrong. Re-run that file with a more specific instruction for the column that failed, without changing the deployment for the thousands of documents that were fine.
Body parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
columnDescriptions | object | body | No | Replacement extraction instructions, keyed by column ID. Stored on the file's job and reapplied on a later run that sends none — see Overrides persist across re-runs. |
tableInstructions | object | body | No | Replacement table instructions, keyed by table ID. Stored on the file's job and reapplied on a later run that sends none — see Overrides persist across re-runs. |
columnsOverride | object[] | body | No | Values you supply yourself. Each array item is a single-key object mapping one column ID to its value. Overridden columns are not extracted; your value is written into the cell. |
columnDescriptions
An object keyed by column ID, whose values are strings. Each one replaces that column's saved description — the instruction the model reads for that column — for this run. A value has to be a non-empty string: an empty string is ignored and the column keeps its saved description.
{
"columnDescriptions": {
"col_001": "The date this invoice covers. This supplier labels it Tax Point Date in the header block, above the address."
}
}Column IDs come from the Configuration endpoint.
tableInstructions
An object keyed by table ID, whose values are strings. Each one replaces that table's saved instructions for this run. As with columnDescriptions, a value has to be a non-empty string: an empty string is ignored and the table keeps its saved instructions.
{
"tableInstructions": {
"tbl_002": "One row per line item. Skip subtotal and carried-forward lines."
}
}The instruction reaches the model for your deployment's primary table and for nested child tables. For guidance about the document as a whole rather than one table, set the deployment's own instructions field instead — see Configuration.
columnsOverride
An array of single-key objects. Each object maps one column ID to the value you want in that column's cells. Two objects, not one object with two keys:
{
"columnsOverride": [
{ "col_002": "SUP-4471" },
{ "col_004": "EUR" }
]
}Values may be strings, numbers, booleans, null, or arrays of those. An overridden column is removed from the set the model is asked to extract, and your value is written into the cell instead.
Sending one object with several keys is rejected with 400 by the single-file endpoints. Use the one-key-per-object form everywhere.
Bulk requests
Process Files (Bulk Async) accepts the same three fields, applied to every file in the request, plus two per-file variants.
| Name | Type | In | Required | Description |
|---|---|---|---|---|
columnsOverrideByFileId | object | body | No | Per-file value overrides, keyed by file ID. For a file present here, this replaces the request-level columnsOverride entirely. |
columnDescriptionsByFileId | object | body | No | Per-file description overrides, keyed by file ID. For a file present here, this replaces the request-level columnDescriptions entirely. |
A per-file entry replaces the request-level field for that file rather than merging with it — a file listed in columnsOverrideByFileId gets exactly the overrides listed there, and none of the request-level ones.
There is no per-file variant of tableInstructions. When set on a bulk request it applies to the whole batch.
A table instruction is capped at 10,000 characters on every processing endpoint — the same cap that applies to a table's saved instructions. A longer value is rejected with a 400.
The bulk endpoint enforces these additional limits:
| Limit | Value |
|---|---|
| Files per request | 250 |
columnsOverride entries | 500 |
| Values in an array override | 100 |
| Description length | 2,000 characters |
Precedence
A cell's final value is resolved in this order:
- The value you supplied in
columnsOverride, if the column is overridden. - The value the model extracted.
- The column's configured
defaultValue, when default values are enabled and the extracted value is empty.
The instruction the model reads for a column is resolved in this order:
- The per-request
columnDescriptionsentry for that column. - The column's saved
description.
Gotchas and limits
Overrides persist across re-runs
This is the one that catches people. Overrides are stored on the document's extraction job record, not on the request. When you re-extract a file and send no overrides, the previous run's overrides are silently applied again.
Omitting a field preserves what is stored for it. So does a request body of {}, since it supplies no field at all, and so does re-processing the document from the app, which sends no overrides.
To clear a field, send its empty container explicitly:
{
"columnDescriptions": {},
"tableInstructions": {},
"columnsOverride": []
}An empty container is a value, so it is written over what was stored and the file goes back to extracting on the deployment's own configuration. Clear the fields you want reset — anything you leave out keeps its stored overrides.
Each field is replaced whole rather than merged. Sending columnDescriptions with one column in it discards the stored overrides for every other column, so send the full set you want to keep, not just the entry you are changing.
Assume that anything you override on a file stays overridden on that file until you overwrite or explicitly clear it.
Not every table carries its instructions into the prompt
Instructions reach the model for the primary table and for nested child tables. A table that is neither — a second top-level table with no parent — is not represented in the prompt, and an instruction set on it has no effect.
For guidance that applies to the document as a whole rather than to one table, use the deployment's instructions field (see Configuration).
Unknown IDs fail silently
Nothing checks that a column ID or table ID exists, or that it belongs to the deployment you are calling. A typo is accepted, the request returns 200, and the override does nothing. Read your IDs from the configuration endpoint rather than typing them.
An empty string fails just as quietly. It is skipped rather than applied, so it never blanks a column's description or a table's instructions. To remove overrides rather than replace them, see Overrides persist across re-runs.
Overriding every column extracts them all anyway
Overridden columns are normally dropped from the extraction prompt. If you override every extractable column, that filter would leave nothing to extract, so it falls back to the full set and the model is asked for all of them. Your supplied values still win in the resulting cells, but the extraction runs as though nothing was excluded.
Description overrides apply to the standard extraction engine
columnDescriptions takes effect on the standard extraction engine, which is what most documents use. Documents routed to the extraction agent do not apply description overrides the same way — Excel files always take that route, as do deployments configured for agent or dynamic-mapping extraction. columnsOverride values are still written for those documents.
If a description override appears to have no effect, check which engine the deployment is configured for and what file type you sent.
Send well-formed JSON
The two single-file endpoints treat a body they cannot parse as no body at all: the request succeeds and every override is dropped. A malformed body does not produce an error you can see.
Worked example
An invoice deployment with one primary table and the following columns:
| ID | Column | Saved description |
|---|---|---|
col_001 | Invoice Date | The date printed on the invoice. |
col_002 | Supplier ID | The supplier identifier. |
col_003 | Total Amount | The invoice total including tax. |
Three files have arrived from a supplier whose invoices print Tax Point Date rather than Invoice Date. You already know the supplier from your own system, so there is no reason to have the model look for it.
1. Queue the batch with overrides
The description override sharpens col_001 for these files. The value override fills col_002 directly, which also removes it from the extraction prompt.
const response = await fetch(
`https://app.limai.io/api/v1/document/${SCHEMA_ID}/process-files-async`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
fileIds: [
"file_abc123def456",
"file_def456ghi789",
"file_ghi789jkl012"
],
columnDescriptions: {
col_001: "The date this invoice covers. This supplier labels it Tax Point Date in the header block, above the address."
},
columnsOverride: [
{ col_002: "SUP-4471" }
]
})
}
)
const { results, summary } = await response.json(){
"extractionSchemaId": "schema_abc123",
"results": {
"file_abc123def456": {
"status": "queued",
"jobId": "job_001"
},
"file_def456ghi789": {
"status": "queued",
"jobId": "job_002"
},
"file_ghi789jkl012": {
"status": "queued",
"jobId": "job_003"
}
},
"summary": {
"queued": 3,
"errors": 0,
"total": 3
}
}2. Poll for the result
Poll get-file-data per file, passing that file's jobId.
const response = await fetch(
"https://app.limai.io/api/v1/document/get-file-data?fileId=file_abc123def456&jobId=job_001",
{ headers: { "Authorization": `Bearer ${API_TOKEN}` } }
)
const data = await response.json()Invoice Date was found under the supplier's own wording, and Supplier ID carries the value you supplied rather than anything read from the page.
{
"status": "COMPLETED",
"message": "File data retrieved successfully.",
"fileId": "file_abc123def456",
"extractionSchemaId": "schema_abc123",
"deployment": {
"id": "dep_abc123",
"name": "Invoice Extraction v1"
},
"data": {
"tables": {
"tbl_001": {
"id": "tbl_001",
"name": "Invoices",
"slug": "invoices",
"columns": [
{
"id": "col_001",
"name": "Invoice Date",
"type": "DATE",
"slug": "invoice_date"
},
{
"id": "col_002",
"name": "Supplier ID",
"type": "TEXT",
"slug": "supplier_id"
},
{
"id": "col_003",
"name": "Total Amount",
"type": "NUMBER",
"slug": "total_amount"
}
],
"rows": [
{
"id": "row_1",
"index": "0",
"status": "PENDING",
"cells": {
"Invoice Date": {
"value": "2026-08-14",
"columnId": "col_001",
"metadata": {
"id": "col_001",
"type": "DATE",
"description": "The date printed on the invoice.",
"slug": "invoice_date"
}
},
"Supplier ID": {
"value": "SUP-4471",
"columnId": "col_002",
"metadata": {
"id": "col_002",
"type": "TEXT",
"description": "The supplier identifier.",
"slug": "supplier_id"
}
},
"Total Amount": {
"value": "1284.50",
"columnId": "col_003",
"metadata": {
"id": "col_003",
"type": "NUMBER",
"description": "The invoice total including tax.",
"slug": "total_amount"
}
}
}
}
]
}
}
}
}3. Re-run one file with a changed override
The supplier turns out to have a separate EU entity, so file_abc123def456 should carry SUP-4471-EU. You also want that file's Invoice Date instruction back to the deployment's normal wording.
Both of these have to be sent explicitly. Re-processing the file with no body would silently re-apply SUP-4471 and the sharpened description, because overrides are stored on the job. Send the new value for col_002, and an empty columnDescriptions object to clear the description override so col_001 reads the deployment's saved wording again.
const response = await fetch(
`https://app.limai.io/api/v1/document/${SCHEMA_ID}/process-file-async/file_abc123def456`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
columnsOverride: [
{ col_002: "SUP-4471-EU" }
],
columnDescriptions: {}
})
}
)
const { jobId } = await response.json(){
"message": "Job queued for processing",
"fileId": "file_abc123def456",
"jobId": "job_001"
}Re-processing reuses the file's existing jobId and flips it back to PROCESSING, so poll it the same way as in step 2.
Related
- Process File (Sync)
- Process File (Async)
- Process Files (Bulk Async)
- Get File Data
- Configuration — where to read column and table IDs, and where to set instructions and descriptions permanently