Errors
Every error this API returns. Branch on code, never on message —
codes are part of the contract, messages are written for people and may be reworded.
The failure shape
{
"success": false,
"code": "UNIT_INACTIVE",
"message": "units.0 refers to a unit that is no longer sellable.",
"errors": {
"units.0": {
"inventory_id": 31,
"is_active": false
}
}
}
errors is present only when there is structured detail to give. Its shape depends
on the code and is documented per code below.
All codes
| 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. |
| 422 | IDEMPOTENCY_KEY_REQUIRED |
POST /bookings was sent without a usable Idempotency-Key header. |
| 422 | IDEMPOTENCY_KEY_REUSED |
The key has already been used, with a different request body. Doorloom fingerprints the body (SHA-256 over key-sorted JSON), so key order does not matter but any value change does. |
| 409 | IN_PROGRESS |
An identical request is still being processed. Two requests raced; the first one is still inside its transaction. |
| 422 | PROPERTY_NOT_MAPPED |
The property reference is missing, unknown, or outside your integration's coverage. Coverage is set by Doorloom staff and only ever includes properties the brand owner owns outright, never cohosted ones. |
| 422 | PROPERTY_ARCHIVED |
The property exists and is covered, but the host has archived it. Archived properties are off-market and cannot take bookings. |
| 422 | UNIT_NOT_MAPPED |
A units[] line names a unit alias Doorloom does not know, or one that belongs to a different property. |
| 422 | UNIT_INACTIVE |
The unit is mapped but no longer sellable — deactivated or deleted by the host. |
| 409 | UNAVAILABLE |
There is no free unit for the whole stay, or a unit you pinned is taken. Nothing was written. Doorloom never silently swaps a pinned unit for another. |
| 409 | BOOKING_CANCELLED |
A cancelled booking is terminal. It cannot be modified or un-cancelled. |
| 412 | REVISION_MISMATCH |
You sent If-Match and the booking has changed since you read it. The revision increments on every change, including host-side edits made in the Doorloom app. |
| 429 | SYNC_TOO_SOON |
A full re-sync was requested too recently. One is allowed every 24 hours. |
| 429 | RATE_LIMITED |
You exceeded one of the rate limits. The standard Retry-After and X-RateLimit-* headers are set on the response. |
| 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. |
| 403 | NOT_INTEGRATION_TOKEN |
The bearer token is valid but is not an integration API key — it belongs to another Doorloom token surface. Integration keys are the only credential this API accepts. |
| 403 | INTEGRATION_DISABLED |
Doorloom staff disabled this integration. No events are recorded and no API call succeeds. This is distinct from paused: a paused integration keeps full API access and only stops receiving webhooks. |
Retrying safely
| Field | Type | Description |
|---|---|---|
429
|
retry | Back off for Retry-After (or errors.retry_after) seconds, then retry the same request. |
409 IN_PROGRESS
|
retry | A race with your own earlier request. Retry in a few seconds. |
5xx
|
retry | Retry with backoff. On a create, reuse the same Idempotency-Key so a request that actually succeeded is not duplicated. |
4xx (everything else)
|
do not retry | The request is wrong, or the world is. Fix it or surface it. Retrying unchanged produces the same answer. |
A refused create replays, it does not re-race
409 or 422 from
POST /bookings is stored
against your idempotency key. Retrying with the same key returns the same refusal rather than
attempting the booking again. To genuinely retry after fixing the problem, use a
new key.Reference
VALIDATION_FAILED
422
The request body failed validation. errors is Laravel's field bag: an object keyed by field path, each holding an array of messages.
What to do: Read errors; each key is the dotted path of the offending field.
Messages:
Validation failedThe same unit is referenced by more than one line.Unknown type '{type}'.
errors
| Field | Type | Description |
|---|---|---|
{field path}
|
array of strings, e.g. guest.mobile, units.0.adults |
|
units
|
["duplicate unit"] when two lines name the same unit |
IDEMPOTENCY_KEY_REQUIRED
422
POST /bookings was sent without a usable Idempotency-Key header.
What to do: Send Idempotency-Key: <8–128 characters>. A UUID per create attempt is ideal.
Message: Send an Idempotency-Key header (8–128 characters) with every booking create.
IDEMPOTENCY_KEY_REUSED
422 The key has already been used, with a different request body. Doorloom fingerprints the body (SHA-256 over key-sorted JSON), so key order does not matter but any value change does.
What to do: Use a new key for a new booking. Reuse a key only to retry the identical request.
Message: This Idempotency-Key was already used with a different request body.
IN_PROGRESS
409 An identical request is still being processed. Two requests raced; the first one is still inside its transaction.
What to do: Retry the same request in a few seconds. A claim left in progress for 60 seconds is taken over automatically.
Messages:
A request with this Idempotency-Key is still being processed. Retry shortly.A booking with this external id is being created. Retry shortly.
PROPERTY_NOT_MAPPED
422 The property reference is missing, unknown, or outside your integration's coverage. Coverage is set by Doorloom staff and only ever includes properties the brand owner owns outright, never cohosted ones.
What to do: Call GET /properties for the authoritative list of what you cover.
Messages:
Provide property.doorloom_id or property.external_id.This property is not covered by your integration.This property no longer exists.The booking no longer references a property.One or more property_ids are not covered by your integration.
errors
| Field | Type | Description |
|---|---|---|
property
|
the reference you sent, echoed back | |
property_ids
|
array of the uncovered ids, on POST /sync |
PROPERTY_ARCHIVED
422 The property exists and is covered, but the host has archived it. Archived properties are off-market and cannot take bookings.
What to do: Stop selling it. A property.changed event with status: "archived" announces this.
Message: This property is archived and cannot be booked.
errors
| Field | Type | Description |
|---|---|---|
property
|
{"doorloom_id": 902} |
UNIT_NOT_MAPPED
422
A units[] line names a unit alias Doorloom does not know, or one that belongs to a different property.
What to do: Aliases are set by Doorloom staff and published on inventory.changed and GET /properties. Re-read them, or omit the unit reference to let Doorloom allocate any free unit.
Messages:
units.{i}.external_unit_id is not mapped for this property.units.{i} does not belong to this property.
errors
| Field | Type | Description |
|---|---|---|
units.{i}
|
the offending line, echoed back |
UNIT_INACTIVE
422 The unit is mapped but no longer sellable — deactivated or deleted by the host.
What to do: Retire the alias. inventory.changed carries inactive and deleted units with is_active and deleted flags so you can do this ahead of time.
Message: units.{i} refers to a unit that is no longer sellable.
errors
| Field | Type | Description |
|---|---|---|
units.{i}
|
{"inventory_id": 31, "is_active": false} |
UNAVAILABLE
409 There is no free unit for the whole stay, or a unit you pinned is taken. Nothing was written. Doorloom never silently swaps a pinned unit for another.
What to do: Apply errors.availability to your calendar in the same breath, then re-quote. Note its window.to is the last NIGHT, i.e. check_out minus one day.
Message: (passed through from the booking engine, naming the listing)
errors
| Field | Type | Description |
|---|---|---|
availability
|
a per-night snapshot of the property across the stay — the same shape as an availability.changed payload, minus units[] |
BOOKING_CANCELLED
409 A cancelled booking is terminal. It cannot be modified or un-cancelled.
What to do: Create a new booking. Cancelling an already-cancelled booking is not an error — that stays a 200.
Message: A cancelled booking cannot be modified.
REVISION_MISMATCH
412
You sent If-Match and the booking has changed since you read it. The revision increments on every change, including host-side edits made in the Doorloom app.
What to do: Re-read the booking, re-apply your change on top, and retry. If-Match is optional; omit it to force the write.
Message: The booking was modified since you last read it.
errors
| Field | Type | Description |
|---|---|---|
current_revision
|
integer — the revision the booking is actually at |
SYNC_TOO_SOON
429 A full re-sync was requested too recently. One is allowed every 24 hours.
What to do: Wait errors.retry_after seconds — which may be most of a day. If you are catching up on missed events, use GET /events?after_sequence= instead: it has no interval and gives you exactly what you missed.
Message: A full sync was requested {time} ago; wait {n} seconds.
errors
| Field | Type | Description |
|---|---|---|
retry_after
|
integer seconds |
RATE_LIMITED
429
You exceeded one of the rate limits. The standard Retry-After and X-RateLimit-* headers are set on the response.
What to do: Back off for Retry-After seconds. Limits are per integration, so they are yours alone to manage.
Message: Too many requests. Retry after {n} seconds.
errors
| Field | Type | Description |
|---|---|---|
retry_after
|
integer seconds |
NOT_FOUND
404 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.
What to do: Check the id, and check that the property is still covered.
Messages:
No such booking for this integration.No booking with that external id.No covered property with that id.
UNAUTHENTICATED
401 No API key, or a key that is not valid. Rotating your API key invalidates the previous one immediately.
What to do: Send Authorization: Bearer <your API key>.
Message: Send your integration API key as: Authorization: Bearer <key>.
NOT_INTEGRATION_TOKEN
403 The bearer token is valid but is not an integration API key — it belongs to another Doorloom token surface. Integration keys are the only credential this API accepts.
What to do: Use the API key Doorloom issued for this integration.
Message: This endpoint accepts integration API keys only.
INTEGRATION_DISABLED
403 Doorloom staff disabled this integration. No events are recorded and no API call succeeds. This is distinct from paused: a paused integration keeps full API access and only stops receiving webhooks.
What to do: Contact Doorloom.
Message: This integration has been disabled.
Two responses that are not in this shape
Both are outside the envelope by nature rather than by oversight, and neither comes from the application.
| Field | Type | Description |
|---|---|---|
404 with an empty body
|
platform | The Brand Integration API is switched off for the environment you are calling. Not a credential problem. Ask Doorloom. |
404 HTML, or a gateway error page
|
infrastructure | You are not reaching the application at all. Check the host and path. |