# Retries and duplicates

> FinchNode retries a failed delivery for about 41 hours over eight attempts, so your endpoint must handle repeats and out-of-order events.

## Expect duplicates

FinchNode writes each event before sending it, and retries until your endpoint answers `2xx`. You will get some events more than once. Deduplicate on the event's `id`, which the signature covers, as [Receive and verify](/docs/webhooks/receive-and-verify) shows.

## Plan for retries

A delivery fails when your endpoint answers outside `2xx`, or sends nothing for five seconds. FinchNode tries up to eight times in all, waiting longer each time:

| After attempt | Next try in |
| --- | --- |
| 1 | 30 seconds |
| 2 | 2 minutes |
| 3 | 10 minutes |
| 4 | 1 hour |
| 5 | 4 hours |
| 6 | 12 hours |
| 7 | 24 hours |

After the eighth failure the event moves to dead letters.

## Avoid instant dead letters

Some failures skip the retries and dead-letter at once:

- Your endpoint answers `404` or `410`.
- The URL no longer passes FinchNode's safety checks.
- The application is suspended.

A deploy that briefly answers `404` loses those events. Answer `503` while you deploy, so FinchNode retries.

## Replay from the console

The application's page in the console shows its three most recent deliveries, each with its status and attempt count. A failed or dead-lettered one has a Replay button; use it once your endpoint is fixed.

Older deliveries don't show there. To catch up after a long outage, read the [change feed](/docs/records/change-feed) and check receipts with `GET /consents/{receiptId}`.

## Handle events out of order

Retries can deliver an older event after a newer one. Don't rely on arrival order:

- For records, read the change feed, which is ordered by `sequence`.
- For consent, check the receipt before acting on an old event.

## Keep handlers quick

Answer `2xx` once you've stored the event, then do the work in a background job. A handler that calls back into FinchNode before answering risks the five-second timeout.
