API documentation
Recipes
Task-based integration guides for the workflows developers build first.
Upload and poll
Use this pattern when your application controls the user flow and can show processing state directly. Upload with an idempotency key, store the returned document ID, then poll until the document is no longer pending.
- Upload the file with
POST /documents. - Store
data.id,data.mode, and yourIdempotency-Key. - Poll
GET /documents/{document}untilstatusiscompleted,error, orblocked. - Read
GET /documents/{document}/extractionswhen completed.
switch (document.data.status) {
case "completed":
// Read /documents/{document}/extractions and continue mapping.
break;
case "pending":
// Poll again with backoff, or wait for a webhook.
break;
case "blocked":
case "error":
// Show an operator path instead of silently retrying forever.
break;
}
Test mode
Use a test-mode token while building and QA testing. The request shape, document lifecycle, extraction fields, idempotency behavior, and webhook payloads match live mode; responses include mode: "test".
| What to test | Expected behavior | Go-live gate |
|---|---|---|
| Representative PDFs | Fields appear with useful candidates. | Your mapper handles missing optional fields. |
| Duplicate upload retry | Same idempotency key returns the stored response. | Your job runner can safely retry network failures. |
| Webhook receiver | Signature verification and delivery dedupe pass. | Your receiver stores processed delivery IDs. |
| Limit response | 429 with test_mode_limit_exceeded. | Your integration shows a clear operator message. |
Webhook receiver
Use webhooks when your system should react without polling. Verify X-Signature against the raw request body before parsing the JSON, then dedupe by X-Delivery.
- Configure the endpoint URL and signing secret in the app.
- Subscribe to
document.completedfirst; add failure events when your support process is ready. - Return a
2xxquickly after storing the delivery. - Run downstream mapping asynchronously.
Never start payment, posting, or approval automation before signature verification and idempotency storage have both succeeded.
Map invoice fields into an ERP
For invoice capture, start with a small stable mapping and keep candidates available for review. Do not require every optional field before creating a draft posting.
| ERP field | exdata field | Mapping note |
|---|---|---|
| Supplier | sender_name, sender_vat_number | Use identifiers for matching when names vary. |
| Invoice number | document_number | Combine with supplier and issue date for duplicate checks. |
| Invoice date | issue_date | Fallback to date only if your process allows it. |
| Due date | payment_due_date | Keep nullable for invoices without explicit terms. |
| Total | gross_amount, currency | Amounts are strings; convert with decimal-safe code. |
| Tax lines | tax_breakdowns | Use row-level taxability and collection mechanism for tax codes. |
| Payment details | iban, bic, payment_reference | Require review before first payment to a new account. |
Handle failed and blocked documents
A terminal state is not always successful. Treat error and blocked as explicit workflow states so operators can resolve the document instead of losing it in a retry loop.
| Status | Inspect | Recommended action |
|---|---|---|
error | processing_error, latest extraction run error fields | Show manual review or retry after the file/source problem is understood. |
blocked | blocked_reason | Resolve account state, credits, or policy condition before re-uploading. |
pending too long | processing_stage, X-Request-ID | Keep polling with backoff and expose an operator support path. |
Custom document types
exdata has standard document types such as invoice, credit-note, bank-statement, contract, timesheet, letter, and other. Use custom_types[] to extend those categories for your workflow, not to replace the normalized type field.
curl -sS -X POST "https://www.exdata.app/api/v1/documents" \
-H "Authorization: Bearer $EXDATA_API_TOKEN" \
-H "Idempotency-Key: supplier-onboarding-2026-1048" \
-F "file=@./supplier-form.pdf" \
-F "custom_types[]=supplier-onboarding" \
-F "custom_types[]=invoice"