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 |
A create — POST /bookings, POST /properties or POST /properties/bulk — 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. Keys are shared across every create endpoint, so a booking key resent on a property create is a reuse too. |
| 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 what you created through POST /properties plus what Doorloom staff mapped, 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: they cannot take bookings, and they cannot be edited through the API. |
| 409 | PROPERTY_EXTERNAL_ID_EXISTS |
The external_id you sent is already mapped to a property of your integration. On a create, that property was not created; on an update, the mapping was not changed. errors.doorloom_id tells you which property holds the id, so you can store the mapping or pick a different id. |
| 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 or property is not found rather than forbidden, so ids cannot be probed across partners. On a property write, a property whose coverage staff have removed is also not found. |
| 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,
POST /properties or
POST /properties/bulk is
stored against your idempotency key. Retrying with the same key returns the same refusal rather
than attempting the write 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, properties.3.address.city on a bulk call |
|
units
|
["duplicate unit"] when two booking lines name the same unit |
|
units.{i}.external_id
|
on a property write: the alias is already mapped to another unit of your integration, or is duplicated in the request | |
units.{i}.inventory_id
|
on a property update: the unit does not belong to the property | |
units.{i}.name
|
on a property update: a unit addressed by an unknown alias needs a name to be created |
|
units.{i}.is_active
|
on a property update: the default unit cannot be deactivated, or the listing's inventory limit is reached | |
units.{i}
|
on a property update: the entry names neither inventory_id nor external_id |
IDEMPOTENCY_KEY_REQUIRED
422
A create — POST /bookings, POST /properties or POST /properties/bulk — was sent without a usable Idempotency-Key header.
What to do: Send Idempotency-Key: <8–128 characters>. A UUID per create attempt is ideal.
Messages:
Send an Idempotency-Key header (8–128 characters) with every booking create.Send an Idempotency-Key header (8–128 characters) with every property create.Send an Idempotency-Key header (8–128 characters) with every bulk property 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. Keys are shared across every create endpoint, so a booking key resent on a property create is a reuse too.
What to do: Use a new key for a new create. 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.A property with this external_id is being created. Retry shortly.This property is being modified by another request. Retry shortly.
PROPERTY_NOT_MAPPED
422
The property reference is missing, unknown, or outside your integration's coverage. Coverage is what you created through POST /properties plus what Doorloom staff mapped, 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: they cannot take bookings, and they cannot be edited through the API.
What to do: Stop selling it. A property.changed event with status: "archived" announces this. Unarchiving happens in the Doorloom app.
Messages:
This property is archived and cannot be booked.This property is archived and cannot be modified through the API.
errors
| Field | Type | Description |
|---|---|---|
property
|
{"doorloom_id": 902} (on a booking) |
PROPERTY_EXTERNAL_ID_EXISTS
409
The external_id you sent is already mapped to a property of your integration. On a create, that property was not created; on an update, the mapping was not changed. errors.doorloom_id tells you which property holds the id, so you can store the mapping or pick a different id.
What to do: If it is the same listing, store errors.doorloom_id and move on (or PUT /properties/{id} to change it). If it is a different listing, send a different external_id. A retry with the same Idempotency-Key replays this refusal; use a new key once the body is corrected.
Message: A property with this external_id already exists for your integration.
errors
| Field | Type | Description |
|---|---|---|
external_id
|
the id you sent, echoed back | |
doorloom_id
|
integer — the property that already carries it |
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 the ones you set on POST /properties / PUT /properties/{id} or that Doorloom staff mapped, 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 or property is not found rather than forbidden, so ids cannot be probed across partners. On a property write, a property whose coverage staff have removed is also not found.
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. |