> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paywise.de/llms.txt
> Use this file to discover all available pages before exploring further.

# Delivery & Retries

> Understand how webhook deliveries work, including retries and failure handling

<Warning>
  This page documents a frozen v1 API. It receives only critical correctness or
  security corrections. For new integrations, use the [current API](/api-docs/overview)
  and follow the linked [migration guide](/api-docs/legacy/overview#migration-guides).
</Warning>

## Delivery Format

Each webhook delivery is an HTTP POST request with the following headers:

| Header | Description |
| - | - |
| `Content-Type` | `application/json` |
| `User-Agent` | `paywise-webhooks/1.0` |
| `X-Paywise-Signature` | HMAC SHA-256 signature (see [Signature Verification](/api-docs/legacy/case-management/webhooks/signature-verification)) |
| `X-Paywise-Event` | The event type (e.g. `mandate.created`) |
| `X-Paywise-Delivery-ID` | Unique ID of the delivery; every retry of the delivery repeats it |
| `X-Paywise-Environment` | `production`, or `sandbox` for deliveries from the paywise sandbox |

## Payload Structure

```json theme={null}
{
  "data": {
    "company": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    ...
  },
  "environment": "production",
  "event": "mandate.created",
  "event_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "timestamp": "2026-04-24T10:30:45.123456Z"
}
```

| Field | Type | Description |
| - | - | - |
| `event` | string | The event type that triggered this webhook |
| `event_id` | string | UUID of the event; every delivery and retry of the same event carries the same value |
| `timestamp` | string | ISO 8601 timestamp of when the event was processed |
| `environment` | string | `production`, or `sandbox` for deliveries from the paywise sandbox; the same value as the `X-Paywise-Environment` header |
| `data` | object | Event-specific payload |
| `data.company` | string | UUID of the company this event belongs to |

The body is sent as compact JSON with sorted keys and no whitespace; it is
shown formatted here. Verify the signature over the raw request body exactly as
received.

## Success Criteria

A delivery is considered **successful** when your endpoint returns any HTTP status code in the **2xx range** (200–299).

<Tip>
  Return HTTP 200 as quickly as possible. If you need to do heavy processing, acknowledge the webhook immediately and process the event asynchronously.
</Tip>

## Timeouts

paywise waits up to **30 seconds** for a response. If your endpoint does not respond within this window, the delivery is marked as failed and may be retried.

Redirects are **not followed** — your endpoint must return a 2xx response directly.

## Retry Logic

Failed deliveries are automatically retried with exponential backoff:

| Attempt | Delay After Failure |
| - | - |
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 15 minutes |

After 3 failed retry attempts, the delivery is permanently marked as `failed`.

## Delivery Statuses

| Status | Description |
| - | - |
| `pending` | Delivery created, awaiting first attempt |
| `success` | Endpoint returned a 2xx response |
| `retrying` | Delivery failed but will be retried |
| `failed` | All retry attempts exhausted |

## Auto-Disable

Webhook endpoints track the number of **consecutive failures** across all deliveries. When the failure count reaches the configured threshold (default: **50**), the endpoint is automatically disabled.

* **Disabled endpoints** stop receiving events entirely
* **Re-enabling** an endpoint (via API or dashboard) resets the failure counter to 0
* You can configure the threshold via the `max_consecutive_failures` field (1–1000)

<Info>
  A single successful delivery resets the consecutive failure counter to 0. The auto-disable only triggers after an unbroken streak of failures.
</Info>

## Idempotency

Each delivery record carries an `idempotency_key`. For a delivery created for an event it is `event:` followed by a SHA-256 hash of the webhook endpoint, the event type and the event, so it stays the same across retries and each endpoint receives exactly one delivery per event. A delivery created by a test event (`POST /v1/webhooks/{id}/test/`) uses the form `{webhook}:{event}:{timestamp}`.

To detect a duplicate on your side, use the `X-Paywise-Delivery-ID` header, which every attempt of a delivery repeats, or the payload's `event_id`, which every delivery of an event repeats.

## Best Practices

1. **Respond quickly** — Return HTTP 200 immediately and process events asynchronously
2. **Verify signatures** — Always validate the `X-Paywise-Signature` header before processing
3. **Handle duplicates** — Use the delivery ID or idempotency key to detect and skip duplicates
4. **Monitor failures** — Check your endpoint's failure count regularly via the API or dashboard
5. **Use test mode** — Verify your integration using test events before subscribing to production events


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.