Get File Data
Retrieve extracted data and processing status for a single file.
/api/v1/document/get-file-dataRetrieve processed document data and check processing status. This endpoint supports both synchronous and asynchronous workflows, returning either the extracted data or processing status based on the job state.
Use this endpoint to:
- Poll for results after submitting an async processing job
- Fetch extracted data using metadata from webhook events
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
fileId | string | query | Yes | The file ID from the upload response or webhook event payload. |
extractionSchemaId | string | query | No | Optional. The model's ID or the model's extraction schema ID; validates that the file belongs to it. A deployment ID does not match and returns 400. |
jobId | string | query | No | Optional. Validate the read against a specific extraction job (the jobId returned by process-file-async) — recommended when polling a job you just queued. A jobId that does not exist or does not belong to this file and deployment returns 404 Extraction job not found; an explicitly empty jobId returns 400. |
include | string | query | No | Comma-separated list of optional enrichments to include: confidence, boundingBoxes, validations, metadata. Omit for the plain data payload. |
Status Semantics
The status is a promise about the body in the same response:
COMPLETEDguarantees the extracted data is present and readable in this response. A successful extraction that produced no rows still returnsCOMPLETEDwith an emptydata.tables— completion is gated on the job's write being committed, never on the row count. You never need a settle delay or an "empty result" re-poll: if the status isCOMPLETED, the data you see is the data.- A job that is queued but not yet started reports
PROCESSING(notCOMPLETEDwith stale or empty data). Keep polling. - A job that stalled reports
FAILEDwith the messageProcessing stalled, rather than a partialCOMPLETED. Stall detection is a watchdog verdict, not always the job's last word: a run that was wrongly declared stalled (for example during a transient infrastructure hiccup) can still finish, flipping aFAILEDpoll toCOMPLETEDon a later read.
When you re-process a file, its extraction job resets and reports PROCESSING again until the new run finishes — with the same jobId as before. To validate that you are polling the job you queued, pass its jobId (see below).
Optional Enrichments
By default the response contains only the extracted values. Pass include to enrich each cell (and, for validations, the file envelope):
confidence— adds aconfidenceobject to every cell withoverall,llm,layout, andconsensusscores (0–1,nullwhen not computed).boundingBoxes— adds aboundingBoxesarray to every cell. Each box is{"page": "2", "bbox": [yMin, xMin, yMax, xMax]}in normalized 0–1000 coordinates with the origin at the top-left.nullwhen boxes were never computed for the cell.validations— adds avalidationsarray to every cell and a file-levelvalidationsobject carrying the document'sstatus, itsfailCount, the custom validation results (checks) and the validation rule results (rules). See Validations.metadata— adds a top-level_metablock describing the document the data came from, besidefileIdandstatus. See Metadata.
Every cell's metadata also carries the column slug, alongside the table-level and column-list slugs, so responses can be consumed by stable slug instead of display name.
Note: documents processed through the synchronous process-file endpoint skip the confidence and bounding-box stages, so those fields stay null for such files even when requested. Use the async processing endpoints when you need them.
Request
const response = await fetch(
`https://app.limai.io/api/v1/document/get-file-data?fileId=${fileId}`,
{
headers: { "Authorization": `Bearer ${API_TOKEN}` }
}
)
const data = await response.json()Job-scoped polling
After process-file-async, pass the returned jobId when polling. A document has one extraction job per model: re-processing resets that same record back to PROCESSING and returns the same jobId again, so a job-scoped read and a plain read of the same file always agree on status. What jobId buys is validation — you are provably reading the job you queued, and a typo, a stale ID, or a mix-up between files fails loudly with 404 Extraction job not found instead of silently reporting another document's status.
Two consequences worth knowing:
- Re-queuing a completed document flips its (unchanged)
jobIdback toPROCESSING— a poller holding that ID correctly starts waiting again. - A
jobIdcan stop existing: moving a file between models or cancelling a job with record deletion removes it, so a poll can start returning404mid-flight. Treat that as terminal for the ID and fall back to polling withoutjobId.
const response = await fetch(
`https://app.limai.io/api/v1/document/get-file-data?fileId=${fileId}&jobId=${jobId}`,
{
headers: { "Authorization": `Bearer ${API_TOKEN}` }
}
)
const data = await response.json()Response Types
The response format depends on the processing state of the file.
PROCESSING
The job is still running. Continue polling.
{
"status": "PROCESSING",
"fileId": "file_abc123def456",
"jobId": "job_789xyz012"
}CLASSIFYING
The file is being classified (when using async classification).
{
"status": "CLASSIFYING",
"fileId": "file_abc123def456",
"message": "Document is being classified"
}COMPLETED
Processing finished successfully. Contains the full extracted data.
{
"status": "COMPLETED",
"message": "File data retrieved successfully.",
"fileId": "file_abc123def456",
"extractionSchemaId": "schema_xyz789",
"deployment": {
"id": "dep_abc123",
"name": "Invoice Extraction v1"
},
"data": {
"tables": {
"table_id_1": {
"id": "table_id_1",
"name": "Invoices",
"slug": "invoices",
"columns": [
{
"id": "col_1",
"name": "Invoice Number",
"type": "TEXT",
"slug": "invoice_number"
}
],
"rows": [
{
"id": "row_1",
"index": "0",
"status": "PENDING",
"cells": {
"Invoice Number": {
"value": "INV-001",
"columnId": "col_1",
"metadata": {
"id": "col_1",
"type": "TEXT",
"description": null,
"slug": "invoice_number"
}
}
}
}
]
}
}
}
}deployment names the model the document belongs to — its id and name — and extractionSchemaId that model's extraction schema ID. This read is not addressed through a deployment, so it reports no deployment pointer; the pointer a document arrived through is on the webhook payloads instead.
COMPLETED with enrichments
With include=confidence,boundingBoxes,validations, each cell carries the requested extras and the envelope gains a file-level validations object.
{
"status": "COMPLETED",
"fileId": "file_abc123def456",
"data": {
"tables": {
"table_id_1": {
"rows": [
{
"id": "row_1",
"cells": {
"Invoice Number": {
"value": "INV-001",
"columnId": "col_1",
"metadata": {
"id": "col_1",
"type": "TEXT",
"description": null,
"slug": "invoice_number"
},
"confidence": {
"overall": 0.94,
"llm": 0.95,
"layout": 0.91,
"consensus": null
},
"boundingBoxes": [
{
"page": "1",
"bbox": [
120,
45,
160,
300
]
}
],
"validations": [
{
"checkKey": "invoice-number-format",
"source": "SCRIPT",
"passed": true,
"message": "Matches expected format",
"expected": null,
"actual": null,
"name": null,
"scope": null,
"operator": null,
"amended": false,
"previousValue": null,
"amendedValue": null
},
{
"checkKey": "vrule_2a8",
"source": "RULE",
"passed": true,
"message": null,
"expected": "non-empty value",
"actual": "INV-001",
"name": "Invoice Number is not empty",
"scope": "FIELD",
"operator": "IS_NOT_EMPTY",
"amended": false,
"previousValue": null,
"amendedValue": null
}
]
}
}
}
]
}
}
},
"validations": {
"status": "failed",
"failCount": 1,
"checks": [
{
"key": "invoice-number-format",
"name": "Invoice number format",
"source": "STATIC",
"status": "PASSED",
"behavior": "SURFACE",
"findings": [],
"errorMessage": null,
"startedAt": "2026-07-21T10:00:00.000Z",
"completedAt": "2026-07-21T10:00:05.000Z"
}
],
"rules": {
"crossField": [
{
"ruleId": "vrule_9k1",
"passed": false,
"expected": "1250.00",
"actual": "1180.00",
"name": "Net Amount vs Gross Amount",
"scope": "CROSS_FIELD",
"operator": "EQUALS",
"message": "Net Amount vs Gross Amount: expected 1250.00, got 1180.00"
}
],
"row": [],
"document": []
}
}
}FAILED
Processing failed. Check the error message for details.
{
"status": "FAILED",
"fileId": "file_abc123def456",
"jobId": "job_789xyz012",
"errorMessage": "Document format not supported or file is corrupted"
}Validations
include=validations reports both kinds of check: the model's validation rules and its custom validations (scripts and validator agents). Validations explains the difference, how a rule's name and message are built, and what the status values mean.
Per-cell entries
| Field | Type | Description |
|---|---|---|
checkKey | string | null | The rule's id for RULE entries, the script's key for SCRIPT and AGENT entries |
source | RULE | SCRIPT | AGENT | Which system produced the entry |
passed | boolean | Whether the check held for this cell |
message | string | null | For rules, the composed failure detail, null when the rule passed. For scripts and agents, the finding's own message |
expected / actual | string | null | What the check required and what it saw |
name | string | null | The rule's name or generated label. null for SCRIPT and AGENT entries, whose name is on the matching checks[] entry |
scope | string | null | FIELD or COLUMN for a rule evaluated on this cell; null for custom checks and for rules that no longer exist |
operator | string | null | The rule's operator; null for custom checks |
amended | boolean | true once a RESEND check has corrected this cell's value. Always false for rules |
previousValue / amendedValue | any | null | The value before and after a RESEND correction. null unless amended is true |
The file-level object
| Field | Type | Description |
|---|---|---|
status | pending | passed | failed | error | null | The document's validation status across rules and custom validations |
failCount | number | null | Failing checks on the document, rules and custom checks together. null before anything has been evaluated |
checks | array | One entry per custom validation, with its key, name, source, status, behavior, findings and timings |
rules | object | null | Cross-field, row and document rule results, grouped by scope. null when no rule results are stored for the document |
Each entry of rules.crossField and rules.document is {ruleId, passed, expected, actual, name, scope, operator, message}. Each entry of rules.row is {ruleId, passed, rowResults, name, scope, operator, message}, with one {rowId, passed, expected, actual} per row. Field and column rule results are on the cells instead.
A rule deleted since its results were written is still listed, with scope and operator set to null and a name derived from the stored result.
The buckets are empty when the model has no validation rules, when the document was extracted before its model had any, and when rule evaluation failed — in that last case status is error.
Metadata
include=metadata adds a reserved _meta key to the response envelope, beside fileId, status and data. It describes the document itself, not its cells, so it is the one enrichment that leaves the extracted values untouched.
{
"status": "COMPLETED",
"fileId": "file_abc123def456",
"extractionSchemaId": "schema_xyz789",
"data": {
"tables": {}
},
"_meta": {
"documentId": "cmm3k7r2v0007",
"fileName": "broker-quote-2026-09.pdf",
"submittedAt": "2026-09-01T10:12:33.000Z",
"submittedBy": {
"id": "usr_8a2f",
"name": "Jane Doe",
"email": "jane@acme.com"
},
"extractedAt": "2026-09-01T10:14:02.000Z"
}
}Fields of _meta:
| Field | Description |
|---|---|
documentId | The file's ID. |
fileName | The uploaded file name. |
submittedAt | When the file was uploaded, shown as "Date Added" in the grid. |
submittedBy | The user who uploaded the document, as { id, name, email }. null when the uploader is unknown. |
extractedAt | When the latest extraction run completed. null when no run has completed. |
_meta is present whenever the file was found and accessible, whatever its status — a PROCESSING, CLASSIFYING or FAILED response carries it too. A file that does not exist or is not accessible returns no _meta at all.
An unrecognized include value returns a 400 naming the allowed values, metadata among them.
Backwards compatibility
Without metadata in include, the response shape is exactly the one documented under Response Types.