Overview
Digitizer API reference
Use the Digitizer API to digitize business documents - invoices, bills, receipts and similar. Upload a scan or a camera photo and get back normalized fields, line items, per-field confidence, and reconciliation warnings — with no templates to configure and nothing to train.
Every business document uses /api/v1/documents and the same public schema. Responses include the page count and a meta.request_id for support. Printed values, calculated amounts, and fields needing review are identified through field_status.
{ success, data, meta }. List endpoints return items and pagination fields inside data. Dates are ISO 8601 and amounts are plain decimals. Unknown values are null, and fields that were not found may be omitted: treat a missing key like null.https://digitizer.ee/api/v1/documentsLine amounts and review
Every document uses the same line schema: net_unit_price and unit_price are net and gross prices before line discounts. subtotal is the net line amount after discounts; vat_amount is its tax; total is its gross amount. The deprecated amount always equals total, including null.
VAT rates use percentage points: 20 means 20%. Unknown or ambiguous values are null. Unit prices may have more decimal places than currency totals. Printed values are retained; calculated line taxes may include a minor-unit adjustment to match document tax totals.
field_status reports read, derived, enriched, or review. Check review_fields and warnings before posting data. Confidence scores are indicative, not guarantees of correctness.
For base64 uploads, send a JSON object with content, content_type, and optional filename. A pages array holds independent files: each entry is queued as its own job and counts as its own document.
Uploads return HTTP 202 with durable job IDs. Poll /api/v1/jobs/{job_id} until it is succeeded or failed, then fetch the document. A paused job is waiting for the workspace owner and resumes by itself; cancel a job you no longer need with DELETE /api/v1/jobs/{job_id}. Unsupported file types and a disabled paid API are refused by the upload itself; page count and other processing problems are reported on the job. An Idempotency-Key is optional but recommended: reuse it when retrying after a timeout so you never create duplicate, billable jobs. If capacity is full, respect the HTTP 429 Retry-After header. Files are limited to 25 MiB each and request bodies to 100 MiB.
Reference
Endpoints
The reference covers documents, jobs, metadata, usage and callbacks. The request and response schemas are in /openapi.json.
Documents
Documents
Upload, retrieve, list, and delete normalized document results.
Base path /api/v1/documents
Extract a document
Upload one or more PDFs or images into the durable processing queue: 1-100 files, 25 MiB each, and at most 100 MiB per request body (base64 JSON adds about a third, so roughly 75 MiB of files). The body must arrive within 120 seconds. Returns HTTP 202 with one job per file; poll GET /api/v1/jobs/{job_id}, then fetch its document_id when succeeded. Multipart uploads and base64 JSON are supported; each file, and each pages entry in JSON, is a separate document and a separate job. Send metadata to tag the upload with your own reference; it comes back unchanged on each job, document and callback. The upload is refused at once with 415 unsupported_media_type if any file is not PDF, PNG, JPEG, WEBP, TIFF or BMP, and with 402 api_billing_required if paid API processing is off and the free allowance is used up. Page count, unreadable files and other processing problems appear later as the job's error.code (for example too_many_pages). Up to 2 uploads per workspace can be in progress at the same time; a further concurrent upload returns 429 queue_full with Retry-After.
https://digitizer.ee/api/v1/documentsHeaders
Idempotency-Keyheader- string·Optional, recommended. A unique key per upload, such as a UUID: 1-160 visible ASCII characters (0x21-0x7E, no spaces). If you retry after a timeout or network error, reuse the key to receive the original jobs instead of new, billable ones. The same key with different files or options returns 409 idempotency_conflict.
Body
filesbodyrequired- file[]·Repeated files form field; 1 to 100 files, 25 MiB and 10 pages each. A single field named file is also accepted.
hintsbody- string·Optional document context, such as a language or vendor name.
raw_textbody- boolean·Include document text: true or false (1/0 and yes/no are also accepted). Default: false.
regionsbody- boolean·Include located field boxes (regions) in this document's responses and callbacks: true or false. Default: true. Coordinates range from 0 to 1000 on each page.
metadatabody- object·Your reference for the upload, such as {"exa":"purchase:30983"}: a flat JSON object of string values, at most 1024 bytes as JSON. In multipart, send it as a JSON string field. It is returned unchanged on every job, document and callback of this upload, including document.failed, and is part of the Idempotency-Key check.
Request
curl --request POST \
--url 'https://digitizer.ee/api/v1/documents' \
--header 'X-API-Key: dig_live_xxxxxxxxxxxx' \
--form '[email protected]' \
--form 'metadata={"exa":"purchase:30983"}'Response
{
"success": true,
"data": {
"id": "bat_example",
"status": "queued",
"jobs": [
{
"id": "job_example",
"status": "queued",
"type": "document",
"document_id": null,
"error": null,
"metadata": {
"exa": "purchase:30983"
},
"created_at": "2026-09-24T09:00:00Z",
"updated_at": "2026-09-24T09:00:00Z"
}
],
"job_ids": [
"job_example"
],
"count": 1,
"created_at": "2026-09-24T09:00:00Z"
},
"meta": {
"request_id": "req_example",
"accepted": 1
}
}Returns: A queued submission carrying one job per uploaded file. Accepted work continues after disconnects and server restarts.
List documents
Browse the processed documents of this business, newest first, including documents uploaded with other API keys, in the dashboard and by email. Queued, paused and failed jobs are not listed; use GET /api/v1/jobs/{job_id} for those. Pages contain up to 100 documents; follow next_cursor using the same parameters until it is null. Limit defaults to 25. To sync, list exported=false with include=full, import each document, then mark it with PATCH /api/v1/documents/{document_id}; created_after bounds the scan.
https://digitizer.ee/api/v1/documentsParameters
limitquery- integer·Results per page, 1-100. Default: 25.
cursorquery- string·Opaque next_cursor from the previous page. Omit for the first page.
qquery- string·Case-insensitive filename search across all history; maximum 254 characters.
filterquery- string·all (default), expense, sales, review, or duplicates. Expense matches the business as customer; sales matches it as vendor. Applies across all history.
exportedquery- boolean·true or false: only documents marked, or not marked, exported with PATCH /api/v1/documents/{document_id}.
created_afterquery- string·ISO 8601 date-time with a time zone, such as 2026-10-01T00:00:00Z: only documents stored after it. A document's created_at can be slightly earlier than when it was stored, so overlap your bound and deduplicate by id.
includequery- string·full returns each item as the complete document, as GET /api/v1/documents/{document_id} does. Default: summaries.
Response
{
"success": true,
"data": {
"items": [
{
"id": "doc_example",
"filename": "invoice-1042.pdf",
"content_type": "application/pdf",
"size_bytes": 48320,
"file_sha256": "a4413c0a7c61f7bd9caa6296b83f726f61f1558f686d415aea02a9e9419f37cc",
"type": "document",
"status": "processed",
"pages": 1,
"confidence": 0.96,
"created_at": "2026-09-22T09:12:44Z",
"processing_ms": 2841,
"job_id": "job_example",
"metadata": {
"exa": "purchase:30983"
},
"source": {
"channel": "api"
},
"direction": "expense",
"external_ref": null,
"exported": false,
"exported_at": null
}
],
"count": 1,
"pagination": {
"limit": 25,
"next_cursor": null
}
},
"meta": {
"request_id": "req_example"
}
}Returns: Document summaries, count, and pagination metadata.
Get document
Retrieve the stored document result. raw_text is present only if requested at upload; regions are present when field locations are available, unless the upload set regions=false. Fields that were not found may be omitted: treat a missing key like null. source says how the document arrived (for email: the message's id, From and To addresses, subject and time); direction says whether it is the business's expense or sale.
https://digitizer.ee/api/v1/documents/{document_id}Parameters
document_idpathrequired- string·Document id returned by extraction.
Response
{
"success": true,
"data": {
"id": "doc_example",
"filename": "invoice-1042.pdf",
"content_type": "application/pdf",
"size_bytes": 48320,
"file_sha256": "a4413c0a7c61f7bd9caa6296b83f726f61f1558f686d415aea02a9e9419f37cc",
"type": "document",
"status": "processed",
"pages": 1,
"confidence": 0.96,
"created_at": "2026-09-22T09:12:44Z",
"processing_ms": 2841,
"job_id": "job_example",
"metadata": {
"exa": "purchase:30983"
},
"source": {
"channel": "api"
},
"direction": "expense",
"external_ref": null,
"exported": false,
"exported_at": null,
"is_document": true,
"extraction": {
"document_number": "INV-1042",
"issue_date": "2026-08-02",
"currency": "USD",
"subtotal": 4200,
"vat_amount": 420,
"total": 4620,
"vat_rates": [
{
"rate": 10,
"net": 4200,
"vat": 420,
"gross": 4620
}
],
"vendor": {
"name": "Northwind Supply Co."
},
"line_items": [
{
"description": "Maintenance service",
"code": "SVC-1042",
"quantity": 2,
"unit": "hour",
"net_unit_price": 2100,
"unit_price": 2310,
"discount_percent": null,
"discount_amount": null,
"subtotal": 4200,
"vat_rate": 10,
"vat_amount": 420,
"total": 4620,
"amount": 4620
}
]
},
"field_confidence": {
"document_number": 0.99
},
"field_status": {
"document_number": {
"status": "read",
"review": false
},
"line_items[0].total": {
"status": "derived",
"review": false
}
},
"review_fields": [],
"warnings": []
},
"meta": {
"request_id": "req_example"
}
}Returns: The document result.
Download the original file
Return the file exactly as it was uploaded or e-mailed, for as long as the document exists, whichever channel it came through. Content-Type is the file's type and Content-Disposition carries its original name. The bytes hash to the document's file_sha256, which is also the ETag. Documents processed before original files were kept return 404 file_not_available.
https://digitizer.ee/api/v1/documents/{document_id}/fileParameters
document_idpathrequired- string·Document id.
Request
curl --request GET \
--url 'https://digitizer.ee/api/v1/documents/doc_example/file' \
--header 'X-API-Key: dig_live_xxxxxxxxxxxx' \
--output invoice-1042.pdfResponse
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 48320
Content-Disposition: attachment; filename="invoice-1042.pdf"; filename*=UTF-8''invoice-1042.pdf
ETag: "a4413c0a7c61f7bd9caa6296b83f726f61f1558f686d415aea02a9e9419f37cc"
<file bytes>Returns: The file's bytes.
Mark a document handled
Record where your software took the document. external_ref is your reference, at most 200 characters; null removes it. exported=true marks the document exported and keeps the time it was first marked; exported=false clears it. Both are returned on the document and in the list, and external_ref is shown in the dashboard. Send either or both; the extraction itself is not changed.
https://digitizer.ee/api/v1/documents/{document_id}Parameters
document_idpathrequired- string·Document id.
Body
external_refbody- string | null·Your reference, such as the purchase invoice it became.
exportedbody- boolean·Whether the document has been exported.
Request
{
"external_ref": "Exa ostuarve 30983",
"exported": true
}Response
{
"success": true,
"data": {
"id": "doc_example",
"filename": "invoice-1042.pdf",
"content_type": "application/pdf",
"size_bytes": 48320,
"file_sha256": "a4413c0a7c61f7bd9caa6296b83f726f61f1558f686d415aea02a9e9419f37cc",
"type": "document",
"status": "processed",
"pages": 1,
"confidence": 0.96,
"created_at": "2026-09-22T09:12:44Z",
"processing_ms": 2841,
"job_id": "job_example",
"metadata": {
"exa": "purchase:30983"
},
"source": {
"channel": "api"
},
"direction": "expense",
"external_ref": "Exa ostuarve 30983",
"exported": true,
"exported_at": "2026-09-22T09:15:02Z",
"is_document": true,
"extraction": {
"document_number": "INV-1042",
"issue_date": "2026-08-02",
"currency": "USD",
"subtotal": 4200,
"vat_amount": 420,
"total": 4620,
"vat_rates": [
{
"rate": 10,
"net": 4200,
"vat": 420,
"gross": 4620
}
],
"vendor": {
"name": "Northwind Supply Co."
},
"line_items": [
{
"description": "Maintenance service",
"code": "SVC-1042",
"quantity": 2,
"unit": "hour",
"net_unit_price": 2100,
"unit_price": 2310,
"discount_percent": null,
"discount_amount": null,
"subtotal": 4200,
"vat_rate": 10,
"vat_amount": 420,
"total": 4620,
"amount": 4620
}
]
},
"field_confidence": {
"document_number": 0.99
},
"field_status": {
"document_number": {
"status": "read",
"review": false
},
"line_items[0].total": {
"status": "derived",
"review": false
}
},
"review_fields": [],
"warnings": []
},
"meta": {
"request_id": "req_example"
}
}Returns: The updated document.
Delete document
Remove a processed document and its extraction from the business. Its usage stays recorded. This cannot be undone. To stop a job that has not produced a document, use DELETE /api/v1/jobs/{job_id}.
https://digitizer.ee/api/v1/documents/{document_id}Parameters
document_idpathrequired- string·Document id to delete.
Response
{
"success": true,
"data": {
"id": "doc_example",
"deleted": true
},
"meta": {
"request_id": "req_example"
}
}Returns: The deleted id and deleted=true.
Jobs
Jobs
Track each uploaded file as it is processed.
Base path /api/v1/jobs
Get job status
Poll no faster than once per second; acceptance responses suggest Retry-After: 2. Status is queued, paused, processing, succeeded, or failed. paused is not terminal: the job keeps its file and resumes automatically after the workspace owner's one-time identity verification (billing_verification_required) or once invoice details are added in Billing (billing_details_required). error is set only when the job is failed (the reason) or paused (the pause reason); it is null otherwise, including while an automatic retry is queued. On success, fetch document_id. metadata is what the upload sent, or null. A cancelled job reports failed with job_cancelled; a job whose document was deleted reports failed with not_found.
https://digitizer.ee/api/v1/jobs/{job_id}Parameters
job_idpathrequired- string·Job id from the submission response.
Response
{
"success": true,
"data": {
"id": "job_example",
"status": "succeeded",
"type": "document",
"document_id": "doc_example",
"error": null,
"metadata": {
"exa": "purchase:30983"
},
"created_at": "2026-09-22T09:12:44Z",
"updated_at": "2026-09-22T09:12:48Z"
},
"meta": {
"request_id": "req_example"
}
}Returns: Job status and result id or error.
Retry a failed job
Requeue a failed job without uploading the file again. Temporary failures are already retried automatically for about three hours; a job that still fails keeps its original file for 30 days, during which it can be retried here. Returns HTTP 202 with the queued job; poll it as after an upload. Unknown ids return 404 not_found. Jobs that are not failed, whose document was deleted, or whose file has expired return 409 job_not_retryable.
https://digitizer.ee/api/v1/jobs/{job_id}/retryParameters
job_idpathrequired- string·Id of the failed job.
Response
{
"success": true,
"data": {
"id": "job_example",
"status": "queued",
"type": "document",
"document_id": null,
"error": null,
"metadata": {
"exa": "purchase:30983"
},
"created_at": "2026-09-22T09:12:44Z",
"updated_at": "2026-09-22T10:02:10Z"
},
"meta": {
"request_id": "req_example"
}
}Returns: The requeued job.
Cancel a job
Stop a job that has not produced a document. Queued, paused and failed jobs stop at once and their retained file is released; a job being processed is stopped before it can save a result or be billed. The job then reports failed with job_cancelled. Cancelling again returns the same job. A job that already produced a document returns 409 job_not_cancellable; delete the document instead.
https://digitizer.ee/api/v1/jobs/{job_id}Parameters
job_idpathrequired- string·Job id from the submission response.
Response
{
"success": true,
"data": {
"id": "job_example",
"status": "failed",
"type": "document",
"document_id": null,
"error": {
"code": "job_cancelled",
"message": "The job was cancelled before it produced a document."
},
"metadata": {
"exa": "purchase:30983"
},
"created_at": "2026-09-22T09:12:44Z",
"updated_at": "2026-09-22T09:13:02Z"
},
"meta": {
"request_id": "req_example"
}
}Returns: The cancelled job.
Metadata
Metadata
Discover the schema and inspect business usage.
Base path /api/v1
List document types
Invoices, receipts, bills, and similar documents share the document type and one schema.
https://digitizer.ee/api/v1/metadata/document-typesResponse
{
"success": true,
"data": {
"items": [
{
"id": "document",
"label": "Document",
"fields": 16
}
]
},
"meta": {
"request_id": "req_example"
}
}Returns: Supported document types and field counts.
List extractable fields
Get top-level field names, data types, nullability, and descriptions. Nested field schemas are defined in /openapi.json.
https://digitizer.ee/api/v1/metadata/fieldsParameters
typequery- string·Only document is supported; default: document.
Response
{
"success": true,
"data": {
"type": "document",
"fields": {
"document_number": {
"type": "string",
"nullable": true,
"description": "Invoice, bill or receipt identifier."
},
"issue_date": {
"type": "date",
"nullable": true,
"description": "Document date, ISO 8601."
},
"due_date": {
"type": "date",
"nullable": true,
"description": "Payment due date."
},
"currency": {
"type": "string",
"nullable": true,
"description": "ISO 4217 currency code."
},
"subtotal": {
"type": "number",
"nullable": true,
"description": "Net of VAT."
},
"vat_amount": {
"type": "number",
"nullable": true,
"description": "Total VAT."
},
"rounding": {
"type": "number",
"nullable": true,
"description": "Rounding adjustment applied to reach the total."
},
"total": {
"type": "number",
"nullable": true,
"description": "Gross amount printed."
},
"amount_due": {
"type": "number",
"nullable": true,
"description": "Unpaid balance."
},
"vat_rates": {
"type": "array",
"nullable": true,
"description": "VAT per rate: rate, net, vat, gross."
},
"vendor": {
"type": "object",
"nullable": true,
"description": "Issuing party."
},
"customer": {
"type": "object",
"nullable": true,
"description": "Billed party."
},
"line_items": {
"type": "array",
"nullable": false,
"description": "Line item rows: description, code, quantity, unit, gross unit_price, net_unit_price, discount_percent, discount_amount, subtotal, vat_rate, vat_amount, total. amount is a deprecated alias of total."
},
"banks": {
"type": "array",
"nullable": true,
"description": "Every bank printed for payment: name, iban, swift_bic."
},
"previous_balance": {
"type": "number",
"nullable": true,
"description": "Balance carried forward."
},
"payment": {
"type": "object",
"nullable": true,
"description": "Payment: method, card_number, card_last4, amount, tendered, change."
}
}
},
"meta": {
"request_id": "req_example"
}
}Returns: The selected type and its field definitions.
Get usage
Return all-time processing usage for the current business from its durable ledger, counting every channel (API, dashboard and email) and including deleted documents. It does not report the free API allowance, paid-API status or billing tiers; the workspace owner sees those in Billing. Legacy configured API keys report retained documents without a history cap.
https://digitizer.ee/api/v1/usageResponse
{
"success": true,
"data": {
"documents": 128,
"pages": 143,
"by_type": {
"document": 128
},
"last_request_at": "2026-09-22T09:12:44Z"
},
"meta": {
"request_id": "req_example"
}
}Returns: Business document counts, page counts, and last successful processing time.
Callbacks
Callbacks
Connect a business to your software with signed document notifications.
Base path /api/v1/callback
Get callback settings
Returns the current business callback and latest 20 deliveries. Signing secrets are never returned by GET.
https://digitizer.ee/api/v1/callbackResponse
{"success":true,"data":{"url":"","enabled":false,"events":["document.processed"],"updated_at":null,"deliveries":[]},"meta":{"request_id":"req_example"}}Returns: Callback settings and delivery status.
Configure a callback
Configure one optional public HTTPS callback per business. Applies to new documents from API, dashboard, and approved email attachments. Read /docs/callbacks for signature verification and retries. Changing configuration cancels queued and failed deliveries. The JSON body is limited to 4,000 characters and url to 2,048.
https://digitizer.ee/api/v1/callbackBody
urlbodyrequired- string·Public HTTPS endpoint on port 443; may be empty when disabled.
enabledbodyrequired- boolean·Enable delivery of callbacks.
eventsbody- string[]·document.processed, document.failed, or both. Omit to keep the current list; a new callback receives document.processed only. Changing only events keeps queued deliveries.
rotate_secretbody- boolean·Generate a new signing secret and cancel queued and failed deliveries.
Request
{
"url": "https://software.example.com/webhook",
"enabled": true,
"events": [
"document.processed",
"document.failed"
]
}Response
{"success":true,"data":{"url":"https://software.example.com/webhook","enabled":true,"events":["document.processed","document.failed"],"updated_at":"2026-09-22T12:00:00Z","deliveries":[],"signing_secret":"whsec_example_only"},"meta":{"request_id":"req_example"}}Returns: Updated settings; signing_secret is returned only on first setup or rotation.