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.

  1. Upload the file with POST /documents.
  2. Store data.id, data.mode, and your Idempotency-Key.
  3. Poll GET /documents/{document} until status is completed, error, or blocked.
  4. Read GET /documents/{document}/extractions when completed.
Polling decision
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 testExpected behaviorGo-live gate
Representative PDFsFields appear with useful candidates.Your mapper handles missing optional fields.
Duplicate upload retrySame idempotency key returns the stored response.Your job runner can safely retry network failures.
Webhook receiverSignature verification and delivery dedupe pass.Your receiver stores processed delivery IDs.
Limit response429 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.

  1. Configure the endpoint URL and signing secret in the app.
  2. Subscribe to document.completed first; add failure events when your support process is ready.
  3. Return a 2xx quickly after storing the delivery.
  4. Run downstream mapping asynchronously.
Receiver rule

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 fieldexdata fieldMapping note
Suppliersender_name, sender_vat_numberUse identifiers for matching when names vary.
Invoice numberdocument_numberCombine with supplier and issue date for duplicate checks.
Invoice dateissue_dateFallback to date only if your process allows it.
Due datepayment_due_dateKeep nullable for invoices without explicit terms.
Totalgross_amount, currencyAmounts are strings; convert with decimal-safe code.
Tax linestax_breakdownsUse row-level taxability and collection mechanism for tax codes.
Payment detailsiban, bic, payment_referenceRequire 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.

StatusInspectRecommended action
errorprocessing_error, latest extraction run error fieldsShow manual review or retry after the file/source problem is understood.
blockedblocked_reasonResolve account state, credits, or policy condition before re-uploading.
pending too longprocessing_stage, X-Request-IDKeep 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.

Custom type extension
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"