status the booking reached — so the
right way to read them is as moves on this diagram, not as seven unrelated messages.
A booking enters the diagram the moment the guest’s payment succeeds — never before.
Which entry it takes depends on the listing: a listing with instant book on goes straight
to confirmed; one without it goes to pending, and the host owes an answer.
The minutes before that are a hold, and they have their own two events. A guest who
reaches checkout takes the nights off sale while their payment completes:
calendar.held says so, and
calendar.released says how it ended —
expired if the payment never landed, or converted with the booking_reference of the
stay that enters this diagram. They are not booking events and they are not on this state
machine: a hold is not a booking, and most of them never become one. See
the unpaid fifteen minutes.
When each event fires
Two things about the last three rows are easy to get wrong:
- Check-in and check-out are the guest’s own confirmations, not a clock. Nothing fires at
the check-in date or the check-out date. A stay whose guest never taps confirm stays
confirmedfor its whole life and produces neither event; if you need “the stay has started” on a date, compute it fromstay.check_in, and treatbooking.checked_inas a bonus signal that the guest is actually there. - A modification is not a new booking.
booking.modifiedkeeps the samebooking_referenceand does not change the status; read the newstayfrom the payload and replace what you hold. It reaches apending,confirmedorchecked_inbooking — including a request the host has not answered yet, because a guest changing their own still-pending request needs no approval to change it. Sobooking.modifiedcan arrive withstatus: pending, before anybooking.confirmed, and that is not an out-of-order delivery.
What never fires
- A pending request does not expire. There is no timer that turns
pendingintorejectedorcancelled; a request ends only when the host answers or the guest withdraws. If a request is old, that is a host who has not answered — nothing more. - A host-invited stay produces no booking event. A booking a host creates by inviting a
guest reaches
confirmedthrough a different path and fires none of the seven; it is visible throughGET /v1/bookingslike any other. This is a known gap, recorded in the changelog. - A session fires none of the seven. A booking on an experience or a service
(
inventory_kindother thanunit) hangs off no unit, so it has no door times and no owning property, and every payload on this page states both as required fields. Nothing listing-keyed is delivered for one — see the event catalogue — so a session reaches you throughGET /v1/bookingsor not at all. Two facts sit behind that and would outlast it: a session has no arrival to confirm, so it is neverchecked_inorcompleted; and it is never modified either, because every reschedule and extension path refuses a session — a change of mind there is a cancellation and a new booking. - Nothing fires before payment, and nothing fires for a review —
review.createdis its own event, documented on Webhooks.
Two timelines
Request to book. The guest pays on Monday for a stay starting Friday.- Monday —
booking.requested,status: pending. - Monday evening — the host accepts:
booking.confirmed,status: confirmed. (Or declines:booking.rejected, and the story ends.) - Wednesday — the guest adds a night and pays the difference:
booking.modified,status: confirmed, the payload’sstay.check_outis Saturday now. - Friday — the guest taps “I’ve arrived”:
booking.checked_in. - Saturday — the guest taps “I’ve left”:
booking.checked_out,status: completed.
booking.confirmed directly, and steps 3–5 are identical. If the guest cancels on Tuesday
under the listing’s policy, you receive booking.cancelled with initiated_by: guest and
nothing afterwards.
Applying events in the order they happened
Deliveries are at least once, retried on failure, and can be replayed by you or by us (redrive), so the sameevent_id can arrive twice and two events
can arrive out of order. Three rules keep your copy correct:
- Deduplicate on
event_id. A replay carries the same id as the original. - Order by
occurred_atif you order at all. It is the time the thing happened, stamped in the same transaction as the fact, so a retry or a redrive does not move it — see what it is. It is a partial order: two events can share an instant. - Treat each event as a transition on the diagram above. If the
statusin a payload is not a legal move from the status you hold, you have received an older event late: keep what you have. When in doubt,GET /v1/bookings/{booking_reference}is the truth and costs one call.
Where the rest lives
- Registering an endpoint and the signature scheme: Webhooks and Webhook signatures. Delivery timing, retries and the full event catalogue: Webhook delivery.
- Reading a booking and answering a request: Bookings and reviews.
- Why none of the seven carries the guest’s identity: The data boundary.
- Nothing on this page can be exercised in a sandbox yet — no payment reaches one, so no booking exists there to move: Sandbox.