Webhooks
Instead of you polling regularly, DebtFlow sends you an HTTP request as soon as something happens: invoice paid, letter delivered, debt moved to a new phase.
Setting up an endpoint
You create webhook endpoints in the dashboard under Settings → Webhooks or via the API with POST /webhook-endpoints (scope webhooks:write). You specify an https:// URL and the events you want. When the endpoint is created, you receive its signing secret once – keep it safe like a password.
curl -X POST 'https://www.debt-flow.io/api/v1/webhook-endpoints' \
-H "Authorization: Bearer $DEBTFLOW_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/webhooks/debtflow",
"events": [
"invoice.paid",
"debt.phase_changed"
]
}'The exact fields are listed in the API reference in the Webhooks section.
Structure of a delivery
Request to your server
POST /webhooks/debtflow HTTP/1.1
Content-Type: application/json
DebtFlow-Event-Id: evt_3kR9xY2mQ7pL0aB4cD1e
DebtFlow-Signature: t=1762248600,v1=5f2b0c…
{
"id": "evt_3kR9xY2mQ7pL0aB4cD1e",
"object": "event",
"type": "invoice.paid",
"apiVersion": "2026-11-01",
"createdAt": "2026-11-04T09:30:00.000Z",
"data": {
"object": { "id": "…", "status": "paid", "…": "…" }
}
}data.objectis the resource in the same format the REST API returns. For*.updated,data.previousshows which fields changed and how.idand theDebtFlow-Event-Idheader are unique per event. An event can arrive more than once – store processed IDs and skip repeats.- The order of delivery is not guaranteed.
createdAtis authoritative.
Verifying the signature
Every delivery is signed with the endpoint's secret. The DebtFlow-Signature header contains a timestamp t (Unix seconds) and the signature v1:
Header
DebtFlow-Signature: t=1762248600,v1=5f2b0c9e…- Build the string:
t, a dot, then the unmodified request body. - Compute HMAC-SHA256 with the secret and encode it as hexadecimal (lower case).
- Compare it in constant time with every
v1value. - Reject deliveries whose
tdiffers from your clock by more than 5 minutes – this prevents old requests from being replayed.
# Generate a signature yourself and test your own endpoint
BODY='{"id":"evt_test","object":"event","type":"webhook.test","apiVersion":"2026-11-01","createdAt":"2026-11-04T09:30:00.000Z","data":{"object":{}}}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$DEBTFLOW_WEBHOOK_SECRET" -hex | sed 's/^.* //')
curl -X POST 'https://example.com/webhooks/debtflow' \
-H 'Content-Type: application/json' \
-H "DebtFlow-Signature: t=$T,v1=$SIG" \
-d "$BODY"Most common mistake
The signature applies to the bytes DebtFlow sent. If the body is parsed as JSON and re-serialised first (whitespace, order, special characters), it no longer matches. Read the raw body.
Responses and retries
- Respond with a
2xxstatus as soon as the signature is valid, and process the event asynchronously afterwards. Long processing within the request leads to timeouts and retries. - On errors or without a response, DebtFlow retries the delivery at increasing intervals for up to three days.
- If delivery to an endpoint fails permanently, the endpoint is deactivated. You can see this in the settings and reactivate it there.
- An endpoint only receives events for which the API key or app that created it has the listed scope.
Events
| Event | Meaning | Scope |
|---|---|---|
contact.created | Contact created | contacts:read |
contact.updated | Contact updated | contacts:read |
contact.deleted | Contact deleted | contacts:read |
communication.created | Communication with a contact logged | contacts:read |
debt.created | Debt created | debts:read |
debt.updated | Debt updated | debts:read |
debt.phase_changed | Debt moves to another phase (dunning, court, enforcement …) | debts:read |
debt.overdue | Debt has become overdue (once per due date) | debts:read |
debt.paid | Debt paid in full | debts:read |
debt.deleted | Debt deleted | debts:read |
payment.received | Payment recorded for a debt | payments:read |
payment.deleted | Payment deleted | payments:read |
installment_plan.created | Instalment plan created | payments:read |
installment_plan.updated | Instalment plan updated (amount, instalments, status) | payments:read |
installment_plan.deleted | Instalment plan deleted | payments:read |
installment.due | Instalment falls due within the next 3 days (once per instalment) | payments:read |
installment.missed | Instalment not paid by its due date (once per instalment) | payments:read |
residence_registry_inquiry.created | Residents’ registration office request ordered (paid) | research:read |
recommendation.created | Creditor AI recommendation created | ai:read |
letter.sent | Letter handed over to the print service | letters:read |
letter.delivered | Letter delivered | letters:read |
letter.returned | Letter returned as undeliverable | letters:read |
letter.failed | Letter dispatch failed | letters:read |
invoice.created | Draft invoice created | invoices:read |
invoice.updated | Draft invoice updated | invoices:read |
invoice.finalized | Invoice finalised (number assigned) | invoices:read |
invoice.sent | Invoice sent (email or post) | invoices:read |
invoice.partially_paid | Partial payment on an invoice | invoices:read |
invoice.paid | Invoice paid in full | invoices:read |
invoice.overdue | Invoice overdue | invoices:read |
invoice.canceled | Invoice cancelled | invoices:read |
invoice.deleted | Draft invoice deleted | invoices:read |
invoice.handed_to_collection | Invoice handed over to dunning (debt created) | invoices:read |
credit_note.finalized | Credit note or cancellation invoice finalised | invoices:read |
offer.created | Quote created | offers:read |
offer.sent | Quote sent | offers:read |
offer.accepted | Quote accepted | offers:read |
offer.rejected | Quote rejected | offers:read |
offer.expired | Quote expired | offers:read |
offer.converted | Quote converted into an invoice or order confirmation | offers:read |
receipt.created | Receipt uploaded or received | receipts:read |
receipt.processed | OCR completed | receipts:read |
receipt.updated | Receipt updated | receipts:read |
receipt.booked | Receipt finalised | receipts:read |
receipt.deleted | Receipt deleted | receipts:read |
bank_transaction.imported | Bank transaction imported | banking:read |
bank_transaction.matched | Bank transaction matched to a receipt or invoice | banking:read |
project.created | Project created | projects:read |
project.updated | Project updated | projects:read |
project.archived | Project archived | projects:read |
project.deleted | Project deleted (only without time entries) | projects:read |
time_entry.created | Time entry recorded | time:read |
time_entry.updated | Time entry updated | time:read |
time_entry.deleted | Time entry deleted | time:read |
export.ready | Export (e.g. DATEV) ready for download | reports:read |
accounting_period.closed | Accounting period locked (GoBD) | accounting:read |
webhook.test | Test event from the webhook settings | webhooks:read |
The schemas of all events are also listed in the webhooks section of the OpenAPI description.