Retry policy
DPay Web EID uses retries to provide reliable webhook delivery. This guide explains how retries work, when an endpoint is disabled automatically and how to ensure idempotency.
Delivery diagram
/api/v1/webhooks/{id}/testTrigger a test delivery to verify an endpoint after fixing it.Full contract in the API Reference
Retry schedule
If your endpoint does not return a 2xx response within 10 seconds, the webhook is retried according to this schedule:
| Attempt | Delay | Time since first attempt |
|---|---|---|
| 1 | immediately | 0 |
| 2 | 10 seconds | 10s |
| 3 | 1 minute | ~1 min |
| 4 | 5 minutes | ~6 min |
| 5 | 30 minutes | ~36 min |
After five failed attempts, the delivery is marked as failed and failure_count is incremented.
What counts as success?
- 2xx (
200,201,202,204) - success; retries stop - Other codes (
3xx,4xx,5xx) - failure; retry according to the schedule - Timeout over 10 seconds - failure; retry
- Connection refused / DNS error - failure; retry
Automatic disabling after 50 failures
After 50 consecutive failed deliveries, the endpoint is disabled automatically and disabled_at is set to the current timestamp. The webhook endpoint is no longer called.
To reactivate an endpoint after it has been disabled:
- Determine why deliveries failed by checking server logs and monitoring
- Fix the issue, for example by restoring the endpoint or increasing capacity
- Send
PATCH /webhooks/{id}with{"is_active": true}; this also resetsfailure_count - Optionally run a test with
POST /webhooks/{id}/test
Monitoring fields
| Field | Description |
|---|---|
failure_count | Consecutive failed delivery count, reset after a successful delivery |
last_triggered_at | Most recent delivery attempt |
last_success_at | Most recent successful delivery |
last_failure_at | Most recent failed delivery |
disabled_at | Automatic disabling date; null while active |
Idempotency
Your endpoint must be idempotent. Because of retries, network behavior or timeouts, it may receive the same event more than once. Processing the same event multiple times must not cause errors or duplicate operations.
Implementing idempotency
The simplest approach is to deduplicate by X-Webhook-Id:
<?php
$webhookId = $_SERVER['HTTP_X_WEBHOOK_ID'] ?? '';
// Check whether the event has already been processed
if (alreadyProcessed($webhookId)) {
http_response_code(200);
exit('Already processed');
}
// Process the webhook...
processEvent($event);
// Mark as processed
markAsProcessed($webhookId);
http_response_code(200);
echo 'OK';
Alternative - deduplicate by session and status
<?php
$sessionId = $event['data']['verification_session_id'];
$status = $event['data']['status'];
$existing = getOrder($sessionId);
if ($existing && $existing['status'] === 'verified') {
// Already verified - do nothing
http_response_code(200);
exit('OK');
}
// Update the status
updateOrderStatus($sessionId, $status);
http_response_code(200);
Best practices
- Respond quickly (under 1s) - perform only essential work synchronously and queue the rest
- Use a queue such as Redis, RabbitMQ or SQS - accept the webhook, enqueue it, return 200 and process it in a worker
- Log webhooks - record
X-Webhook-Id, the payload and response time for diagnostics - Monitor
failure_count- alert when a webhook has more than five consecutive failures - Test before production - use
POST /webhooks/{id}/testto verify the configuration - Always verify the signature - see HMAC signature
What if a webhook was not delivered?
If a webhook was not delivered, for example because your endpoint was unavailable for more than 36 minutes, you can always use:
- Polling - periodically check the session status with
GET /verifications/{id} - Reconciliation - compare the
GET /verificationssession list with your local database each day - Panel - retrieve the result manually in the administration panel
Webhooks are the fastest method, but they are not the only source of truth.