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.
Return 200 Quickly
Respond immediately, process in the background:
@app.post("/webhooks")
async def handle_webhook(request):
verify_signature(request)
event = await request.json()
background_tasks.add_task(process_event, event)
return {"status": "received"} # Return immediately
Handle Duplicates
Events may be delivered more than once. Use the event id to deduplicate:
def process_event(event):
event_id = event["id"]
if is_already_processed(event_id):
return # Skip duplicate
handle_event(event)
mark_as_processed(event_id)
Handle Out-of-Order Events
Events may arrive out of sequence. Use timestamps to determine the latest state:
def handle_payout_event(event):
payout_id = event["data"]["id"]
event_time = event["created"]
current = db.get_payout(payout_id)
if current and current["updated"] > event_time:
return # Ignore outdated event
db.update_payout(payout_id, {
"status": event["data"]["status"],
"updated": event_time
})
Retry Behavior
If your endpoint returns a non-2xx status, we retry with exponential backoff:
| Retry | Delay |
|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 15 minutes |
| 4 | 1 hour |
| 5 | 6 hours |
After 5 failed attempts, the endpoint is automatically disabled.
Error Handling
Log errors but don’t block the webhook response:
async def process_event(event):
try:
await handle_event(event)
except Exception as e:
logger.error(f"Webhook processing failed: {e}")
# Don't re-raise if you've already returned 200
Local Testing
Use ngrok to test webhooks locally:
Then register the ngrok URL as your webhook endpoint in the sandbox environment.