Documentation menu

Delivery and retries

What EventSend promises about delivering an event: the retry schedule, the 24-hour freshness ceiling, which failures are retried, duplicates, and replays.

A 202 from the events API means your event is queued. This page is the rest of the promise: what happens between "queued" and "delivered", how hard EventSend tries, and what you should build your side against.

Attempts and the retry schedule

Every destination has a Retries setting from 0 to 4. Retries plus the first try is the destination's attempt budget, so the default of 4 gives five attempts. When an attempt fails in a way that is worth retrying, the next one waits according to a fixed schedule:

After attempt Waits Retry happens at
1 30 seconds 0:30
2 2 minutes 2:30
3 4.5 minutes 7:00
4 8 minutes 15:00

So with the default budget EventSend keeps trying for about fifteen minutes. An outage shorter than that is absorbed and your event arrives late; one longer than that fails the delivery, with a record in the event's history that you can replay once things are healthy again. Each wait can run up to ten percent longer than the table says, never shorter, so the times in the last column are floors.

Two things override the table. If the destination answers 429 — or a 503 with a Retry-After header — EventSend waits exactly what the header says instead of the scheduled delay; that answer still counts as one of the attempts, so an endpoint that rate-limits every request will exhaust the budget. (Pushover is the one exception: its 429 means the account's message allowance is spent, so the delivery fails, and until the allowance resets later deliveries to that destination fail the same way without a request being made.) And a destination that is held — behind a quiet-hours window, a circuit that is still open from an earlier failure, or a provider-wide slowdown EventSend is pacing — is not being retried at all: waiting for capacity never spends an attempt.

Which failures are retried

For a webhook destination:

Your endpoint What EventSend does
Any 2xx Delivered. Nothing else is sent for this event.
429 Retried after Retry-After (3 seconds if the header is missing). Counts as an attempt.
Any 5xx Retried on the schedule.
Connection refused, TLS failure, DNS lookup failure, timeout Retried on the schedule.
401, 403, 404 Failed, and the destination is deactivated — the endpoint is gone or refusing us, and retrying will not change that. Fix it, then re-enable the destination.
Any other 4xx Failed, not retried. A 400 or 422 says the request itself is wrong, which a retry will not fix.
A hostname that resolves to a private address Refused before sending, not retried.

Chat destinations follow the same shape with the provider's own vocabulary: a Discord or Telegram server error or network failure is retried on the schedule, a rate limit waits the provider's Retry-After (a Discord rate limit never spends an attempt), and a revoked or invalid credential ends delivery and marks the connection so you can see why. Push destinations do too: a Pushover or Web Push server error or network failure is retried, and a browser subscription the push service reports as gone (404 or 410) ends delivery and removes that subscription — re-subscribe from Destinations → Push notifications on that device. Slack is stricter: a network failure that happened before the message was sent is retried, but a Slack server error or a failure after the message may have been posted is recorded as outcome unknown and not retried — see "Duplicates" below for why.

The freshness ceiling

A notification is a claim about now. A delivery is never sent more than 24 hours after its event was created: once it is that old — because a destination was paused for a day, or failing for a day — it is skipped as too old to send rather than delivered stale, and the event's history says so. Nothing was sent and nothing is wrong with the destination.

The clock runs from the event, not from the last attempt. A delivery that has been failing for six hours is six hours old however recently it was tried. Slack destinations are stricter on top of this: a critical event expires after 15 minutes, error and warning after an hour, and everything else after six hours, because a channel full of stale alerts is worse than a gap.

Duplicates, and how to be safe against them

EventSend's delivery model is at least once: a queued event is delivered to each matching destination at least once, or ends in a delivery record that says why it was not. The case that produces a duplicate is an ambiguous attempt: the request was sent, and then the connection dropped or timed out before any answer came back. Your endpoint may have processed it. EventSend cannot know, so it retries — a dropped event is a worse failure than a repeated one — and rarely, that retry is a second copy. An infrastructure restart that interrupts an attempt is treated the same way: the delivery is re-queued and finishes on its own, which is another path to that rare second copy rather than a lost event.

Build your endpoint to be idempotent on the event's id. It is the stable evt_… identifier in every signed payload, it never changes across retries or replays, and it is exactly what a "seen before" check should key on. Keep the ids you have handled for as long as your plan retains events (7 days on Free, 30 on Starter, 60 on Growth, 90 on Scale): automatic retries finish within a day, but a replay of an event can be requested at any point in that window and carries the same id. Answer 2xx when an id you have seen comes back.

Slack is the one exception to "retried": a Slack message with an ambiguous outcome is not retried automatically, because a duplicate in a channel cannot be deduplicated on your side. The delivery is recorded as outcome unknown and the retry is yours to make, by replaying it.

Replays

Any failed delivery can be replayed from the event's page — one at a time, or every failed delivery to a destination within a date range. A replay is a new delivery: it gets the full attempt budget and the full schedule, it is delivered with the destination's current configuration, and its freshness clock starts when you replay it, not when the original event happened. Fixing an integration and resending yesterday's failures works exactly as you would expect.

A replay is a deliberate second send, so an endpoint that has already handled the event will see its id again — days or weeks after the original, which is why the dedup check above has to keep ids for the whole retention window rather than a day.

Reading the delivery log

Every attempt, wait, skip and outcome is recorded against the delivery, and the event's page shows them per destination: the attempt count, the last status code, the response time, and for a skipped delivery, the reason it was skipped. A destination whose deliveries keep failing shows its failure count on the destinations page.