Delivery and Retries

What counts as a delivered webhook, how Tonder retries a failed one, and how to resend events yourself.

Tonder treats an event as delivered when your server answers any 2xx in under 10 seconds. If it doesn't, Tonder retries automatically — up to 6 attempts over about 8.5 hours — and you can resend any event yourself from the Events log.

Webhooks 2.0 is enabled per account. If Developers in your dashboard has no Events tab, your account is on the previous version: ask your Tonder contact to enable it.

Without Webhooks 2.0, each event is delivered once.

Retry schedule

AttemptWait since the previous attempt
1Immediate
21 minute
35 minutes
430 minutes
52 hours
66 hours

Each wait varies by ±10% so retries don't all land on your server at once. If the 6th attempt fails, the event is Failed.

A retrying event and its attempts

How Tonder reads your response

Your server answersWhat happens
Any 2xx within 10 secondsDelivered. No more attempts.
429 or 503 with a Retry-After headerThe next attempt waits as long as you ask, up to 6 hours.
410 GoneThe endpoint no longer exists: the event is Failed, with no more attempts.
Any other code, a timeout or a connection errorRetried on the schedule above.

A disabled or deleted endpoint gets no retries: its pending events end as Failed with "Endpoint unavailable".

Resend events yourself

One event. In the Events log, open the event and click Resend event.

  • If the event was still being retried and your resend fails, it keeps the automatic attempts it had left.
  • If it was already Failed or Delivered, the resend is a single attempt.
  • You can't resend while an automatic attempt is about to go out (±1 minute), or twice within 30 seconds.

Every failed event. Retry all resends the Failed events that match the current filter — up to 5,000, at 10 per second.

Manual resends count toward the total attempts but not toward the 6 automatic ones, so a counter can read 7/6.

What this means for your integration

  • Answer 2xx immediately, then process in your own queue — you have 10 seconds.
  • Deduplicate on the webhook-id header. A retry or a resend carries the same webhook-id.
  • Don't rely on arrival order. A retried payment_Pending can arrive after payment_Success; use the payload's status and created.
  • After an outage, check the Events log once your server is back: anything left Failed can be resent.
  • Dedupe on the webhook-id header with Webhooks 2.0 — a retry or a resend carries the same one. Without Webhooks 2.0, dedupe on id + event_type. Correlate with metadata.external_id, which repeats on every event of a transaction and works across Short + Long formats.
  • Keep processed webhook-ids for at least 60 days — an event can be resent while it is kept.
  • Keep an idempotency_key index in your DB.
  • Treat duplicate Pending + Success in Hybrid mode as intentional.
  • Always verify final status via GET /api/v1/transactions/{id}/.
  • Handle all five withdrawal terminal states — Success, Declined, Cancelled, Failed and Returned; Returned arrives after Success.

Without Webhooks 2.0

Accounts on the previous version get each event once. The response is recorded, but it does not trigger a redelivery:

ResponseWhat we do
2xx within 30 secondsDelivery recorded as successful.
4xx (400, 401, 404, etc.)Delivery recorded as failed. An internal alert is raised. The event is not resent.
5xx (500, 502, 503, etc.)Delivery recorded as failed. An internal alert is raised. The event is not resent.
Timeout (no response after 30 seconds)Delivery recorded as failed. An internal alert is raised. The event is not resent.

Reconcile by polling GET /api/v1/transactions/{id}/ for any period during which your endpoint was unavailable or returning errors, and if you detect lost events, contact support with the date range. Deduplicate on id + event_type: metadata.external_id repeats on every event of the same transaction, so it correlates but cannot tell Pending from Success.

Do not treat the webhook as your only source of truth. A payment or withdrawal whose notification you never received still reached its final status — the transaction endpoint always has it.

Next steps

Was this page helpful?

On this page