Webhook Retry
Fix the receiver before retrying a failed delivery. Retry is for eligible Error logs, not for re-running events already acknowledged successfully. Inspect canRetry / canRetryBulk from logs; they are eligibility hints, not a reservation against concurrent retries.
Single retry
curl -X POST 'https://app.chatt2.me/v1/organization/webhook/WEBHOOK_UUID/webhook-log/LOG_UUID/retry' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY'
{"success": true, "newLogId": "d4e5f6a7-b8c9-4123-def0-456789012345"}
success reports the new delivery result. A failed new delivery can still have newLogId: inspect that log. Each retry creates a child log; the original remains Error and becomes resolved when a descendant succeeds. Once an attempt has a child, retry the latest eligible attempt instead.
A validation log can be retried individually even to recover an inactive webhook. This issues a fresh empty-body token challenge; success activates/verifies the webhook. Ordinary event delivery requires the webhook to be active and its current access/delivery conditions to permit a send.
HTTP / stable code | Meaning and recovery |
|---|---|
400 CANNOT_RETRY_SUCCESSFUL_WEBHOOK_LOG | Already successful; recover failures from your own durable worker queue |
409 WEBHOOK_LOG_RETRY_IN_PROGRESS | Another attempt owns the retry/delivery lock; wait and refresh logs |
400 WEBHOOK_LOG_ALREADY_RETRIED | Use the latest eligible child |
400 WEBHOOK_LOG_PAYLOAD_UNAVAILABLE | No complete replayable stored payload; inspect logs/source data |
400 WEBHOOK_DELIVERY_NOT_STARTED | No HTTP attempt began; fix current access/delivery conditions and refresh eligibility |
| 401 / 404 | Invalid key or webhook/log outside this application's scope |
Bulk retry
Send 1–100 IDs with canRetryBulk: true. Validation logs do not support bulk retry.
curl -X POST 'https://app.chatt2.me/v1/organization/webhook/WEBHOOK_UUID/webhook-log/retry' \
-H 'x-api-key: YOUR_APPLICATION_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"webhookLogIds": ["b2c3d4e5-f6a7-4901-bcde-f23456789012"]}'
{
"results": [
{"webhookLogId": "b2c3d4e5-f6a7-4901-bcde-f23456789012", "success": true, "newLogId": "e5f6a7b8-c9d0-4234-ef01-567890123456"},
{"webhookLogId": "c3d4e5f6-a7b8-4012-cdef-345678901234", "success": false, "errorCode": "WEBHOOK_INACTIVE"}
]
}
A rejected item may have neither errorCode nor newLogId; inspect each result and current eligibility. Inactive webhook rejection can return WEBHOOK_INACTIVE. Bulk processing also respects per-log locks; it does not override an in-progress single/history retry.
Recovering a failure period
- Page through
status=Error&limit=50&offset=0, thenoffset=1, etc., usingpagination.totalPages. - Collect and deduplicate IDs with
canRetryBulk: trueinto a snapshot before starting retries. New retry logs change offset-based pages. - If the snapshot is empty, skip the POST. Split remaining IDs into batches of at most 100.
- Submit each batch and inspect every result/new log. Concurrent incoming failures can still move listing pages; rescan and deduplicate if completeness matters.
- Handle validation recovery separately using single retry. Refresh the binding and webhook's active state before replaying events.
What replay preserves
Event replay uses the stored body/envelope with its original event timestamp and expiry, including recorded delivery identity where available. It uses the webhook's current endpoint, headers, authorization and signing configuration, and generates a fresh HMAC timestamp/signature. Old payload time is not an old signing time. Updating a destination or credentials therefore affects subsequent retries. Receivers must deduplicate messages/provider events/delivery IDs where available; expiry may already be past when replay occurs.
No general automatic retry policy is promised for ordinary live events. WhatsApp Coexistence history delivery has a separate durable policy: at most eight HTTP attempts for transient failures (no HTTP response, 408, 429 or 5xx), with backoff and Retry-After respected; non-transient failures stop earlier. This history policy does not apply to every live event or create unlimited replay. History and manual retries share delivery coordination to avoid overlapping attempts.