Integration guide
Document callbacks
Receive extracted documents in your accounting software as soon as processing finishes. Configure one optional HTTPS callback per business in Settings → Integrations, or through the API. It covers API and dashboard uploads and approved email attachments.
Configure your endpoint
PUT /api/v1/callback
X-API-Key: YOUR_BUSINESS_API_KEY
Content-Type: application/json
{"url":"https://your-software.com/webhooks/digitizer","enabled":true}The response includes signing_secret only on first setup or when rotate_secret is true. Store it on your server. GET /api/v1/callback returns settings and the latest 20 deliveries without the secret. A business API credential can manage this business setting; keep those credentials private.
What arrives
{
"id": "evt_example",
"type": "document.processed",
"created_at": "2026-09-22T12:00:00.000Z",
"business_id": "biz_example",
"source": {
"channel": "email",
"email_id": "mail_example"
},
"data": {
"id": "doc_example",
"filename": "invoice.pdf",
"status": "processed",
"extraction": {
"total": 120,
"currency": "EUR"
}
}
}The data property contains the complete public document response, including review fields and duplicate matches available at completion. The example above is abbreviated. Source channel is api, email, or dashboard; email_id is present for email. Each new document produces one event. Reused email attachments, repeated worker attempts, and updates to an existing document do not produce additional events. Failed extraction does not produce a document.processed event. Enabling a callback does not replay older documents.
Verify the signature
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody is the original UTF-8 request body, before parsing JSON.
function verifyCallback(rawBody, headers, secret) {
const timestamp = headers.get("x-digitizer-timestamp") ?? "";
const signature = headers.get("x-digitizer-signature") ?? "";
if (!/^\d+$/.test(timestamp) || !/^v1=[a-f0-9]{64}$/.test(signature)) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(timestamp + "." + rawBody).digest();
return timingSafeEqual(expected, Buffer.from(signature.slice(3), "hex"));
}
// Verify first. Persist the event and deduplicate by event.id, then return 2xx.
// Perform slow accounting imports in your own background worker.Delivery and retries
Respond with any 2xx status within 10 seconds. All other responses, redirects, and network failures retry with delays of 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours, and 12 hours: eight attempts in total. Retries use the same event ID and body with a fresh signature timestamp. Delivery is at least once and order is not guaranteed. Deduplicate by event ID and use document duplicate metadata to review separate uploads of the same invoice.