Errores
Casi todos los errores llevan un código estable, legible por máquina:
{
"message": "Not found.",
"error_code": "NOT_FOUND"
}Hay que comparar contra error_code. message es prosa escrita para una
persona y se puede reescribir en cualquier momento sin aviso. Ramificar sobre él
se va a romper.
Los errores de validación no traen error_code
error_codeEsta es la excepción que agarra a la gente desprevenida. Una falla corriente de
validación de campos devuelve 422 con otra forma — un message y un objeto
errors por campo, y ninguna llave error_code:
{
"message": "The start field is required. (and 1 more error)",
"errors": {
"start": ["The start field is required."],
"end": ["The end field is required."]
}
}Hay que leer errors, no message — message es genérico y no trae nada
accionable.
Así que un 422 puede tener cualquiera de las dos formas. Conviene revisar
primero si hay error_code y, si no, caer a errors.
Los códigos
Credenciales
| Código | Estado | Significado |
|---|---|---|
APIKEY_MISSING | 401 | No se mandó key. |
APIKEY_INVALID | 401 | Desconocida, expirada, revocada o desactivada — se responden igual a propósito. |
APIKEY_SCOPE_MISSING | 403 | Key válida, falta el alcance. El mensaje lo nombra. |
APIKEY_NOT_FOUND | 401 | La key no llegó a resolverse en la petición. En la práctica se ve igual que una key inválida. |
TENANT_INACTIVE | 403 | La cuenta del hotel está desactivada. El hotel puede arreglarlo. |
Direccionamiento
No existe un NOT_FOUND genérico en esta API. Cada 404 nombra qué fue lo que
no se encontró, para que un manejador pueda distinguir "el id de habitación está
mal" de "el id de reserva está mal" sin leer prosa.
| Código | Estado | Significado |
|---|---|---|
PROPERTY_NOT_IDENTIFIED | 422 | Una key de varias propiedades omitió X-Property. |
PROPERTY_NOT_FOUND | 404 | La propiedad está fuera de la lista de la key, o no existe. Una sola respuesta para ambas. |
UNIT_NOT_FOUND | 404 | No hay esa habitación en esta propiedad — o existe y no es vendible por esta API. |
RATE_PLAN_NOT_FOUND | 404 | El plan tarifario no está en esta propiedad o no es vendible por esta API. |
GUEST_NOT_FOUND | 404 | El guest_id que se mandó no existe en esta cuenta. |
BOOKING_NOT_FOUND | 404 | No hay esa reserva en esta propiedad. |
INVALID_CURSOR | 422 | El cursor de paginación es inservible. |
Crear una reserva
| Código | Estado | Significado |
|---|---|---|
IDEMPOTENCY_KEY_REQUIRED | 422 | Falta el header. Ver Idempotencia. |
IDEMPOTENCY_KEY_REUSED | 409 | La key ya se usó con otro cuerpo. |
IDEMPOTENCY_IN_FLIGHT | 409 | Una petición con esta key no ha terminado. |
Confirmar y cancelar
| Código | Estado | Significado |
|---|---|---|
BOOKING_HOLD_EXPIRED | 409 | El reloj del hold venció antes de que llegara el confirm. La habitación volvió al mercado y puede estar vendida. Hay que cotizar de nuevo. |
BOOKING_NOT_CONFIRMABLE | 422 | Sólo una reserva pending se puede confirmar. Cualquier otra cosa — ya cancelada, ya iniciada, o un hold que produjo otro sistema — es decisión del hotel, no de la integración. |
BOOKING_NOT_CANCELLABLE | 422 | La estadía ya empezó o ya terminó. Desde ahí, cancelar es decisión del hotel. |
Ni confirm ni cancel necesitan Idempotency-Key: son idempotentes por
estado. Reenviar el mismo confirm a una reserva ya confirmada, o el mismo
cancel a una ya cancelada, devuelve 200 con el mismo cuerpo. Un reintento
después de una respuesta perdida es el caso normal, no un error.
Por qué no se pudo vender la estadía
Todos responden 422 salvo UNIT_NOT_AVAILABLE, que en POST /bookings es
409: la habitación estaba libre al cotizar y dejó de estarlo. Son los que más
seguido va a ver una integración real.
Llevan además los números detrás del rechazo, que son la parte accionable: un
motor de reservas puede decir "mínimo 3 noches" en vez de "no se pudo reservar".
| Código | Qué pasó |
|---|---|
UNIT_NOT_AVAILABLE | 409. No hay nada libre para esas fechas — se vendió después de cotizar. Hay que volver a consultar la disponibilidad. |
OCCUPANCY_OUT_OF_BOUNDS | La ocupación pedida excede la capacidad de la unidad. |
INVALID_BOOKING_QUANTITY | La cantidad pedida no es válida para esa unidad. |
RATE_PLAN_MIN_STAY | La estadía es más corta que el mínimo del plan. Trae min_stay y nights. |
RATE_PLAN_MAX_STAY | La estadía es más larga que el máximo del plan. |
RATE_PLAN_OCCUPANCY_NOT_CONFIGURED | El plan no tiene precio para esa ocupación. Trae requested_adults y configured_adults. |
RATE_PLAN_UNIT_NOT_LINKED | Ese plan no está enganchado a esa unidad. |
RATE_PLAN_WRONG_SALES_CHANNEL | El plan no se vende en el canal api. |
RATE_PLAN_INACTIVE · RATE_PLAN_PARENT_INACTIVE | El plan, o el padre del que deriva, está apagado. |
RATE_PLAN_NOT_USABLE | El plan no se puede usar por una razón que no cae en las anteriores. |
{
"message": "This rate plan has a 3 night minimum stay.",
"error_code": "RATE_PLAN_MIN_STAY",
"min_stay": 3,
"nights": 2
}El objeto hay que tratarlo como abierto: qué campos extra vienen depende del
motivo, así que se leen los que uno reconozca y se ignora el resto.
Límites de uso
Hay dos clases de 429 y no se comportan igual.
Un 429 de límite de peticiones — la cuota por minuto de tu key, o el techo
anónimo por IP — no trae error_code. Contra ése hay que comparar el
estado.
Un 429 de techo de inventario sí lo trae, y además dice cuánto:
| Código | Estado | Significado |
|---|---|---|
KEY_BOOKING_QUOTA_EXCEEDED | 429 | La key creó su máximo de reservas en las últimas 24 horas. |
KEY_HOLD_LIMIT_REACHED | 429 | La key ya mantiene su máximo de holds vivos a la vez. |
{
"message": "This API key is already holding its maximum of 25 rooms.",
"error_code": "KEY_HOLD_LIMIT_REACHED",
"limit": 25,
"used": 25
}Los dos traen Retry-After. Ver Límites de uso.
Webhooks
| Código | Estado | Significado |
|---|---|---|
WEBHOOK_SUBSCRIPTION_NOT_FOUND | 404 | La suscripción no existe, la creó el hotel desde su pantalla, o cubre propiedades fuera del alcance de la key. Una sola respuesta para las tres. |
WEBHOOK_EVENT_NOT_PERMITTED | 403 | La suscripción incluye eventos booking.* y la key no tiene bookings:read:all. Aplica al crearla y a cualquier cambio posterior. Ver Recibir eventos por webhook. |
Una URL insegura — no https, o que resuelve a una dirección privada — no
tiene código propio: es un error de validación sobre url, con la forma de
errors de arriba.
El "no encontrado" dice qué, no dice por qué
El código nombra qué tipo de cosa no se encontró — habitación, plan, huésped,
reserva, propiedad. Lo que deliberadamente no distingue es por qué, y dentro
de cada tipo un 404 cubre tres situaciones:
- el recurso no existe;
- existe pero pertenece a otra propiedad;
- existe y no está expuesto a esta API.
Distinguirlas le permitiría a quien llama mapear la cuenta de otro sondeando ids.
Por eso UNIT_NOT_FOUND responde igual para una habitación inventada que para una
del hotel de al lado.
Cuando llega un 404 inesperado sobre un id en el que uno cree, lo primero que
hay que revisar no es si el registro existe — es si la habitación tiene un plan
tarifario activo del canal api y si uno está apuntando a la propiedad correcta.
Relacionado
Updated about 7 hours ago