Durable webhook delivery and asynchronous replies
Room webhooks can now keep undelivered messages on their host, encrypted, and retry them for up to 24 hours; receivers can reply minutes later; and publishers see when a webhook refused their message.
Durable delivery
Turn it on for a webhook and its host writes each message and file from channels that allow receiver_local to an encrypted spool on its own disk before acknowledging it, then retries for up to 24 hours, across restarts, with the same Idempotency-Key. Messages that still fail become dead letters you can replay for up to 7 days. Everything stays on the host's machine: Beam's infrastructure never holds Room content. Update the host's machine with beam update to use it.
Asynchronous replies
A receiver that needs longer than the 10-second attempt answers 202 Accepted with X-Beam-Reply: v1 and a Location on the webhook URL's origin. The host polls that URL with signed requests and publishes the reply in the Room as the webhook once it is ready, within a reply window of 1 hour by default and up to 24 hours.
- Durable delivery for Room webhooks: beam room <room-id> webhook durable <webhook-id> on, or accept --durable, keeps the messages and files of channels that allow receiver_local, encrypted on the host, and retries them for up to 24 hours.CLI
- Dead letters: beam room <room-id> webhook dead-letters list, replay and purge.CLI
- Asynchronous replies: answer 202 Accepted with X-Beam-Reply: v1 and a Location, and the host polls for the reply. Set the window with webhook reply-window or accept --reply-window.BeamCore
- beam room <room-id> webhook show tells every member whether the webhook's host delivers durably.CLI
- Delivery guarantees in the docs: at-least-once for what the host accepted into its spool, no ordering guarantee, and deduplication on Idempotency-Key, with a guide to running the host next to your receiver.Docs
- publish --hop for services that bridge Rooms, so the webhook hop limit stops loops between them.CLI
- Publishers see when a webhook refused their message: member_failures names the webhook with webhook_rate_limited, webhook_queue_full, webhook_paused, webhook_spool_full or webhook_spool_unavailable.BeamCore
- A webhook whose receiver keeps failing is no longer paused: after 5 failed attempts in a row it becomes degraded and its host sends one attempt at a time until one succeeds. Only a 410 response, or its host leaving or being removed, pauses it.BeamCore
- Other deliveries go on while one message is retried, so a slow or failing message no longer holds up the others. Deliveries can arrive in any order.BeamCore
- The host checks the webhook's access before every delivery attempt: it stops within about a second when access is revoked, and holds deliveries while it cannot confirm access.BeamCore
How it works
Get started
- Update the host's machine to CLI 0.1.40 or later with beam update. Members need 0.1.40 to see the durable setting in webhook show.
- Give the webhook's channels the receiver_local persistence mode with a maximum size and age. The full 24 hours of retries and 7 days of dead letters need a maximum age of at least 192 hours.
- Turn it on with beam room <room-id> webhook durable <webhook-id> on, or accept the webhook with --durable.
- To reply later, answer 202 with X-Beam-Reply: v1 and a Location. The host's owner sets the reply window with webhook reply-window, or at accept with --reply-window.
Costs and limits
- Room messages are free. The spool uses the host's own disk: 1 GiB per host by default, set with BEAM_TUNNEL_WEBHOOK_SPOOL_MAX_MIB and divided equally among the webhooks that deliver durably. Dead letters count toward it.
- Retries last up to 24 hours and dead letters 7 days; a channel's maximum age shortens both. A message past the space or size limit is refused with webhook_spool_full.
- Files up to the webhook's file cap, 25 MiB at most, are kept with their bytes; a larger file is delivered as a file.notice with its metadata only.
- The reply window is 1 hour by default, from 1 minute to 24 hours. A reply is a JSON body of at most 64 KiB, and replies are limited to 30 a minute per webhook.
What it does not do
- Nothing published while the host is offline reaches the webhook, durable or not, and a message refused before the host accepted it is not kept.
- Delivery is at least once and in no particular order: deduplicate on Idempotency-Key, and add your own sequencing where order matters.
- A paused webhook does not poll for replies; they wait for a resume within their window. A webhook that loses access to a channel stops at once, and its kept messages for that channel are deleted.