Replay events
Fetch events after a sequence cursor. This is how you catch up on anything your endpoint missed, without asking anyone to resend it.
Query parameters
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
after_sequence
|
integer | Optional | Return events with a sequence greater than this. Defaults to 0, i.e. from the beginning of what is retained. |
limit
|
integer | Optional | 1 to 200. Defaults to 100. |
property_id
|
integer | Optional | Only events for this Doorloom property. |
types
|
array | Optional | Only these event types. Repeat the parameter: types[]=rates.changed&types[]=availability.changed. |
include_superseded
|
boolean | Optional | Include events that were retired because a newer event covered them. Off by default, and normally you want it off. |
curl -G https://api.doorloom.com/api/integrations/v1/events \
-H "Authorization: Bearer $DOORLOOM_API_KEY" \
--data-urlencode "after_sequence=1042" \
--data-urlencode "limit=200"
Response
{
"success": true,
"message": "OK",
"data": [
{
"sequence": 1043,
"status": "delivered",
"delivered_at": "2026-10-01T15:04:12+05:30",
"event": {
"id": "01J6Y0R2M4P7Q9S1T3V5W7X9Y1",
"type": "rates.changed",
"version": "2026-09",
"sequence": 1043,
"occurred_at": "2026-10-01T15:04:11+05:30",
"integration": {
"id": 12,
"slug": "acme-pms"
},
"property": {
"doorloom_id": 902,
"external_id": "VILLA-9",
"master_property_id": 902
},
"window": {
"from": "2026-10-12",
"to": "2026-10-14"
},
"data": [
"…"
]
}
}
],
"cursor": {
"after_sequence": 1042,
"next_after_sequence": 1043,
"has_more": false,
"latest_sequence": 1047,
"retention_days": 30
}
}
| Field | Type | Description |
|---|---|---|
data[].sequence
|
integer | The event's sequence. |
data[].status
|
string | pending, delivering, delivered, failed, dead or superseded. Delivery state, for diagnosis — it does not change how you should apply the event. |
data[].delivered_at
|
string|null | ISO-8601, when it was accepted by your endpoint. Null if it never was. |
data[].event
|
object | The exact envelope that was or would have been POSTed, byte for byte. |
cursor.after_sequence
|
integer | The cursor you asked with, echoed. |
cursor.next_after_sequence
|
integer | Pass this as after_sequence on your next call. |
cursor.has_more
|
boolean | True while more events exist past this page. |
cursor.latest_sequence
|
integer | The newest sequence that exists for your integration. Compare with your cursor to measure how far behind you are. |
cursor.retention_days
|
integer | How long events are kept. Currently 30. |
cursor sits beside data, not inside it
pagination elsewhere, it is a top-level key.Paging through
cursor = load_cursor() # your stored high-water mark
while True:
r = get("/events", params={"after_sequence": cursor, "limit": 200})
for row in r["data"]:
handle(row["event"]) # same code path as your webhook handler
cursor = r["cursor"]["next_after_sequence"]
save_cursor(cursor)
if not r["cursor"]["has_more"]:
break
Feed the events through the same handler your webhook endpoint uses, including the apply-if-newer rule. Replayed events you have already applied are then dropped for free, so overlapping replays are harmless.
Superseded events
An event that was still undelivered when a newer event fully covered its window is retired as
superseded and omitted here by default. That is what you want: the newer event holds
the same truth and more.
include_superseded=1 shows them, which is occasionally useful when reconstructing
what happened during an outage. Do not apply them — they are older state.
Retention
Events are kept for 30 days
Compare cursor.latest_sequence with your own high-water mark to see the size of the
gap before you start.
There is no acknowledge, and no single-event resend
Doorloom does not track what you have processed — you keep your own cursor. And there is no endpoint to re-request one specific event, because a redelivery is always issued as a new event with a new sequence. To recover, page forward from your cursor.
Errors
Errors
| Status | Code | Meaning |
|---|---|---|
| 422 | VALIDATION_FAILED |
The request body failed validation. errors is Laravel's field bag: an object keyed by field path, each holding an array of messages. |
| 401 | UNAUTHENTICATED |
No API key, or a key that is not valid. Rotating your API key invalidates the previous one immediately. |
| 429 | RATE_LIMITED |
You exceeded one of the rate limits. The standard Retry-After and X-RateLimit-* headers are set on the response. |