Hotelminder webhooks — event types, signing, retries
The event stream that keeps boutique-group advisor tooling in sync with the Hotelminder platform. Signed with HMAC-SHA256, retried with exponential backoff up to eight hours.
Every consequential state change in the Hotelminder platform emits a webhook. Advisor-side tools consume the stream to drive dashboards, boutique-group orchestration triggers off it, and the internal on-call rota uses it for paging. If you build against the Hotelminder API for an advisor-facing tool, webhooks are how you keep your view of the world current without polling.
Event types under the hm.* namespace
Every event carries a type in the form hm.<object>.<action>. The current set is small on purpose — we add events when we need them, not speculatively.
| Event type | Meaning |
|---|---|
hm.module.activated | A module has been activated on a property. Payload includes property_id, module_id, tier, pilot_pack flag, and the advisor_id who confirmed. |
hm.module.paused | A module has been paused. Payload includes property_id, module_id, reason (advisor|billing|guest_request|maintenance). |
hm.pilot_pack.expired | A pilot pack has reached its expiry timestamp without conversion. Emitted regardless of whether the property continues onto a paid tier. |
hm.subscription.paused | A property's paid subscription has been paused — usually at the property's request between seasons. |
hm.advisor.scoped | An advisor has completed a scoping session for a property. Payload includes the scoped module list and the advisor's note. |
hm.basket.confirmed | A boutique-group basket has been confirmed. Downstream: pending activations open, scoping call is auto-booked. |
HMAC-SHA256 signing
Every webhook carries an X-Hotelminder-Signature header with an HMAC-SHA256 signature of the raw request body, computed with the endpoint's shared secret. Do not verify against a re-serialised body — always the raw bytes as delivered. Reject requests older than five minutes by inspecting the X-Hotelminder-Timestamp header; this prevents replay attacks against endpoints that are network-reachable.
const expected = crypto
.createHmac("sha256", process.env.HM_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
const provided = req.headers["x-hotelminder-signature"];
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided))) {
return res.status(401).end();
} Retry policy — up to eight hours
Every event is retried up to five times with exponential backoff if the endpoint does not return a 2xx status. Retry intervals are 30s, 5m, 30m, 2h, and 8h — five deliveries in total, spread across just over ten hours. After the fifth failed delivery the event is placed in the failed-deliveries queue and the advisor tooling dashboard raises a red badge on the endpoint. Failed events can be manually redelivered from the dashboard within thirty days; after thirty days they are archived.
4xx responses are not retried — the endpoint is telling us it will not accept this payload, and repeating will not help. 5xx responses and network timeouts are retried.
Endpoint requirements
Your endpoint must respond within ten seconds. If the work you need to do on the event is longer than that, respond 202 immediately and process asynchronously. Every delivery carries a unique X-Hotelminder-Delivery-Id — use it for idempotency, since retries and manual redeliveries reuse the delivery ID.