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

Enable a business callback
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

Event envelope
{
  "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

Node.js receiver verification
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.

Settings → Integrations shows delivery status and lets you retry failed deliveries. Changing the URL, disabling the callback, or rotating its secret cancels pending deliveries; an already in-flight request may still finish. Only public HTTPS endpoints on port 443 are accepted.