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

# Retries & Idempotency

> Delivery guarantees, retry policy, and how to make your endpoint idempotent.

## Delivery behavior

* We `POST` to your callback URL and treat **HTTP 200/201/202** as delivered.
* On non-2xx or network/timeout errors, we **retry** after a short delay, up to a small maximum number of attempts.
* After the maximum is reached, we stop retrying.

> **Your responsibility:** Always return a 2xx once you’ve **safely persisted** the event.

### Default retry schedule (service)

* **Max attempts:** 3 (initial send + up to 2 retries)
* **Delay between attempts:** 60 seconds via delay queue
* **Success criteria:** HTTP 200/201/202

## Building idempotency

Webhooks may arrive **more than once** (e.g., retries, network jitter). Implement idempotency using any of:

* **Dedup key**: `payment_uuid` (primary) and/or `transaction_reference` (your `client_reference`).
* **Upsert** logic keyed by `payment_uuid`, updating status if it changes.
* **Exactly-once side effects**: persist first, enqueue internal work, then return 2xx.

### Recommended database structure

| Column               | Purpose                     |
| -------------------- | --------------------------- |
| id (auto)            | Internal primary key        |
| payment\_uuid (uniq) | Dedup key                   |
| client\_reference    | Your reference              |
| last\_status         | Last delivered status       |
| last\_code           | Code mapped to status       |
| payload\_json        | Raw payload for audit/debug |
| processed\_at        | When you confirmed delivery |

See [Webhook payload](/pages/webhooks/payload) for the full field list.
