API documentation
Invoice walkthrough
Follow one realistic PDF invoice from upload through completed JSON and webhook delivery.
Sample document
This walkthrough uses a realistic cross-border PDF invoice from a German supplier to a Malta business customer. The document uses EU B2B reverse charge, so tax is not seller-collected even though the supply is taxable.
- Recipient
- Northwind Operations Ltd.
- Issue date
- 2026-05-10
- Due date
- 2026-06-09
- Net amount
- EUR 1,079.50
- VAT treatment
- Reverse charge
Upload request
Upload the PDF with a stable idempotency key. Store the key with the local job so retries remain safe.
curl -sS -X POST "https://www.exdata.app/api/v1/documents" \
-H "Authorization: Bearer $EXDATA_API_TOKEN" \
-H "Idempotency-Key: invoice-re-2026-1048" \
-F "file=@./invoice-re-2026-1048.pdf" \
-F "locale=en" \
-F "custom_types[]=invoice" \
-F "requester=accounts-payable"
Status response
The first response confirms that exdata accepted the document. Processing is asynchronous, so status can be pending until extraction completes.
{
"data": {
"id": 123,
"mode": "test",
"status": "pending",
"processing_stage": "queued",
"filename": "invoice-re-2026-1048.pdf",
"file_format": "pdf",
"file_size": 240123,
"custom_types": ["invoice"],
"requester": "accounts-payable",
"locale": "en",
"latest_extraction_run": {
"id": 456,
"mode": "test",
"source": "api",
"status": "pending",
"extractor_version": "document:2026-05-17",
"normalization_version": "base:2026-05-10",
"created_at": "2026-05-10T01:00:03.000000Z"
},
"created_at": "2026-05-10T01:00:00.000000Z",
"updated_at": "2026-05-10T01:00:03.000000Z"
}
}
Final JSON
Once the document is completed, map from the normalized value keys and keep candidates for review screens or support diagnostics.
{
"data": {
"type": {"value": "invoice", "candidates": ["Invoice"]},
"document_number": {"value": "RE-2026-1048", "candidates": ["RE-2026-1048"]},
"issue_date": {"value": "2026-05-10", "candidates": ["10 May 2026"]},
"payment_due_date": {"value": "2026-06-09", "candidates": ["Due 09/06/2026"]},
"sender_name": {"value": "Meyer Supply GmbH", "candidates": ["Meyer Supply GmbH"]},
"sender_country_code": {"value": "DE", "candidates": ["Germany"]},
"sender_vat_number": {"value": "DE123456789", "candidates": ["VAT ID DE123456789"]},
"recipient_name": {"value": "Northwind Operations Ltd.", "candidates": ["Northwind Operations Ltd."]},
"recipient_country_code": {"value": "MT", "candidates": ["Malta"]},
"recipient_vat_number": {"value": "MT12345678", "candidates": ["VAT MT12345678"]},
"currency": {"value": "EUR", "candidates": ["EUR"]},
"net_amount": {"value": "1079.50", "candidates": ["Net amount EUR 1,079.50"]},
"gross_amount": {"value": "1079.50", "candidates": ["Total due EUR 1,079.50"]},
"tax_amount": {"value": "0.00", "candidates": ["VAT reverse charge"]},
"tax_system": {"value": "vat", "candidates": ["VAT"]},
"tax_collection_mechanism": {"value": "reverse_charge", "candidates": ["Reverse charge"]},
"cross_border_tax_treatment": {"value": "eu_b2b_service", "candidates": ["Article 196 VAT Directive"]},
"tax_breakdowns": {
"value": [
{
"taxable_amount": "1079.50",
"tax_amount": "0.00",
"tax_rate": "0.00",
"taxability": "taxable",
"tax_collection_mechanism": "reverse_charge",
"tax_exemption_reason": "Intra-EU B2B reverse charge"
}
],
"candidates": ["Reverse charge applies under Article 196 VAT Directive"]
},
"payment_reference": {"value": "RE-2026-1048", "candidates": ["Payment reference RE-2026-1048"]},
"iban": {"value": "DE89370400440532013000", "candidates": ["IBAN DE89 3704 0044 0532 0130 00"]}
}
}
Webhook payload
If your endpoint subscribes to document.completed, exdata sends the completed document resource to your receiver with the same document and extraction fields.
{
"id": "1d9d0f2b-4eb8-4c94-a636-7a71f8b6a071",
"type": "document.completed",
"created_at": "2026-05-10T01:00:19.000000Z",
"data": {
"document": {
"id": 123,
"mode": "test",
"status": "completed",
"filename": "invoice-re-2026-1048.pdf",
"extractions": {
"document_number": {"value": "RE-2026-1048", "candidates": ["RE-2026-1048"]},
"gross_amount": {"value": "1079.50", "candidates": ["Total due EUR 1,079.50"]},
"tax_collection_mechanism": {"value": "reverse_charge", "candidates": ["Reverse charge"]}
}
}
}
}
Suggested mapping
Start with deterministic mappings and make review rules explicit. This keeps the first integration understandable while still preserving evidence for finance or support teams.
| Target | Use | Review rule |
|---|---|---|
| Vendor match | sender_name, sender_vat_number, iban | Review first payment to a new IBAN. |
| Duplicate check | document_number, sender_vat_number, gross_amount | Block exact duplicate before posting. |
| Tax code | tax_breakdowns.value[].tax_collection_mechanism | Route reverse_charge to the reverse-charge tax code. |
| Payment run | gross_amount, currency, payment_due_date, payment_reference | Require due date fallback rules before automation. |