Webhook subscriptions report their health, and pause when the endpoint is dead
A subscription now says how its endpoint is doing, emails you when it keeps failing, and is paused after 25 failed deliveries over more than 48 hours.
Until now a subscription whose endpoint had stopped answering stayed active forever and nobody was told. Each subscription now tracks the deliveries that never got through, tells its owner, and stops trying once the endpoint is clearly gone.
A healthy endpoint is unaffected: any successful delivery resets the count, so occasional failures never add up to a pause.
What else changed
- An email at 5 failed deliveries in a row, with the last error, one per run of failures. A delivery counts as failed once it is given up on: all 5 attempts used, or rejected with a
4xxthat is not retried. - A pause at 25 failed deliveries in a row, over a run longer than 48 hours. Both must hold, so a busy endpoint that fails for an afternoon while you fix it is not paused. The subscription moves to
active: falsewithdeactivated_by: beelanddeactivated_at, and the owner gets a second email. - Reactivating a paused subscription runs a test delivery first.
PATCHwithactive: truesends a signed test to your URL: a2xxturns it back on, anything else answers422 WEBHOOK_REACTIVATION_REQUIRES_LIVE_ENDPOINTand it stays paused. A subscription you turned off yourself (deactivated_by: owner) reactivates without a test. - The health is on the subscription:
consecutive_failures,last_errorandlast_error_cause, cleared together by the first successful delivery.last_used_atnow means the last delivery that succeeded, and isnullif none ever has. - Failures carry a stable code.
failure_causeon a test result andlast_error_causeon the subscription take one ofdns,connection,tls,timeout,http_client_error,http_server_error,invalid_url,forbidden_targetorunknown. Branch on it, never on the wording oferror, and treat a value you do not know asunknown. - Creating a subscription tests your endpoint. The
201carriestest_delivery, the result of one signed delivery sent while registering. It never blocks the subscription, which exists and is active whatever it says, and it isnullwhen the test could not run. 408and429are retried, with the same backoff as a5xx, since 6 July. Before that they were treated like any other4xxand the delivery was dropped. Answer429when you need BeeL. to slow down.
Endpoints
- POST/v1/accounts/{account_id}/webhooksThe 201 carries test_delivery
- PATCH/v1/accounts/{account_id}/webhooks/{webhook_id}Reactivating a paused subscription requires a passing test delivery
- GET/v1/accounts/{account_id}/webhooks/{webhook_id}consecutive_failures, last_error, last_error_cause, deactivated_by, deactivated_at
Where to go next
A webhook URL on a private network is rejected when you register it
Registering or changing a subscription URL that points to a private or internal network address now answers `422 URL_TARGET_NOT_ALLOWED`, instead of creating a subscription that would never receive a delivery. A host that does not resolve yet is still accepted, so you can register before your DNS is live.
Filter invoices by several statuses
The `status` filter on `GET /v1/invoices` accepts a comma-separated list, so one request covers several statuses.