# Webhook Retry Policy

When Pay.io delivers a webhook to your configured endpoint, it expects a **`2xx` response** within **5 seconds**. If no valid response is received within that window, the delivery is considered failed and Pay.io will **automatically** **attempt** **redelivery** according to the [retry schedule below](#automatic-retries).

### Delivery Lifecycle

Every webhook delivery follows the same progression from initial attempt through to escalation:

{% stepper %}
{% step %}

#### Initial delivery

`POST` sent instantly to your configured endpoint. A `2xx` response is expected within 5 seconds.
{% endstep %}

{% step %}

#### Automatic retries (if initial delivery fails)

Up to 50 attempts

Linear back off kicks in when the initial delivery fails. Each attempt waits longer before retrying. See the [table of attempts and delays below](#automatic-retries).
{% endstep %}

{% step %}

#### Manual retries (if webhook status is `CALLBACK_FAILED`)

Up to 3 merchant-initiated attempts via the Merchant Console. When status is `CALLBACK_FAILED`, the Merchant can re-trigger delivery from the Merchant Console at any time. Tracked independently of automatic retries.

Available for Deposits and Payouts.
{% endstep %}

{% step %}

#### Support escalation

If all automatic and manual attempts exhausted. Contact Pay.io support to manually re-trigger delivery.
{% endstep %}
{% endstepper %}

### Automatic Retries

Failed webhook deliveries are retried automatically using a **linear backoff** strategy. The delay between each attempt is calculated as:

```
delay = attempt number × 10 minutes
```

| Attempt | Delay After Previous Failure |
| ------- | ---------------------------- |
| 1       | 10 minutes                   |
| 2       | 20 minutes                   |
| 3       | 30 minutes                   |
| 4       | 40 minutes                   |
| …       | …                            |
| 50      | 500 minutes (\~8.3 hours)    |

A maximum of **50 retry attempts** are made. Once all attempts are exhausted, the webhook is marked as **permanently failed** and no further automatic deliveries will be attempted.

### Manual Retries

In addition to automatic retries, merchants can manually re-trigger webhook delivery from the **Merchant Console** at **any time** whilst the callback status is `CALLBACK_FAILED`.

#### **Manual retry rules**

* Manual retries are available for both **Deposits** and **Payouts** with a status of `CALLBACK_FAILED`.
* A maximum of **3 manual retry attempts** are permitted **per transaction**.
* Manual retries are tracked independently of automatic retries — exhausting automatic attempts does not consume your manual retry allowance, and vice versa.
* Once all 3 manual retry attempts have been used, contact Pay.io support to request further assistance with redelivery.

***

### Best Practices

**Respond immediately, process asynchronously**

Your endpoint should return a `2xx` status code as soon as the webhook payload is received — even if you have not yet finished processing it. Queue the payload for processing in the background. Delaying your response to complete processing first risks exceeding the 5-second response window and triggering the retry cycle unnecessarily.

**Set up internal alerts for early-stage failures**

Automatic retries are designed to handle transient endpoint issues. However, as the attempt number increases, so does the delay before the next retry — reaching over 8 hours by attempt 50. Waiting for the automatic retry schedule to resolve a failure is rarely the right approach for time-sensitive transactions such as deposits or withdrawal confirmations.

{% hint style="success" %}
**We strongly recommend** configuring your own internal monitoring to alert your team when a webhook enters `CALLBACK_FAILED` status. This allows you to assess and resolve the underlying issue promptly, and to use your **manual retry allowance** to redeliver the webhook as soon as your endpoint is healthy — rather than waiting hours for the next automatic attempt.
{% endhint %}

A practical alerting threshold to consider:

| Automatic Attempt Reached | Cumulative Delay          | Recommended Action                              |
| ------------------------- | ------------------------- | ----------------------------------------------- |
| 3                         | \~60 minutes              | Investigate endpoint health                     |
| 5                         | \~150 minutes             | Trigger internal alert to your engineering team |
| 10                        | \~550 minutes (\~9 hours) | Escalate and initiate manual retry immediately  |

**Use manual retries before automatic attempts are exhausted**

Your 3 manual retry attempts are a valuable tool for recovering from delivery failures quickly — particularly when the automatic retry schedule has reached a long delay window. Do not wait until automatic retries are fully exhausted before intervening. If your endpoint is healthy and a webhook has been waiting for hours, trigger a manual retry from the Merchant Console to resolve the delivery immediately.

{% hint style="info" %}
Manual and automatic retry attempts are tracked independently. However, once both are exhausted for a given transaction, Pay.io will not attempt further automatic redelivery. If you reach this state, contact Pay.io support as soon as possible.
{% endhint %}

**Ensure your endpoint is idempotent**

Because the same webhook event may be delivered more than once — across both automatic and manual retry attempts — **your endpoint must be built to handle duplicate deliveries safely**. Use the **transaction ID** included in the webhook payload as an idempotency key to ensure that processing the same event multiple times does not result in duplicate operations on your platform.
