Quickstart
Three steps, in the order that finds problems earliest: prove your key works, prove you can verify a webhook, then write a booking.
You need an API key and a webhook secret from Doorloom before starting. See Authentication.
1. Prove your key works
curl https://api.doorloom.com/api/integrations/v1/me \
-H "Authorization: Bearer $DOORLOOM_API_KEY"
A 200 means you are talking to the right environment with a live key. Note two
things in the response: data.webhook.last_sequence, which is the cursor you will
need to stay in sync, and data.covered_properties, which tells you how many
properties this integration can see.
Then list them:
curl https://api.doorloom.com/api/integrations/v1/properties \
-H "Authorization: Bearer $DOORLOOM_API_KEY"
Store the mapping between each doorloom_id and your own record now. Everything
after this point refers to properties and units by one id or the other.
2. Receive and verify a webhook
Give Doorloom an HTTPS URL that answers POST. Then ask staff to send a test ping
to it, or make any calendar change in the Doorloom app to trigger a real event.
Your endpoint has exactly three obligations, and the first two matter more than they look:
- Verify the signature before trusting a byte of the payload.
- Answer 2xx within 10 seconds. Acknowledge first, process afterwards.
- Apply only if newer — the sequence rule, below.
# POST /hooks/doorloom
def webhook(request):
raw = request.get_data() # RAW bytes, not a re-encoded dict
header = request.headers.get("X-Doorloom-Signature", "")
if not verify(os.environ["DOORLOOM_WEBHOOK_SECRET"], raw, header):
return Response(status=401)
event = json.loads(raw)
# Hand off and return immediately. Anything slow happens off-request.
queue_for_processing(event)
return Response(status=204)
The verify() function is on the
signature page, in Python and
JavaScript. Copy it rather than writing your own — the two mistakes people make (hashing
re-serialised JSON, and accepting only the first v1=) both produce something that
works right up until it does not.
Do not process inside the request
3. Implement the sequence rule
Every event carries a sequence, and every payload is the full current
state for its scope, never a delta. So the entire correctness requirement is one line:
The rule
sequence is higher than
the last one you applied for that property and type. Otherwise ignore it.That makes duplicates, retries and out-of-order arrivals harmless without any other bookkeeping. Ordering and idempotency explains why it is sufficient.
4. Create a booking
curl -X POST https://api.doorloom.com/api/integrations/v1/bookings \
-H "Authorization: Bearer $DOORLOOM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2f2a-6b3a-4a1e-9d2f-8a7c5e3b1d40" \
-d '{
"external_booking_id": "PMS-100234",
"property": { "external_id": "VILLA-9" },
"units": [ { "external_unit_id": "ROOM-A", "adults": 2, "children": 1, "meal_plan": "CP" } ],
"check_in": "2026-10-12",
"check_out": "2026-10-14",
"guest": { "name": "Asha Rao", "phone_code": "+91", "mobile": "9876543210" },
"pricing": { "total": 16000, "currency": "INR" },
"status": "confirmed"
}'
A 201 returns the booking. Two other outcomes are normal and neither is a bug:
200withdata.duplicate: true— you already created a booking with thisexternal_booking_id. Treat it as success.409 UNAVAILABLE— nothing was free for the whole stay.errors.availabilitycarries the per-night picture so you can correct your calendar in the same breath.
Send an Idempotency-Key on every create
5. Handle the echo
Moments later you will receive a booking.changed webhook for the booking you just
made, with origin: "partner". That is your own write coming back as confirmation.
You can ignore it, or use it to mark the write as durably landed.
When the host edits that booking in the Doorloom app, you get the same event with
origin: "doorloom". That one you must apply.
Before you go live
- Signatures verified, bad ones rejected with a
401. - The sequence rule implemented per property and type, with your cursor persisted.
- 2xx inside 10 seconds; processing is asynchronous.
- On start-up and after any outage, call
GET /events?after_sequence=. If your cursor is older than 30 days, take snapshots instead. Idempotency-Keyon every create;200 duplicatetreated as success.409 UNAVAILABLEfeeds your calendar fromerrors.availability.- Money parsed as numbers, timestamps parsed with their offset.