Uploading documents
Invoices and bank statements into Tabula
Send one document to POST /v1/uploads, or a ZIP archive of documents to
POST /v1/uploads/batches. Tabula reads documents after the request returns:
the response is 202 Accepted with an upload to poll until it is completed or
failed.
curl https://api.tabula.de/v1/uploads \
-H "Authorization: Bearer tab_live_…" \
-F file=@Rechnung-2026-0042.pdf \
-F documentType=supplier_invoice \
-F externalId=ERP-2026-0042{
"id": "0b7f6f0e-4b8e-5c1d-9f0a-6a2e3d4c5b61",
"batchId": null,
"externalId": "ERP-2026-0042",
"fileName": "Rechnung-2026-0042.pdf",
"documentType": "supplier_invoice",
"status": "queued",
"result": null,
"error": null,
"createdAt": "2026-10-12T09:15:02.118Z"
}Document types
documentType | Files | What Tabula does |
|---|---|---|
auto (default) | PDF, XML | Tells supplier invoices, customer invoices and other documents apart. Bank statements are not detected: send them as bank_statement. |
supplier_invoice | PDF, XML | Processes the file as an invoice the company received from a supplier (Ausgabe). A file that is no invoice ends up in other documents. |
customer_invoice | PDF, XML | Processes the file as an invoice the company issued to a customer (Einnahme). A file that is no invoice ends up in other documents. |
bank_statement | Reads the transactions of the bank statement. They are confirmed in Tabula. | |
other_document | PDF, XML | Files the document in other documents, without booking it. |
XML files are e-invoices such as XRechnung. A PDF with an embedded e-invoice, such as ZUGFeRD or Factur-X, is read from its structured data. DATEV ledger and document archive XML files are refused.
A file may be up to 50 MB. A ZIP archive may hold up to 100 files and unpack to
200 MB; every file in it becomes an upload with the batch's documentType.
Repeated requests
externalId is your ID for the file. A request with an externalId that was
already used returns the original upload, so a timed-out request can be sent
again safely; the file name may differ. The same externalId with a different
file or document type is refused with 409 external_id_conflict.
Without an externalId, the same file with the same document type returns the
original upload.
Status
Poll GET /v1/uploads/{uploadId}. While the upload is queued or
processing, the Retry-After header suggests when to poll again.
status | Meaning |
|---|---|
queued | Waiting to be read. |
processing | Being read. |
completed | The result is in Tabula; result links to it. |
failed | Tabula could not process the file; error says why. |
deleted | The result was deleted in Tabula. |
A completed upload's result says what Tabula made of the file:
result.type | Meaning |
|---|---|
invoice | A supplier or customer invoice. |
other_document | A document that is no invoice. |
bank_statement | A bank statement import. Its transactions are confirmed in Tabula. |
payment_file | A SEPA payment file. |
result.url opens the result in Tabula.
Batches
Poll GET /v1/uploads/batches/{batchId}. The batch lists every upload in it and
counts them by status. Files of an unsupported type are listed as failed
uploads.
status | Meaning |
|---|---|
processing | At least one upload is queued or processing. |
completed | Every upload completed. |
completed_with_errors | Some uploads failed. |
failed | Every upload failed. |
Failures
error.code | Message | What to do |
|---|---|---|
unsupported_file_type | The file type is not supported for this document type. | Send a PDF or XML invoice or other document, or a PDF bank statement. |
file_too_large | The file exceeds the 50 MB limit. | Split the document. |
invalid_e_invoice | The e-invoice failed validation. | Ask the sender for a valid e-invoice. |
password_protected | The PDF is password-protected. | Remove the password and upload again. |
not_a_bank_statement | The file is not a bank statement. | Upload it with another document type. |
no_transactions_found | The bank statement contains no transactions. | Check the file. |
unreadable_document | The scan is not legible. | Upload a better scan. |
processing_failed | Tabula could not process the file. It can be retried in Tabula. | Retry the upload in Tabula. |
Sending the same request again retries an upload that failed before Tabula started reading it, and uploads a file again whose result was deleted in Tabula. An upload that Tabula could not process is retried in Tabula.