Skip to main content

The contract

At least once, not exactly once. You will occasionally receive the same event twice. Idempotency is your responsibility.

Acknowledge fast

Ten seconds is the whole budget. Do the minimum synchronously and queue the rest.
Anti-pattern to avoid: calling a payment provider, generating a PDF, or sending an email inline. Any of those can exceed the timeout, which turns a successful delivery into a retry and a duplicate.

Idempotency

Every delivery carries a stable X-Webhook-Id, which also appears as id in the body. Retries of the same event reuse it.
Record the ID and do the work in the same transaction. Otherwise a crash between them leaves you having recorded an event you never acted on.

When your endpoint is down

Failed deliveries are retried with backoff. If you’re down for a deploy, deliveries resume once you’re back.
Money is never at risk here. Webhooks are notifications, not settlement. A payment we failed to tell you about still arrived in your pool, and still appears in Payments and the status API. The worst case is delayed fulfilment, not lost funds.

Reconciliation

Don’t rely on webhooks alone. Two backstops worth building:

Poll for stragglers

For orders still pending after a sensible window, call the status API.

Reconcile periodically

Compare your paid orders against the dashboard’s ledger. Any gap is a webhook you missed.

Debugging

The dashboard’s Webhooks tab shows recent deliveries with response codes and bodies. Start there — most failures are a signature mismatch caused by a parsed body, or a timeout from inline work.

Signature verification

The most common source of rejected deliveries.