API Reference
One REST endpoint family for turning documents into structured JSON. Upload a PDF, scan or image — get back typed fields with per-field confidence scores.
Quickstart
From zero to your first extraction. Should take under five minutes.
Create an account
Sign up at doxroute.com/login.html, or straight from the API:
# returns your user object and sets a session cookie curl -X POST https://doxroute.com/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{"email":"you@company.com","password":"YourPassword123","name":"You"}'Create an API key
Go to Settings → API keys and click Create key. The full key (
dxr_k1_…) is shown once — store it somewhere safe. Free accounts get 2 keys, Pro 10, Enterprise unlimited.Send your first document
Leave
doc_typeout and DoXroute classifies the document itself and picks the matching schema.curl -X POST https://doxroute.com/api/v1/extract \ -H "X-Api-Key: dxr_k1_YOUR_KEY" \ -F "file=@invoice.pdf" # {"job_id":"a3f1...","status":"queued","filename":"invoice.pdf"}Poll for the result
Most documents finish in about 3–5 seconds.
curl https://doxroute.com/api/v1/jobs/a3f1... \ -H "X-Api-Key: dxr_k1_YOUR_KEY"{ "job_id": "a3f1...", "status": "completed", "result": { "invoice_number": "INV-2025-0042", "invoice_date": "2025-03-14", "currency": "EUR", "total": 12480.50, "shipper_name": "SCHMIDT GMBH", "incoterm": "CIF", "_confidence": { "overall": 0.97, "fields": { "total": 0.99 } }, "_meta": { "schema_used": "invoice", "auto_classified": true, "extraction_mode": "vision", "extraction_time_ms": 3310 } } }Skip the polling (optional)
Register a webhook and DoXroute posts the result to your endpoint as soon as the job finishes, signed with HMAC-SHA256.
Authentication
Two mechanisms, same permissions. Every endpoint below requires one of them unless marked otherwise.
| Method | Use it for | How |
|---|---|---|
| API key | Servers, scripts, the SDK | Header X-Api-Key: dxr_k1_… |
| JWT cookie | The web dashboard | Set automatically by /auth/login — HttpOnly, Secure, SameSite=Lax, 24h |
Conventions
| Topic | Behaviour |
|---|---|
| Base URL | https://doxroute.com/api/v1 — HTTPS only, HTTP redirects with 301 |
| Uploads | multipart/form-data. Max 50 MB per file |
| Formats | PDF, PNG, JPG, TIFF, DOCX, XLSX, PPTX |
| Responses | application/json, UTF-8 |
| Dates | Normalised to ISO 8601 (YYYY-MM-DD) in extraction output |
| Amounts | JSON numbers, never strings. Currency in a separate field |
| Tracing | Every response carries X-Request-ID; include it in support requests |
| Rate limit | 30 req/s per IP with a burst of 20, plus per-plan monthly quotas |
Extraction
Four ways to submit documents. All accept an optional doc_type; omit it to let the classifier route the document.
| Field | Type | Notes |
|---|---|---|
file | file | Required. The document |
doc_type | string | Optional. One of the document types. Empty = auto-classify |
curl -X POST https://doxroute.com/api/v1/extract \
-H "X-Api-Key: dxr_k1_YOUR_KEY" \
-F "file=@bill_of_lading.pdf" -F "doc_type=bill_of_lading"
Same fields as /extract, but the response body is the extraction itself. Convenient for testing; use the async endpoint in production so a slow document cannot tie up your request.
Repeat the files field once per document. Maximum 50 files per call; per-plan batch caps also apply. Returns {"batch_id", "job_ids", "count"}.
curl -X POST https://doxroute.com/api/v1/extract/batch \
-H "X-Api-Key: dxr_k1_YOUR_KEY" \
-F "files=@inv1.pdf" -F "files=@inv2.pdf" -F "files=@inv3.pdf"
| Field | Type | Notes |
|---|---|---|
file | file | Required. PDF only |
split_method | string | auto (default), blank (blank-page separators), text |
doc_type | string | Optional, applied to every part |
Returns {"split": true, "parts": n, "batch_id": "…"}, or a single job_id when no split is detected.
Document types
Seven trade-ready schemas ship with the product. Fetch the live list from GET /schemas (no authentication required).
doc_type | Covers |
|---|---|
invoice | Commercial, proforma and shipping invoices, facturas |
packing_list | Packing lists and packing slips |
bill_of_lading | B/L, sea waybills, conocimiento de embarque |
delivery_note | Delivery notes, albaranes, bons de livraison |
purchase_order | Purchase orders, órdenes de compra |
customs_declaration | DUA / SAD customs declarations |
receipt | Till receipts, tickets de compra |
generic | Universal schema — used when nothing else fits |
doc_type is omitted, the first page is classified before extraction and the matching schema is applied. The result reports _meta.schema_used and _meta.auto_classified. Measured at 100% on a 56-document, 7-type internal corpus.Jobs
Query: status (queued · processing · completed · failed), limit (default 50).
Query: format (csv or xlsx), status, limit.
Batch
Returns per-job status plus counters for completed, failed and pending.
Query: format — csv (default) or xlsx.
Custom extractors
Describe the fields you need in plain language; DoXroute generates a reusable schema. Extractors are private to the account that created them.
| Field | Type | Notes |
|---|---|---|
name | string | Required |
description | string | Required, at least 10 characters. What to pull out of the document |
sample_fields | string | Optional comma-separated hints, e.g. policy_number,insured_name |
curl -X POST https://doxroute.com/api/v1/extractors \
-H "X-Api-Key: dxr_k1_YOUR_KEY" \
-F "name=Marine insurance certificate" \
-F "description=Extract policy number, insured party, vessel, voyage, sum insured and currency"
Webhooks
Get pushed a payload when a job finishes instead of polling.
curl -X POST https://doxroute.com/api/v1/webhooks \
-H "X-Api-Key: dxr_k1_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://your-app.com/hooks/doxroute","events":["job.completed","job.failed"]}'
Verifying the signature
Each delivery carries X-DoXroute-Signature: the HMAC-SHA256 of the raw body, keyed with the secret returned when the webhook was created. Compare in constant time and reject anything that does not match.
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
localhost, cloud metadata addresses and non-HTTP schemes are rejected at registration time.Account & API keys
Body: {"email", "password", "name"}. New accounts always start on the free plan.
Body: {"email", "password"}. Five consecutive failures lock the account for five minutes and return 429.
{"name"}Billing
Body: {"plan": "pro"} or {"plan": "enterprise"}. Returns {"url"} — redirect the browser there.
Data export & deletion
Returns the account record, API key metadata, extractors, webhooks and job history as JSON.
Admin
Restricted to accounts with the admin flag. Everyone else receives 403.
Plans & limits
| Free | Pro — €49/mo | Enterprise — €149/mo | |
|---|---|---|---|
| Documents / month | 50 | 5,000 | 25,000 |
| Custom extractors | 3 | 50 | unlimited |
| API keys | 2 | 10 | unlimited |
| Files per batch | 5 | 100 | 200 |
| Webhooks | — | yes | yes |
Quotas reset on the first of each month. Exceeding the document quota returns 403 with an upgrade message; the request is not counted.
Errors
Conventional HTTP status codes. The body is always {"detail": "…"}.
| Code | Meaning | What to do |
|---|---|---|
400 | Malformed request or unsupported file | Check the field names and file type |
401 | Missing, invalid or expired credentials | Check the X-Api-Key header or log in again |
403 | Plan limit reached, or not your resource | Upgrade, or verify the id belongs to your account |
404 | Not found — also returned for resources you do not own | Check the id |
413 | File larger than 50 MB | Split or compress the document |
429 | Rate limited, or login temporarily locked | Back off and retry; login locks clear after five minutes |
500 | Processing failed | Retry once, then send us the X-Request-ID |
502 | Upstream model unavailable | Automatic failover usually handles this; retry |
Failed jobs return status: "failed" with an error field rather than an HTTP error, since the request itself succeeded.
Python SDK
A thin wrapper over the REST API. Ask support for the package if you would rather not write HTTP calls.
from doxroute import DoXroute
client = DoXroute(api_key="dxr_k1_YOUR_KEY")
# one document, blocking until done
result = client.extract("invoice.pdf", wait=True)
print(result["total"], result["currency"])
# a folder, in one batch
batch = client.extract_batch(["a.pdf", "b.pdf", "c.pdf"])
client.wait_for_batch(batch["batch_id"])
client.export_batch(batch["batch_id"], format="xlsx", path="results.xlsx")