Skip to content
Quarters Developers
esc
  • Type an endpoint, an object or a word from a guide.

Webhooks

Webhooks

Register an endpoint, verify the signature, read the record. Nothing has to poll.

Register an https endpoint and Quarters POSTs to it when something changes. Each delivery is signed with a secret only the two of you hold, so you can prove the call came from us before acting on it.

Register an endpoint

Under Settings › Integrations, add the URL. The secret (whsec_…) is shown next to it. The test button sends a ping through the same path, and the last deliveries with their outcome are listed under the endpoint, so a receiver can be debugged from the dashboard.

The URL must be https and resolve to a public address; that is checked at connection time, not at save time, so a hostname that later points inward is refused then.

What arrives

The body is an envelope: the delivery id, the event, when it was createdAt, and a small data object that points at the record. It is a pointer, not the record: read the current state from the API, which is what makes a late or repeated delivery harmless.

Delivery body

{
  "id": "6f1c…",
  "event": "reservation.updated",
  "createdAt": "2026-09-13T10:00:00.000Z",
  "data": {
    "reservationId": "7c02…",
    "propertyId": "0d6e1c2a-…",
    "status": "confirmed",
    "previousStatus": "reserved"
  }
}

Three headers travel with it:

HeaderValue
x-housalot-eventThe event name, the same as event in the body.
x-housalot-deliveryThe delivery id, the same as id in the body. Dedupe on it.
x-housalot-signaturesha256= followed by the hex HMAC-SHA256 of the raw body under your endpoint’s secret.

The header names carry the product’s former name and will be renamed only with notice and a transition period.

Verify the signature

Compute HMAC-SHA256 over the raw body, the bytes exactly as received and before any JSON parsing, under the endpoint’s secret, and compare it in constant time with the header. Reject anything that does not match before you read a byte of the body.

Node

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody is the request body exactly as received, before any JSON parsing.
export function verify(secret, rawBody, signatureHeader) {
  const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
  const actual = signatureHeader ?? "";
  return actual.length === expected.length &&
    timingSafeEqual(Buffer.from(actual), Buffer.from(expected));
}

Python

import hashlib
import hmac

# raw_body is the request body exactly as received, before any JSON parsing.
def verify(secret: str, raw_body: bytes, signature_header: str | None) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature_header or "", expected)

Handle it

  • Answer fast. Any 2xx counts as received. Queue the work and return; a handler that takes longer than ten seconds is a failed delivery.
  • Dedupe on the delivery id. A delivery is sent once, but your side may see it twice through a retrying proxy. The id is in the body and in the header.
  • Read, don’t trust. The envelope says what changed; the API says what it is now. Fetch the record and act on that.
  • Plan for a miss. Delivery is one attempt with no retry queue. A full read of the resource is always the recovery, and it is cheap: listings are one call, reservations page with updatedSince.

The events

Eight events, listed in full with their payloads under Webhook events.

  • listing.updated A property was published, or re-published with changes.
  • listing.deleted A property was unpublished or removed. Drop it from your site.
  • calendar.updated Availability changed for a property: a booking, a cancellation, a block.
  • reservation.created A booking was created, in any status.
  • reservation.updated A booking changed: its status, dates, guest, money or notes.
  • reservation.deleted A booking was deleted outright, which is a data-entry mistake being undone. A cancellation is an update.
  • agreement.requested Staff pressed Request contract on a booking while another system signs the contracts. Issue the envelope and report it back. force is true when a live envelope should be re-issued.
  • ping Somebody pressed the test button in settings. Confirms the endpoint and the signature.