Skip to main content
Dolfin sends a signed HTTP POST to a URL you control whenever something happens to a bill, invoice, credit note, or match group. Instead of polling GET /v1/bills/{id} in a loop waiting for extraction to finish, you get told the moment it does. Most events carry the full entity snapshot — the same shape the corresponding GET endpoint returns — so you usually don’t need a follow-up call. A few send a compact reference instead and expect you to fetch the detail; see what each event sends.

How it works

1

Create a subscription

In the Dolfin portal, add your HTTPS endpoint and pick the events you want. Copy the signing secret — it’s shown once.
2

Receive the POST

Dolfin delivers each matching event to your URL with a Dolfin-Signature header.
3

Verify the signature

Recompute the HMAC over the raw request body and compare. Reject anything that doesn’t match.
4

Return 2xx

Any 2xx acknowledges the event. Anything else — or no response within 10 seconds — is retried with backoff.

Create a subscription

Subscriptions are managed in the Dolfin portal under Webhooks. Create one with:
  • URL — must be https:// and publicly reachable. Private, loopback, link-local, and cloud metadata addresses are rejected.
  • Event types — one or more from the catalog. Subscribe only to what you handle; you can change the selection later.
Dolfin generates a signing secret (whsec_…) and shows it once, at creation. Store it somewhere your webhook handler can read it. If you lose it or need to cycle it, rotate the secret from the portal — the previous one stops working immediately.
Subscriptions are client-wide. One subscription covers every organisation under your client, so you don’t need one per organisation. Each event carries organisationId — route on that field. You can create up to 10 subscriptions per client, which is usually more than enough: use separate ones to split traffic across services, not across organisations.
There’s no backfill. A new subscription only receives events that fire after it’s created. To seed your initial state, page through GET /v1/bills and GET /v1/invoices once, then let webhooks keep you current.

The event envelope

Every delivery has the same outer shape. Only data changes between event types.
Two headers are also set, so you can route or de-duplicate without parsing the body: Dolfin-Event-Id and Dolfin-Event-Type.

Example: react to bill extraction

This is the flow that most often replaces polling. Uploading a bill returns immediately — the document is read asynchronously — so you need to know when the extracted fields are ready. Subscribe to these four events: Upload the bill as usual:
The response comes back straight away with the bill in Extracting. Rather than polling it, wait for the webhook:
A bill that finished extraction carries a confidence score between 0 and 1, so you can route low-confidence documents to a human and let clean ones flow through.
Extraction methods. Dolfin can read bill documents with either OCR or LLM vision extraction. Which one your account uses is configured per client — contact us to switch. Either way the events, states, and extracted fields are identical, so nothing in your integration changes.
The event is named bill.ocr_failed while the bill state is ExtractionFailed. The event name is kept as-is so existing integrations don’t break — treat it as “the document couldn’t be read”, whichever extraction method your account uses.

Verify the signature

Always verify before trusting a payload. Your endpoint is public, so anyone could post to it. Each request carries:
t is the Unix timestamp of the delivery attempt and v1 is a hex HMAC-SHA256. To verify:
  1. Concatenate t, a literal ., and the raw request body.
  2. Compute HMAC-SHA256 over that string, keyed with your signing secret.
  3. Compare against v1 in constant time.
  4. Reject deliveries whose t is outside your tolerance (5 minutes is a good default) to block replays.
Sign the raw bytes you received. Parsing the JSON and re-serializing it changes whitespace and key order, and the signature will never match. This is the most common integration bug.

Responding, retries, and delivery

Return any 2xx to acknowledge. Do it quickly — Dolfin gives up on a delivery attempt after 10 seconds. If your processing is slow, enqueue the event and return 200 immediately. A non-2xx response, a timeout, or a connection error schedules a retry: After 10 failed attempts the delivery is marked failed and no longer retried automatically. You can inspect deliveries and replay a failed one from the portal. A few guarantees worth designing around:
  • At-least-once delivery. A successful delivery can still be re-sent — for example if your 200 was lost in transit. The event id is stable across every attempt, so record processed IDs and ignore repeats. Make your handler idempotent.
  • No ordering guarantee. Events aren’t guaranteed to arrive in the order they occurred. Don’t infer state from arrival order — trust the entity snapshot in data, or re-fetch the entity.
  • Auto-disable. After 50 consecutive delivery failures, a subscription is switched off to stop hammering a dead endpoint. Re-enable it in the portal once you’ve fixed things; that resets the failure count.

Testing locally

Your endpoint has to be reachable over HTTPS, so localhost won’t work directly. Expose your local server with a tunnel:
Create a subscription pointing at the tunnel’s HTTPS URL, then upload a bill to trigger real events. The portal’s delivery list shows each attempt with its status, response code, and payload, and lets you replay a failed delivery while you iterate.

What each event sends

data is not the same shape for every event. Most give you the whole entity; a couple give you just enough to go and fetch it. Each event’s reference page shows its exact payload. Full entity — no follow-up call needed. The nested object is identical to what the matching GET endpoint returns. Full entity plus context — the entity, plus the specifics of what just happened. Summary or reference only — fetch the detail yourself if you need it.

Event catalog

Bills

Invoices

Three-way matching

See the three-way matching guide for what these mean in context.

Supplier credits

See the supplier credit notes guide for the full flow.

Companies

Next steps

Quick Start - AP

Upload a bill and drive it through review and approval.

Three-way Matching

Match purchase orders to bills and gate approval on variances.