Delivery semantics
Automatic retries with exponential backoff, plus manual redelivery from the dashboard.
The platform delivers every webhook at least once: an immediate dispatch on each invoice state transition, then automatic retries with exponential backoff while your endpoint keeps failing.
What happens on dispatch#
- The platform POSTs the signed JSON body to your webhook URL.
- Before connecting, the destination is re-validated: the hostname must resolve to a public address (see URL requirements).
- The HTTP response is captured:
- status code — stored and visible in the dashboard.
- error string — captured if the request failed before a response (DNS, TLS, timeout, or a rejected destination).
- A 2xx status marks the event
delivered. Anything else marks itfailedand schedules the next automatic retry.
The body of your server's response is not shown in the dashboard and is not returned by the API — only the status code and error string are.
Automatic retry schedule#
Failed deliveries are retried on an exponential backoff (with ±20% jitter):
| Attempt | Delay after previous failure |
|---|---|
| 2 | ~30 seconds |
| 3 | ~2 minutes |
| 4 | ~10 minutes |
| 5 | ~30 minutes |
| 6 | ~2 hours |
| 7 | ~6 hours |
| 8 | ~24 hours |
After 8 total attempts the event becomes exhausted: automatic retries
stop and platform operators are alerted. You can still redeliver an exhausted
event manually — see Retry failed webhooks.
Webhook URL requirements#
Your webhook URL must:
- use
httporhttps(https strongly recommended); - contain no embedded credentials (
user:pass@); - resolve to a publicly routable address. Private, loopback, link-local and carrier-grade-NAT ranges are rejected — both when you save the URL and again at every delivery.
Redirects are not followed — point the URL at the final destination. For local development use a public tunnel (ngrok, cloudflared).
How to build against this contract#
- Dedupe with
X-Webhook-ID. Every attempt of the same event — automatic or manual — carries the same ID; first successful processing wins. - Return 2xx fast. Do slow work async on your side. A handler that takes seconds will occasionally time out and burn a retry attempt.
- Don't assume arrival order. Retries mean a later transition can arrive
before an earlier one. The
statusfield in the payload is authoritative. - Monitor the dashboard's Webhook events tab for
failed/exhaustedrows — they mean your endpoint has been failing for a while.