Delivery behavior
The payload envelope, the 5-second budget, why there are no retries, and what every delivery status means.
Webhook delivery is deliberately simple, and simple in a way that has consequences for your receiver: one HTTP attempt, five seconds, no retry. Read this page before you decide how much work your handler does inline.
The envelope
Every event except listing.submission arrives in the same top-level shape,
with the event-specific fields nested under data.
{ "event": "connection.location_connected", "timestamp": "2026-08-25T14:32:10Z", "account_id": 11073, "location_id": "279381", "data": { "platform": "google", "connected_account_id": "ba03bc4b-9f8c-4d3b-8e93-9628034c63cc" }}Optional fields inside data are omitted when empty rather than sent as
null. Code defensively: treat a missing key and an explicit null the same
way, and never assume a key that is documented as optional will be present.
Two exceptions
The 5-second budget
Each delivery is a single POST with a 5-second connect timeout and a
5-second read timeout. If your endpoint has not responded in time, the
attempt is abandoned and recorded as timeout.
That budget covers your entire handler. The only safe design is:
- Verify the signature.
- Write the payload somewhere durable: a queue, a table, a log.
- Return
200.
Everything else (enrichment, fan-out, calling back into the REST API, anything that touches a third party) belongs on the other side of that queue.
There are no retries
A delivery is attempted exactly once. There is no backoff, no retry queue, and no dead-letter queue. If your endpoint is down for two minutes, the events that fired in those two minutes are gone. They will not arrive later.
Two consequences worth being explicit about:
- Redirects are not followed. Redirects are disabled outright, so a
301or302from your endpoint is recorded as a failed delivery, not followed to a second URL. Configure your webhooks URL as the final URL. Watch for the bare-domain-to-wwwand the trailing-slash redirects that many web servers add by default. - Only a
2xxcounts as delivered.3xx,4xxand5xxare all recorded as failures.
Duplicates and ordering
- You may receive the same event more than once. De-duplicate on the stable
identifiers inside
data(an interaction id, a post id, a connected-account id) combined withevent. Handlers should be idempotent. - Ordering is not guaranteed. Events are produced by several independent
rails and delivered as they arrive. Do not infer a sequence from arrival
order; use the
timestampand the IDs indata, and be prepared for alocal_post.publishedto land before thelocal_post.createdyou expected first. profile.updatedis debounced to one delivery per location per 60 seconds. This is a real collapse, not a delay: edits inside the window are never delivered, andchanged_fieldsdescribes only the edit that opened the window. If you need the full current state, read the location back from the REST API when the event arrives. Nothing else is debounced.
Delivery outcomes
Every attempt, including one that never leaves the building, is recorded with a status. These are the exact strings.
A receiver checklist
- Verify the signature on every request; return
401and process nothing on a mismatch. - Hash the raw body bytes, never a re-serialized object.
- Return
2xxin well under 5 seconds; queue the work. - Make handlers idempotent and key them on the IDs inside
data. - Don't infer ordering from arrival order.
- Normalize
location_idto a string on the way in. - Ignore
eventvalues you don't recognize, and still return200. - Serve the webhooks URL as a final URL, with no redirects.
- Reconcile periodically against the REST API; there is no retry to save you.