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.

All successful responses use { 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.
POSThttps://digitizer.ee/api/v1/documents

Line 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.

POSThttps://digitizer.ee/api/v1/documents

Headers

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
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

202 Accepted
{
  "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.

GEThttps://digitizer.ee/api/v1/documents

Parameters

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

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/documents/{document_id}

Parameters

document_idpathrequired
string·Document id returned by extraction.

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/documents/{document_id}/file

Parameters

document_idpathrequired
string·Document id.

Request

curl
curl --request GET \
  --url 'https://digitizer.ee/api/v1/documents/doc_example/file' \
  --header 'X-API-Key: dig_live_xxxxxxxxxxxx' \
  --output invoice-1042.pdf

Response

200 OK
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.

PATCHhttps://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

curl
{
  "external_ref": "Exa ostuarve 30983",
  "exported": true
}

Response

200 OK
{
  "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}.

DELETEhttps://digitizer.ee/api/v1/documents/{document_id}

Parameters

document_idpathrequired
string·Document id to delete.

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/jobs/{job_id}

Parameters

job_idpathrequired
string·Job id from the submission response.

Response

200 OK
{
  "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.

POSThttps://digitizer.ee/api/v1/jobs/{job_id}/retry

Parameters

job_idpathrequired
string·Id of the failed job.

Response

202 Accepted
{
  "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.

DELETEhttps://digitizer.ee/api/v1/jobs/{job_id}

Parameters

job_idpathrequired
string·Job id from the submission response.

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/metadata/document-types

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/metadata/fields

Parameters

typequery
string·Only document is supported; default: document.

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/usage

Response

200 OK
{
  "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.

GEThttps://digitizer.ee/api/v1/callback

Response

200 OK
{"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.

PUThttps://digitizer.ee/api/v1/callback

Body

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

curl
{
  "url": "https://software.example.com/webhook",
  "enabled": true,
  "events": [
    "document.processed",
    "document.failed"
  ]
}

Response

200 OK
{"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.