Enquiries Channel manager Bookings Your website Booking engine Pricing
v2026-09

Ordering and idempotency

One rule makes duplicates, retries and out-of-order arrivals harmless. This page explains the rule and why nothing else is needed.

Two guarantees to build on

Every event carries a sequence — a gap-free, strictly increasing integer, unique per integration. Events are delivered in sequence order, one at a time.

Every payload is full state for its scope, never a delta. An availability.changed for a property and a date window contains the complete availability of that property across that window, as it stands at the moment the event was built. It does not describe what changed.

The rule

The only rule you must implement

Per property and per event type, apply an event only if its sequence is greater than the last sequence you applied for that property and type. Otherwise ignore it.
In practice python
key = (event["property"]["doorloom_id"], event["type"])

if event["sequence"] <= last_applied.get(key, 0):
    return                       # duplicate, retry, or arrived late — safe to drop

apply(event["data"])
last_applied[key] = event["sequence"]

Persist last_applied, and separately persist the highest sequence you have seen overall. The latter is your cursor for replay.

Why that is enough

  • A duplicate carries a sequence you have already applied, so it is dropped.
  • A retry is a duplicate.
  • An out-of-order arrival carries a lower sequence than what you hold, so it is dropped — and dropping it is correct, because the state you already hold is newer.
  • A stale event can never overwrite newer state, because newer state always arrived with a higher sequence.

The second guarantee is what makes the first one sufficient. Because Doorloom builds every payload from the live database at the moment the event is created — not from a diff recorded earlier — a later sequence always describes a later reality. There is no scenario where applying only the highest sequence loses an intermediate change, because there are no intermediate changes to lose. There is only current state, stamped.

Coalescing and supersession

Two things reduce the volume you receive, and both are safe for the same reason.

Bursts are coalesced. Several changes to one property within about 10 seconds become one event per type, carrying the state after all of them.

Undelivered events are superseded. If an event is still waiting to be delivered when a newer event of the same type fully covers its date window, the older one is dropped and marked superseded. You receive the newest state rather than the history. This is what shrinks the backlog while your endpoint is down.

Sequences stay gap-free

Superseded events do not leave holes in what you receive out of order — they simply never arrive. Do not treat a missing sequence number as an error or try to fetch it. Track the highest applied, not the set of all seen.

Windows

Date-scoped events carry a window with a from and to, both inclusive. A window never spans more than one calendar month: a change across a longer range arrives as several events, each with its own sequence.

Applying an event means replacing your stored state for that property across that window, not merging into it. Every night in the window is present in data.dates, so a replace is well defined.

Three event types are windowed: availability.changed, rates.changed and restrictions.changed. The rest describe a property or a booking and carry window: null.

Redelivery is a new event

Doorloom never re-sends a sequence number. When staff redeliver a dead-lettered event, a fresh event is created — new id, new sequence, payload rebuilt from current data. Your rule then applies it, as it should.

This is also why there is no "resend event 1042" endpoint. To recover missed events, use your cursor with GET /events.

What about ordering across properties

The sequence is per integration, not per property, so it also gives you a global order if you want one. But you do not need it: state for one property never depends on state for another. Keying your rule on (property, type) is both sufficient and more forgiving.

The exception is booking.changed, which is keyed by booking rather than by date. Its data.revision increments on every change and is the natural comparison there, though the sequence works equally well.

Something unclear or wrong on this page? Write to [email protected] and tell us which page — we would rather fix the doc than answer the ticket twice.