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

Esta 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ódigoEstadoSignificado
APIKEY_MISSING401No se mandó key.
APIKEY_INVALID401Desconocida, expirada, revocada o desactivada — se responden igual a propósito.
APIKEY_SCOPE_MISSING403Key válida, falta el alcance. El mensaje lo nombra.
APIKEY_NOT_FOUND401La key no llegó a resolverse en la petición. En la práctica se ve igual que una key inválida.
TENANT_INACTIVE403La 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ódigoEstadoSignificado
PROPERTY_NOT_IDENTIFIED422Una key de varias propiedades omitió X-Property.
PROPERTY_NOT_FOUND404La propiedad está fuera de la lista de la key, o no existe. Una sola respuesta para ambas.
UNIT_NOT_FOUND404No hay esa habitación en esta propiedad — o existe y no es vendible por esta API.
RATE_PLAN_NOT_FOUND404El plan tarifario no está en esta propiedad o no es vendible por esta API.
GUEST_NOT_FOUND404El guest_id que se mandó no existe en esta cuenta.
BOOKING_NOT_FOUND404No hay esa reserva en esta propiedad.
INVALID_CURSOR422El cursor de paginación es inservible.

Crear una reserva

CódigoEstadoSignificado
IDEMPOTENCY_KEY_REQUIRED422Falta el header. Ver Idempotencia.
IDEMPOTENCY_KEY_REUSED409La key ya se usó con otro cuerpo.
IDEMPOTENCY_IN_FLIGHT409Una petición con esta key no ha terminado.

Confirmar y cancelar

CódigoEstadoSignificado
BOOKING_HOLD_EXPIRED409El 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_CONFIRMABLE422Só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_CANCELLABLE422La 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ódigoQué pasó
UNIT_NOT_AVAILABLE409. No hay nada libre para esas fechas — se vendió después de cotizar. Hay que volver a consultar la disponibilidad.
OCCUPANCY_OUT_OF_BOUNDSLa ocupación pedida excede la capacidad de la unidad.
INVALID_BOOKING_QUANTITYLa cantidad pedida no es válida para esa unidad.
RATE_PLAN_MIN_STAYLa estadía es más corta que el mínimo del plan. Trae min_stay y nights.
RATE_PLAN_MAX_STAYLa estadía es más larga que el máximo del plan.
RATE_PLAN_OCCUPANCY_NOT_CONFIGUREDEl plan no tiene precio para esa ocupación. Trae requested_adults y configured_adults.
RATE_PLAN_UNIT_NOT_LINKEDEse plan no está enganchado a esa unidad.
RATE_PLAN_WRONG_SALES_CHANNELEl plan no se vende en el canal api.
RATE_PLAN_INACTIVE · RATE_PLAN_PARENT_INACTIVEEl plan, o el padre del que deriva, está apagado.
RATE_PLAN_NOT_USABLEEl 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ódigoEstadoSignificado
KEY_BOOKING_QUOTA_EXCEEDED429La key creó su máximo de reservas en las últimas 24 horas.
KEY_HOLD_LIMIT_REACHED429La 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ódigoEstadoSignificado
WEBHOOK_SUBSCRIPTION_NOT_FOUND404La 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_PERMITTED403La 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


Did this page help you?