Retries & Timeouts
Retry Behavior
Retry behavior depends on whether the webhook is synchronous or asynchronous — they use two different delivery paths.
WITHDRAW_REQUEST
WITHDRAW_REQUEST is delivered synchronously by default, but asynchronously (via the durable outbox, same as below) when your operator is on external-approval mode — in that mode the decision comes from your POST /withdraw/:id/decision callback, not from the webhook response. See Withdrawals.
Synchronous mode: delivered inline while AbstraPay waits for your approve/deny response. The inline client retries up to 2 times with a 5-second delay between attempts. Only network-level errors are retried:
- Connection reset (
ECONNRESET) - Timeout (
ETIMEDOUT) - Connection refused (
ECONNREFUSED) - Host unreachable (
EHOSTUNREACH) - HTTP 5xx, 408, 429 responses
Application-level errors (non-zero error code in the response) are not retried.
Asynchronous — DEPOSIT and WITHDRAW_COMPLETE
These events are delivered through a durable outbox, not inline. If your endpoint is unreachable or returns an error, the outbox retries up to 5 attempts with exponential backoff (~20s, then 40s, 80s, … capped at 15 minutes between attempts). After the attempts are exhausted, the event is moved to a dead-letter queue for manual retry from the management panel (see Management Panel) — nothing is silently dropped.
Make your handler idempotent. Because the outbox retries, the same DEPOSIT or WITHDRAW_COMPLETE event may be delivered to you more than once. Deduplicate deposits on idempotencyKey and withdrawal completions on withdrawRequestId (the withdrawal UUID — the same id sent on WITHDRAW_REQUEST; a separate sessionId field carries the session), and ignore duplicates, so you never credit or debit a user twice.
Timeouts
| Timeout | Value |
|---|---|
| Connect | 10 seconds |
| Headers | 10 seconds |
| Body | 15 seconds |
Ensure your endpoint responds within these limits.