> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.hopnow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Best Practices

> Patterns for reliable webhook handling

## Verify, Persist, and Respond Quickly

Verify the signature using the raw body, persist the event, and return a 2xx response within 10 seconds. Process business logic asynchronously when possible.

```python theme={null}
@app.post("/webhooks")
async def handle_webhook(request):
    raw_body = await request.body()
    verify_signature(raw_body, request.headers)

    event = json.loads(raw_body)
    await event_store.save(event)
    background_tasks.add_task(process_event, event)

    return {"status": "received"}
```

## Handle Duplicates

Events may be delivered more than once. Use the envelope `id` to deduplicate deliveries:

```python theme={null}
def process_event(event):
    event_id = event["id"]

    if is_already_processed(event_id):
        return

    handle_event(event)
    mark_as_processed(event_id)
```

## Handle Out-of-Order Events

Events may arrive out of sequence. Compare the resource's `updated` timestamp before replacing local state:

```python theme={null}
def handle_withdraw_event(event):
    payout = event["data"]
    current = db.get_payout(payout["id"])

    if current and current["updated"] > payout["updated"]:
        return

    db.update_payout(payout["id"], payout)
```

## Retry Behavior

Connection errors, timeouts, and non-2xx responses are retried with exponential backoff. Delivery currently allows up to 12 attempts, beginning with a five-minute retry delay and capping the delay at six hours. The `attempt` field and `X-Webhook-Attempt` header identify the current attempt.

Repeated delivery failures do not automatically delete or disable the webhook endpoint. Use the [Delete Webhook Endpoint](/fx/api-reference/webhooks/delete-endpoint) API when an endpoint should stop receiving events.

## Local Testing

Because registered endpoints must use HTTPS, expose your local server through an HTTPS tunnel such as ngrok:

```bash theme={null}
ngrok http 8000
```

Register the generated `https://` URL as a webhook endpoint in the sandbox environment.
