Webhooks
Receive real-time notifications when case events occur instead of polling the API.
This guide assumes you've completed the Quickstart.
Overview
Webhooks push event data to your endpoint as cases progress through collection. Subscribe to specific events to keep your systems synchronized without repeated API calls.
Available Events
| Event | Description |
|---|---|
case.created | New case submitted |
case.updated | Case lifecycle state changed |
case.closed | Case resolved (paid, written off, or otherwise closed) |
payment.created | Payment registered on a case |
payment.deleted | Payment deleted (reversed) on a case |
chat.created | Chat message added to a case |
Create a Webhook Subscription
You can create webhooks via the API (shown below) or through the Debitura portal. See Webhooks in the portal for UI instructions.
POST https://customer-api.debitura.com/webhooks
Content-Type: application/json
XApiKey: YOUR_API_KEY
{
"url": "https://your-domain.com/webhooks/debitura",
"events": ["case.created", "case.updated", "case.closed", "payment.created"],
"isTestMode": false
}
Request body parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
url | string | — | The HTTPS endpoint to deliver events to |
events | string[] | — | Event types to subscribe to (see Available Events) |
isTestMode | boolean | false | When true, this subscription only receives webhook events from test cases. When false (default), it receives events from live production cases. Can be changed later via PATCH /webhooks/{id} with isTestMode. |
During integration development, set "isTestMode": true to receive events from test cases only — your live production data is unaffected. See Test vs Live subscriptions for details.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://your-domain.com/webhooks/debitura",
"events": ["case.created", "case.updated", "case.closed", "payment.created"],
"isActive": true,
"isTestMode": false,
"createdUtc": "2026-01-31T15:23:45Z",
"updatedUtc": "2026-01-31T15:23:45Z",
"disabledReason": null,
"secret": "dGhpc2lzYWJhc2U2NGVuY29kZWRzZWNyZXQ="
}
The secret is returned only once during creation. Store it immediately. You cannot retrieve it later.
To regenerate: PATCH /webhooks/{id} with "regenerateSecret": true.
Event Payload Structure
All events use a CloudEvents-inspired format:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"specVersion": "1.0",
"event": "case.created",
"timestamp": "2026-01-31T15:23:45Z",
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W"
},
"links": {
"self": "https://customer-api.debitura.com/cases/123e4567-e89b-12d3-a456-426614174000"
}
}
| Field | Description |
|---|---|
id | Unique event ID—use for idempotency |
event | Event type |
timestamp | When the event occurred (ISO 8601 UTC) |
data | Event-specific payload |
links.self | API URL for the affected resource |
HTTP Headers:
| Header | Value |
|---|---|
X-Debitura-Signature | t={timestamp},v1={signature} |
X-Debitura-Timestamp | Unix timestamp |
The event type is available in the JSON body under the event field.
Event Payloads
case.created
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"lifecycle": "Pending contract signing"
}
}
| Field | Description |
|---|---|
caseId | Debitura's internal case ID |
reference | Debitura case reference (e.g. Q8OAXF3W) |
lifecycle | Initial lifecycle state — useful when you need to know whether contracts are still pending without a separate API call. See Case Lifecycle for all possible values. |
case.updated
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"oldLifecycle": "Active",
"newLifecycle": "Pending Verification"
}
}
See Case Lifecycle for all lifecycle values and their meanings.
case.closed
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"closeCode": "Paid",
"closeComment": "Paid in full by debtor"
}
}
See Case Lifecycle for all close codes and their meanings.
payment.created
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"paymentId": "789e4567-e89b-12d3-a456-426614174999",
"amount": 5000.00,
"currency": "EUR",
"date": "2026-01-31T12:00:00Z"
}
}
payment.deleted
Fires when a payment is deleted (reversed) on your case — for example a client correcting a payment that was recorded with the wrong amount. Same payload shape as payment.created; date still reflects the original payment date, not when it was deleted (use the event's top-level timestamp for that).
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"paymentId": "789e4567-e89b-12d3-a456-426614174999",
"amount": 5000.00,
"currency": "EUR",
"date": "2026-01-31T12:00:00Z"
}
}
chat.created
{
"data": {
"caseId": "123e4567-e89b-12d3-a456-426614174000",
"reference": "Q8OAXF3W",
"chatId": "456e4567-e89b-12d3-a456-426614174888",
"role": "Partner",
"message": "Please provide updated payment plan"
}
}
Role values: Partner (collection partner), Creditor (you), ManagedByPartner (partner acting on your behalf)
Signature Verification
Verify every webhook request using webhook signature verification. The signature proves the request came from Debitura and wasn't tampered with.
Quick summary:
- Extract
t(timestamp) andv1(signature) fromX-Debitura-Signature - Construct:
signed_payload = timestamp + "." + raw_json_body - Decode your secret from Base64
- Compute HMAC-SHA256 of signed payload
- Compare signatures using constant-time comparison
- Verify timestamp is within 5 minutes
Processing Best Practices
Respond Quickly
Return 2xx within 10 seconds. Verify the signature, acknowledge immediately, then process the event asynchronously via a queue or background job.
Idempotent Processing
Use the id field to detect duplicates—Debitura may retry delivery. Store processed event IDs and skip duplicates. See idempotent webhook processing for implementation patterns.
Retry Behavior
Debitura retries failed deliveries with exponential backoff:
| Attempt | Delay | Cumulative |
|---|---|---|
| 1 | 0s | 0s |
| 2 | ~60s | ~1 min |
| 3 | ~120s | ~3 min |
| 4 | ~240s | ~7 min |
| 5 | ~480s | ~15 min |
| 6 | ~960s | ~31 min |
| 7 | ~1800s | ~1 hour |
| 8 | ~1800s | ~1.5 hours |
After 8 consecutive failures (1 initial delivery + 7 retries), the webhook is automatically disabled. Fix your endpoint and re-enable via the API.
A 410 Gone response immediately disables the webhook with reason "Endpoint returned 410 Gone (endpoint retired)".
Managing Webhooks
List subscriptions:
GET https://customer-api.debitura.com/webhooks
Update subscription:
PATCH https://customer-api.debitura.com/webhooks/{id}
{
"url": "https://new-endpoint.com/webhooks",
"isActive": true
}
The PATCH body accepts url, isActive, isTestMode, and regenerateSecret. Event subscriptions are immutable — sending events in a PATCH returns 400 (WebhookEventsImmutable). To change which events a subscription receives, delete it and create a new one.
Delete subscription:
DELETE https://customer-api.debitura.com/webhooks/{id}
Test delivery:
POST https://customer-api.debitura.com/webhooks/{id}/test
Test deliveries include X-Debitura-Test: true header.
Webhook action endpoints use a slash sub-route (/test, /replay). An older colon form ({id}:test) still works, but only if you percent-encode the colon as %3A ({id}%3Atest) — a literal : returns 404. Use the slash form; it is canonical.
Delivery History & Replay
To inspect what's been delivered to a subscription, or to re-deliver a specific event, use the webhook events endpoints:
List delivered events for a case:
GET https://customer-api.debitura.com/webhooks/events?caseId={caseId}
XApiKey: YOUR_API_KEY
Replay a specific event:
POST https://customer-api.debitura.com/webhooks/events/{eventId}/replay
XApiKey: YOUR_API_KEY
This re-enqueues that exact payload for redelivery to the subscription's current endpoint. The endpoint targets this specific subscription only — it does not affect other subscriptions on your account.
Auto-generated API reference: List webhook events dispatched for a case, Replay a specific webhook event.
POST /webhooks/{id}/replayAn older, differently-shaped endpoint at this same path only counts case.created events since a timestamp — it does not redeliver anything, despite the name. It's kept for backward compatibility but shouldn't be used going forward; use the two endpoints above instead. See the WebhookEvents reference for details.
Testing Webhooks in CI
For running automated webhook tests, Debitura provides a full CI loop: create a test case, advance it through lifecycle states, inspect delivery history, and clean up — without pointing at an external receiver. The Customer API is the primary example used throughout that guide. See CI Testing for the full walkthrough and code examples.
Troubleshooting
Webhook not received
Your endpoint must be publicly accessible via HTTPS with a valid TLS certificate. Private IP addresses and localhost are blocked.
Signature verification fails
Ensure you're using the raw request body (not parsed JSON) and decoding the secret from Base64 before computing HMAC.
Webhook disabled
Check disabledReason in the webhook details. Fix the underlying issue, then update with "isActive": true.
For a fuller error-message reference, see Webhook Troubleshooting.