Webhook Best Practices

A webhook handler looks trivial — receive a POST, do something. But production webhooks fail in subtle ways. These ten practices separate an integration that survives from one that pages you at 3am.

1. Acknowledge fast, process later

Respond 200 the instant you've safely received the event — before doing any real work. Providers expect a response within seconds; slow handlers get marked as failed and retried, causing duplicates. Push the actual work to a queue or background job.

2. Always verify the signature

Your endpoint is public. Without signature verification, anyone can forge events. Verify the HMAC signature on every request and reject anything that fails. See verifying webhook signatures.

3. Make handlers idempotent

Delivery is at-least-once — the same event will arrive twice eventually. Record each event's unique id and skip anything you've already processed. Idempotency is not optional; it's the difference between one charge and two.

4. Return the right status codes

Use 2xx for "received, stop retrying." Use 4xx/5xx only when you genuinely want a retry. Don't return 200 for an event you failed to process — the provider will never resend it.

5. Expect and embrace retries

If your endpoint is briefly down, providers retry — often for hours or days with backoff. That's a feature: it means a short outage doesn't lose events. Design for it instead of fighting it.

6. Don't trust the payload's contents for security decisions

A verified signature proves the request came from the provider — it doesn't prove the payload's claims. For high-value actions, treat the webhook as a notification and re-fetch the authoritative state from the provider's API before acting.

7. Log every webhook — request and response

When something breaks at 2am, logs of the raw payload, headers, and your response are the difference between a five-minute fix and a five-hour one. Keep a searchable history.

8. Handle events you don't recognize gracefully

Providers add new event types over time. An unknown event should be acknowledged with 200 and ignored — never crash on it.

9. Use separate endpoints/secrets per environment

Local, staging, and production each need their own webhook configuration and signing secret. Mixing them is the most common "works here, fails there" bug.

10. Monitor delivery health

A silently broken webhook is invisible until a customer complains. Watch your provider's delivery dashboard, alert on a spike in failed deliveries, and consider a periodic reconciliation job (see webhooks vs polling) as a safety net.

Before you ship: capture a real event, confirm your handler verifies the signature, returns 200 fast, and survives the same event arriving twice. If all three hold, you're production-ready.

Test your webhook handler →

Related guides