Cancel a booking
Cancel a booking you created. Idempotent, and inventory is released immediately.
Body
Everything is optional; an empty body is a valid cancellation.
Body
| Field | Type | Required | Description |
|---|---|---|---|
reason
|
string | Optional | Why it was cancelled. Max 500 characters. Recorded on the booking. When omitted, Doorloom records the source channel instead. |
refund_amount
|
number | Optional | Amount refunded to the guest, if any. |
refund_note
|
string | Optional | Free text about the refund. Max 500 characters. |
curl -X POST https://api.doorloom.com/api/integrations/v1/bookings/55231/cancel \
-H "Authorization: Bearer $DOORLOOM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Guest cancelled within the free window",
"refund_amount": 4000,
"refund_note": "Advance refunded in full to source"
}'
Response
200 with the
booking object, now showing
status: "cancelled" and a cancelled_at timestamp.
{
"success": true,
"message": "Booking cancelled",
"data": {
"id": 55231,
"external_booking_id": "PMS-100234",
"revision": 3,
"origin": "partner",
"status": "cancelled",
"cancelled_at": "2026-10-02T09:14:20+05:30",
"cancellation_note": "Guest cancelled within the free window",
"…": "…"
}
}
It is idempotent
Cancelling an already-cancelled booking is a 200, not an error. The booking comes
back unchanged, with the same revision as before — nothing was written the second
time.
So a retry after a timeout is always safe, and you never need an idempotency key here.
Cancellation is terminal
PATCH against it returns
409 BOOKING_CANCELLED. If the guest rebooks, create a new booking.Inventory
The units held by the booking are released immediately, and an
availability.changed
event follows for the affected dates. You do not need to adjust your own availability by hand —
apply the event.
A booking.changed
event follows too, with status: "cancelled". There is no separate
booking.cancelled event type.
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. |
| 404 | NOT_FOUND |
No such booking or property for this integration. Another integration's booking is not found rather than forbidden, so ids cannot be probed across partners. |
| 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. |