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
- Create an organisation and sign in.
- In the app, open API keys and create a key with the permissions you need. It is shown once.
- Export it:
export DOCAI_API_KEY=dai_live_… - 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:writeUpload 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.
| Parameter | In | Description |
|---|---|---|
filesrequired | form | One or more files (PDF, PNG, JPEG, TIFF, WebP), or exactly one ZIP archive. Repeat the field for several files. |
metadata | form | A JSON object stored with the batch, for your own references. |
document_type_hint | form | A document type you expect, such as invoice. Classification still runs. |
Idempotency-Key | header | Any 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"}'{
"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:readThe 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.
| Parameter | In | Description |
|---|---|---|
document_id | query | One document. |
document_ids | query | Several documents, comma-separated or repeated. |
batch_id | query | Every 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"{
"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:readThe 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.
| Parameter | In | Description |
|---|---|---|
document_id | query | One document. |
document_ids | query | Several documents (always a ZIP). |
batch_id | query | Every processed document of a batch (a ZIP). |
format | query | zip (default), json or csv. json and csv are for one document. |
include_original | query | true 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"{
"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:rerunProcess 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.
| Parameter | In | Description |
|---|---|---|
document_id | body | One document. |
document_ids | body | A list of documents; each is accepted or rejected on its own. |
batch_id | body | Every document of a batch. |
Idempotency-Key | header | Makes 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"}'{
"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 neededThe 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"{
"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.
{
"type": "https://docs.documentai.local/errors/not-found",
"title": "Not found",
"status": 404,
"detail": "Document was not found.",
"code": "NOT_FOUND",
"request_id": "a76ba09f78b0465182bdcdb2858c6a99"
}| Status | Code | Meaning |
|---|---|---|
| 401 | UNAUTHENTICATED | No API key was sent. |
| 401 | INVALID_API_KEY | The key is malformed, unknown, expired or revoked. |
| 403 | PERMISSION_DENIED | The key lacks the permission the endpoint needs. |
| 404 | NOT_FOUND | The resource does not exist in your organisation. |
| 409 | IDEMPOTENCY_KEY_REUSED | The Idempotency-Key was used before with a different request. |
| 409 | RERUN_REJECTED | No requested document could be rerun; rejected gives each reason. |
| 413 | PAYLOAD_TOO_LARGE | The request body exceeds the size limit. |
| 422 | VALIDATION_ERROR | A parameter is invalid; errors lists each field. |
| 422 | NO_ACCEPTABLE_FILES | Nothing in the upload could be accepted; rejected lists why. |
| 429 | RATE_LIMITED | Too many requests; wait Retry-After seconds. |
| 429 | QUOTA_EXCEEDED | A plan limit for this period is reached. |
| 429 | TENANT_BACKLOG | Your organisation has too many documents still processing; retry after Retry-After seconds. |
| 503 | SERVICE_BUSY | The 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})