Skip to content

Developers

API documentation

Five REST endpoints cover the whole API consumer workflow: authenticate, upload, track, download and integrate. JSON in snake_case, errors as RFC 9457 problem documents, every response with an X-Request-ID. The complete, machine-readable specification is the OpenAPI document.

Quick start

  1. Create an organisation and sign in.
  2. In the app, open API keys and create a key with the permissions you need. It is shown once.
  3. Export it: export DOCAI_API_KEY=dai_live_…
  4. Upload a document, poll its status, and download the result — the examples below show each call.

Signed in, the app's API quick-start page shows these calls with your own address filled in.

Authentication

Send the key in the Authorization header as a bearer token (or in X-API-Key). A key belongs to one client workspace and has scopes; an endpoint needs the permission shown next to it. Keys are stored only as a hash: if you lose one, revoke it and create another.

Authorization: Bearer dai_live_xxxxxxxx_…

Upload

POST/api/v1/documents/uploadneeds documents:write

Upload one document, several documents, or one ZIP archive. Returns at once with a batch ID and one document ID per accepted file; processing continues in the background.

Parameters of Upload
ParameterInDescription
filesrequiredformOne or more files (PDF, PNG, JPEG, TIFF, WebP), or exactly one ZIP archive. Repeat the field for several files.
metadataformA JSON object stored with the batch, for your own references.
document_type_hintformA document type you expect, such as invoice. Classification still runs.
Idempotency-KeyheaderAny unique string. Repeating a request with the same key and files returns the first response and creates nothing.
curl -X POST "https://your-host/api/v1/documents/upload" \
  -H "Authorization: Bearer $DOCAI_API_KEY" \
  -H "Idempotency-Key: upload-2026-10-10-0001" \
  -F "files=@invoice-001.pdf" \
  -F 'metadata={"source": "erp-export"}'
Example response · 202
{
  "batch_id": "batch_01m4jn8jaxzeqvvghk2d7mzfhz",
  "status": "PROCESSING",
  "documents": [
    {
      "document_id": "doc_01m4jn8jaxzeqvvghk2d7mzfj0",
      "file_name": "invoice-001.pdf",
      "status": "UPLOADED"
    }
  ],
  "rejected": []
}

Status

GET/api/v1/documents/statusneeds documents:read

The processing status of one document, a list of documents, or every document of a batch. Unknown IDs, including other organisations' IDs, are listed under not_found.

Parameters of Status
ParameterInDescription
document_idqueryOne document.
document_idsquerySeveral documents, comma-separated or repeated.
batch_idqueryEvery document of a batch (paginated with limit and cursor).
curl -X GET "https://your-host/api/v1/documents/status?document_id=doc_01m4jn8jaxzeqvvghk2d7mzfj0" \
  -H "Authorization: Bearer $DOCAI_API_KEY"
Example response · 200
{
  "batch": null,
  "documents": [
    {
      "document_id": "doc_01m4jn8jaxzeqvvghk2d7mzfj0",
      "file_name": "invoice-001.pdf",
      "status": "COMPLETED",
      "document_type": "invoice",
      "document_subtype": "vendor_invoice",
      "classification_confidence": 0.98,
      "run_number": 1,
      "reruns_used": 0,
      "reruns_remaining": 2,
      "error": null,
      "created_at": "2026-10-10T10:20:43.221638Z",
      "completed_at": "2026-10-10T10:21:05.569219Z",
      "batch_id": "batch_01m4jn8jaxzeqvvghk2d7mzfhz"
    }
  ],
  "not_found": []
}

Download

GET/api/v1/documents/downloadneeds documents:read

The structured result of a processed document as JSON or CSV, or a ZIP of a batch or several documents with a manifest. Documents that are not finished are listed in the manifest with the reason.

Parameters of Download
ParameterInDescription
document_idqueryOne document.
document_idsquerySeveral documents (always a ZIP).
batch_idqueryEvery processed document of a batch (a ZIP).
formatqueryzip (default), json or csv. json and csv are for one document.
include_originalquerytrue adds each uploaded file to the ZIP.
curl -X GET "https://your-host/api/v1/documents/download?document_id=doc_01m4jn8jaxzeqvvghk2d7mzfj0&format=json" \
  -H "Authorization: Bearer $DOCAI_API_KEY"
Example response · 200
{
  "output_version": 3,
  "document_id": "doc_01m4jn8jaxzeqvvghk2d7mzfj0",
  "batch_id": "batch_01m4jn8jaxzeqvvghk2d7mzfhz",
  "file_name": "invoice-001.pdf",
  "document_type": "invoice",
  "document_subtype": "vendor_invoice",
  "schema": {
    "name": "invoice",
    "version": 1
  },
  "status": "COMPLETED",
  "run_number": 1,
  "revision": 1,
  "confidence": 0.9894,
  "content": {
    "invoice": {
      "invoice_number": "INV-2026-0418",
      "invoice_date": "2026-03-14"
    },
    "line_items": [
      {
        "description": "Carbide end mill 8 mm",
        "quantity": 20,
        "unit_price": 18.4,
        "unit_price_raw": "18.40",
        "amount": 368,
        "amount_raw": "368.00"
      }
    ]
  }
}

Rerun

POST/api/v1/documents/rerunneeds documents:rerun

Process a finished document again — for example after changing the extraction engine. A document is processed once and can be rerun at most twice; each rerun counts as a document. Give exactly one of document_id, document_ids or batch_id.

Parameters of Rerun
ParameterInDescription
document_idbodyOne document.
document_idsbodyA list of documents; each is accepted or rejected on its own.
batch_idbodyEvery document of a batch.
Idempotency-KeyheaderMakes a retried request safe.
curl -X POST "https://your-host/api/v1/documents/rerun" \
  -H "Authorization: Bearer $DOCAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"document_id":"doc_01m4jn8jaxzeqvvghk2d7mzfj0"}'
Example response · 202
{
  "rerun_request_id": "rerun_01m4jnjd3zxhw6q65j8aw56fkq",
  "status": "ACCEPTED",
  "batch_id": null,
  "accepted": [
    {
      "document_id": "doc_01m4jn8jaxzeqvvghk2d7mzfj0",
      "run_number": 2,
      "reruns_used": 1,
      "reruns_remaining": 1,
      "status": "QUEUED",
      "human_corrections_replaced": false
    }
  ],
  "rejected": []
}

Health Check

GET/api/v1/healthno key needed

The platform's health: HEALTHY, DEGRADED or UNHEALTHY overall, with the status of each component. No key is needed; with a key you get more detail.

curl -X GET "https://your-host/api/v1/health"
Example response · 200
{
  "status": "HEALTHY",
  "checked_at": "2026-10-10T10:20:43+00:00",
  "service": "document-ai-api",
  "version": "0.1.0",
  "detail_level": "summary",
  "components": [
    {
      "name": "api",
      "status": "HEALTHY"
    },
    {
      "name": "postgresql",
      "status": "HEALTHY"
    },
    {
      "name": "llm_gateway",
      "status": "HEALTHY"
    }
  ]
}

Document states

UPLOADED
Accepted and stored.
VALIDATING
File checks are running.
QUEUED
Waiting for a worker.
CLASSIFYING
Identifying the document type.
EXTRACTING
Reading fields and tables.
VALIDATING_OUTPUT
Checking the result against its schema.
REVIEW_REQUIRED
A person must confirm something. Final for the API until reviewed.
COMPLETED
Final. The result can be downloaded.
FAILED
Final. error explains why; the document can be rerun.

Errors

Errors are application/problem+json with a stable code, a human detail, a request_id to quote to support, and errors per field for validation problems.

Example error · 404
{
  "type": "https://docs.documentai.local/errors/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "Document was not found.",
  "code": "NOT_FOUND",
  "request_id": "a76ba09f78b0465182bdcdb2858c6a99"
}
Error codes
StatusCodeMeaning
401UNAUTHENTICATEDNo API key was sent.
401INVALID_API_KEYThe key is malformed, unknown, expired or revoked.
403PERMISSION_DENIEDThe key lacks the permission the endpoint needs.
404NOT_FOUNDThe resource does not exist in your organisation.
409IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used before with a different request.
409RERUN_REJECTEDNo requested document could be rerun; rejected gives each reason.
413PAYLOAD_TOO_LARGEThe request body exceeds the size limit.
422VALIDATION_ERRORA parameter is invalid; errors lists each field.
422NO_ACCEPTABLE_FILESNothing in the upload could be accepted; rejected lists why.
429RATE_LIMITEDToo many requests; wait Retry-After seconds.
429QUOTA_EXCEEDEDA plan limit for this period is reached.
429TENANT_BACKLOGYour organisation has too many documents still processing; retry after Retry-After seconds.
503SERVICE_BUSYThe platform is shedding load; retry after Retry-After seconds.

Rate limits

Each plan allows a number of API requests per minute, hour and day (see pricing). Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy. Over a limit you get 429 with Retry-After in seconds; when the platform is shedding load it answers 503 with Retry-After. Back off and retry after that time.

Idempotency

Upload and Rerun accept an Idempotency-Key header. Repeating a request with the same key and the same content returns the original response and creates nothing new; reusing a key with different content is refused with 409 IDEMPOTENCY_KEY_REUSED. Use one key per logical operation, for example the name of a nightly export.

Complete example

The API consumer workflow end to end in Python: upload a batch, wait, download, rerun failures.

import os, time
import httpx

BASE = "https://your-host/api/v1"
client = httpx.Client(base_url=BASE, headers={"Authorization": f"Bearer {os.environ['DOCAI_API_KEY']}"}, timeout=60)

# 1. Upload two documents in one batch (safe to retry with the same Idempotency-Key)
with open("invoice-001.pdf", "rb") as a, open("receipt-001.jpg", "rb") as b:
    upload = client.post("/documents/upload", files=[("files", a), ("files", b)],
                         headers={"Idempotency-Key": "nightly-2026-10-10"})
upload.raise_for_status()
batch_id = upload.json()["batch_id"]

# 2. Track until every document reaches a final state
FINAL = {"COMPLETED", "FAILED", "REVIEW_REQUIRED"}
while True:
    docs = client.get("/documents/status", params={"batch_id": batch_id}).json()["documents"]
    if all(d["status"] in FINAL for d in docs):
        break
    time.sleep(5)

# 3. Download the structured results of the batch as a ZIP (manifest + one folder per document)
with open(f"{batch_id}.zip", "wb") as out:
    out.write(client.get("/documents/download", params={"batch_id": batch_id}).content)

# 4. Rerun the failed ones (at most twice per document)
failed = [d["document_id"] for d in docs if d["status"] == "FAILED"]
if failed:
    client.post("/documents/rerun", json={"document_ids": failed})