Retry & Reliability
Delivery Guarantee
Medallion guarantees at-least-once delivery. If your endpoint does not return a 2xx response within 15 seconds, the delivery is considered failed and Medallion will retry.
This means the same event may be delivered more than once. Design your handler to be idempotent.
Idempotency
Use the webhook-id request header as a deduplication key. This value is stable across retries. The same event always has the same webhook-id.
event_id = request.headers.get("webhook-id")
if db.already_processed(event_id):
return 200 # acknowledge without re-processing
db.process_and_mark(event_id, payload)Retry Schedule
When a delivery fails, Medallion retries on the following schedule:
| Attempt | Delay after previous attempt |
|---|---|
| 1 | Immediately |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 5 hours |
| 7 | 10 hours |
| 8 | 10 hours |
| Total | ~27.5 hours |
Endpoint Auto-Disable
If an endpoint fails continuously for 5 days, it will be automatically disabled. The 5-day countdown begins once your endpoint has failed at least twice in a 24-hour window, with those failures spread at least 12 hours apart.
To re-enable a disabled endpoint, open the Webhooks page, select the endpoint, and click Enable Endpoint.
Replaying Events
You can replay any event within the message retention window (90 days). Open your endpoint, go to the Logs tab, select the message, and click Replay.
This is useful for:
- Re-processing events after fixing a bug in your consumer
- Recovering from a period of downtime
- Testing your handler against real production payloads
Responding Quickly
Your endpoint must return 2xx within 15 seconds. For handlers that require more time (database writes, downstream API calls), return 2xx immediately and process asynchronously:
from fastapi import BackgroundTasks, Request, Response
@app.post("/webhooks")
async def receive(request: Request, background_tasks: BackgroundTasks):
payload = await verify_and_parse(request)
background_tasks.add_task(process, payload)
return Response(status_code=204) # respond immediatelyUpdated about 19 hours ago