Webhooks
Webhooks notify your integration when data changes, so you can keep a local copy fresh without polling. Register an HTTPS endpoint with POST /webhooks, choosing which of the event types listed by GET /webhook_events it should receive.
Events describe what changed, not the new values. A delivery names the listings affected and expects you to re-read the detail through the relevant endpoint — price recommendations, reservations or flags. This keeps payloads small and stable, and means you never have to reconcile a webhook body against an API response.
What fires each event
Each event type has its own trigger, and they differ in how closely a delivery tracks a change to the individual listing it names. reservations.ingested and flags.detected name a listing only when that listing's own data actually changed, so a portfolio where nothing happened receives no request at all rather than an empty one. recommendations.updated is broader: its main trigger is a market being repriced, which names every active listing in that market. Read a delivery as "these listings are worth re-reading" rather than as proof that each one is different.
recommendations.updated
Fires when a listing's price recommendations have been recomputed or posted, which is the point at which the numbers you can read may have changed. Two things cause that:
- The nightly pricing run for a market completes. Every active listing in that market is named, because the run may have moved any of them. This is the bulk of the volume, and it is why the event batches per market — see
market_idin the payload. - Rates are posted for a single listing, whether by automatic price posting, by
POST /listings/{listing_id}/sync, or by a post from the Wheelhouse app. Only that listing is named.
Note that the nightly trigger does not compare each listing's numbers before naming it: a market being repriced names every active listing in it, including any whose recommendations ended up identical to yesterday's. So a delivery tells you a recompute covered these listings, not that every one of them moved.
It does not fire because you changed a listing's settings. A preference write that alters future recommendations is not itself this event; the recommendations move when they are next computed or posted, and the event follows then.
reservations.ingested
Fires when the set of reservations you would read back for a listing has changed. That covers:
- A reservation arrives that Wheelhouse did not already hold, whether picked up by a channel sync or pushed in by the integration that manages the listing's reservations.
- A reservation Wheelhouse already held is amended — its dates, prices, payout, fees, taxes, deposits, discounts, currency, unit assignment, or booking time change.
- A reservation is cancelled, whether the channel reports it cancelled or simply stops returning it. Cancellations detected by a reservation's disappearance are limited to stays ending within roughly the last month, so a very old reservation vanishing from a channel feed is not treated as a cancellation and does not fire.
- A reservation is removed outright instead of being marked cancelled. Removals come from the integration that manages the listing's reservations, so a re-read finds the reservation simply gone rather than reported as
Canceled.
It does not fire when a sync re-reads a reservation and finds it unchanged. Reservations are compared field by field, so a listing whose bookings are simply confirmed again each day never notifies. This is what keeps the event proportional to real booking activity rather than to sync frequency.
flags.detected
Fires when a listing gains a flag it did not already have. Flags are the automatically derived conditions returned by GET /listings/{listing_id}/flags, and they are recomputed as a listing, its settings, or its performance change.
Only newly raised flags fire it, and the delivery names the listing rather than the flag, so re-read the flags endpoint to see which are now set. It does not fire when a flag is cleared — a listing whose last flag has just been resolved sends nothing, so do not treat the absence of deliveries as a stable flag state. If you track flags clearing as well as raising, re-read the endpoint on your own schedule in addition to reacting to this event.
Deliveries are batched. Events occurring within a five-minute window collapse into a single request whose data.listing_ids names every listing involved, so a nightly repricing run across a large portfolio arrives as a handful of requests rather than thousands. Each delivery only ever names listings you have access to.
Verify every delivery. Requests carry a Wh-Signature header of the form t=<unix timestamp>,v1=<hex>, where the hex is HMAC-SHA256(secret, "<t>.<raw request body>"). Recompute it with the subscription's secret and compare; reject the request if it does not match, or if t is older than your tolerance (five minutes is a reasonable choice) to prevent replay. The secret is returned only when the subscription is created and by POST /webhooks/\{id\}/rotate_secret — it cannot be read back afterwards.
Delivery is at-least-once, so de-duplicate. Retries reuse the same Wh-Event-Id header (also data-adjacent as the envelope id); treat a repeated id as already handled. Respond with any 2xx status as soon as you have durably accepted the request — do not do your processing before responding, since we time out after 15 seconds. Failed attempts are retried with backoff over roughly 20 minutes, and a subscription that keeps failing is disabled automatically; re-enable it with PUT /webhooks/{id} once your endpoint is healthy. Use GET /webhooks/{id}/deliveries to see what we actually sent and what your endpoint returned.
List subscribable event types.
Returns every event type a webhook subscription can ask for, with the object its ids refer to and how long events of that type are batched before being sent. Use the `event_type` values in `event_types` when creating or updating a subscription.
List webhook subscriptions.
Returns the webhook subscriptions belonging to the authenticated user, most recently created first. Signing secrets are **not** included — they are only available when a subscription is created or its secret is rotated.
Create a webhook subscription.
Registers an HTTPS endpoint to receive the given event types. The subscription covers the authenticated user's entire portfolio — owned listings plus listings shared with them to manage — and each delivery names only listings they can access.
Get a webhook subscription.
Returns one of the authenticated user's webhook subscriptions, without its signing secret. Responds `404` for a subscription belonging to another account.
Update a webhook subscription.
Updates the fields provided and leaves the rest unchanged. Passing `event_types` **replaces** the subscribed set rather than adding to it; omitting it leaves the current set alone.
Delete a webhook subscription.
Removes the subscription and stops all delivery to it. Its delivery history is removed with it.
Rotate a webhook signing secret.
Issues a new signing secret and returns it. Use this if the current secret has leaked or been lost.
List recent delivery attempts.
Returns recent deliveries for the subscription, most recent first, with the HTTP status the endpoint returned and the reason for any failure. Use it to confirm what was sent and to diagnose an endpoint that is failing verification or timing out.