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:

AttemptDelay after previous attempt
1Immediately
25 seconds
35 minutes
430 minutes
52 hours
65 hours
710 hours
810 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 immediately

What’s Next