FlaresendDocs

Delivery and retries

What counts as a successful delivery, how failed deliveries are retried, and how to see every attempt.

Each event becomes one delivery per matching webhook. Deliveries go through their own queue (flaresend-webhooks), so a slow or broken endpoint never holds up sending email.

What counts as success

Flaresend POSTs the event and waits up to 10 seconds for an answer.

Your endpointResult
Answers 200–299Success. The delivery is done.
Answers anything else, including 3xxFailed attempt. Redirects are not followed.
Doesn't answer within 10 secondsFailed attempt, recorded as timeout after 10s.
Can't be reached (DNS, TLS, connection refused)Failed attempt, with the error message recorded.

The first 1,024 characters of your response body are stored with the attempt, so return something short and useful, like ok or an error message.

Retry schedule

After a failed attempt, the delivery waits and tries again:

After attemptNext try in
130 seconds
22 minutes
310 minutes
430 minutes
51 hour
63 hours
76 hours
812 hours

The webhooks queue allows 8 retries. When they are used up, the delivery is marked failed and is not tried again. In total a delivery is tried for roughly a day before Flaresend gives up.

A delivery also ends as failed without being sent when the webhook was disabled or deleted before its turn. Its response body says webhook disabled or webhook deleted.

Duplicates and order

  • The same event can reach you more than once, for example when your endpoint did the work but answered too slowly. Every attempt carries the same Flaresend-Event-Id and Flaresend-Delivery-Id headers. Store the event ID and skip ones you have seen.
  • Events are not guaranteed to arrive in order. A retried email.sent can arrive after email.delivered. Use createdAt, and the email's own status from Retrieve an email when you need the current state.

The deliveries log

Every delivery and its last attempt are stored. See them on the webhook's page in the dashboard, or list them with the API:

const { data, nextCursor } = await flaresend.webhooks.deliveries('wh_01K6B1X9Q2M7T4V8N3R5P6W1ZA', { limit: 50 });

for (const d of data) {
  console.log(d.eventType, d.status, d.attempt, d.responseCode, d.nextAttemptAt);
}

Each record has:

FieldMeaning
statuspending (waiting for its first try or a retry), success or failed
attemptHow many attempts have been made
responseCodeThe HTTP status of the last attempt, or null if there was no response
responseBodyUp to 1,024 characters of the last response, or the error
nextAttemptAtWhen the next retry is due, while pending
completedAtWhen it succeeded or finally failed

See List deliveries.

Send a test event

Use Send test event on the webhook's page, or:

const { deliveryId } = await flaresend.webhooks.test('wh_01K6B1X9Q2M7T4V8N3R5P6W1ZA');

The test event is an email.delivered event with made-up values and "test": true in data. It goes to this one webhook even if the webhook doesn't subscribe to email.delivered, and it is signed, retried and logged like a real delivery. The webhook must be enabled, or the call fails with 409 webhook_disabled.

When your endpoint is down

Nothing is lost while your endpoint is down for less than about a day: pending deliveries keep retrying on the schedule above. After a longer outage, read what you missed from the events API instead of webhooks:

const { data } = await flaresend.events.list({ since: '2026-09-26T00:00:00Z', type: 'email.bounced' });

See List events.

On this page