Holds
Entre que un huésped elige una habitación y termina de pagarla pasa algo real: se
autoriza una tarjeta, se llena una segunda pantalla, alguien va por su pasaporte.
Durante ese rato la habitación sigue a la venta para todos los demás.
Un hold la aparta. POST /bookings acepta un campo opcional:
{
"unit_id": "…",
"rate_plan_id": "…",
"start_date": "2027-03-01",
"end_date": "2027-03-04",
"adults": 2,
"hold_minutes": 20,
"guest": { "first_name": "Ada", "last_name": "Lovelace", "email": "[email protected]" }
}La reserva se crea igual, pero marcada como retenida y con un reloj:
{
"data": {
"id": "…",
"status": "pending",
"is_hold": true,
"hold_expires_at": "2027-02-10T14:20:00+00:00"
}
}hold_minutes va de 1 a 30. El tope es de la plataforma: una habitación
retenida es inventario que nadie más puede vender.
Omitir
hold_minutesdeja todo exactamente como estaba. Una integración
que nunca lo mande se comporta igual que antes de que los holds existieran:
is_holdviene enfalse,hold_expires_atennull, y la reserva no
caduca. Nada de esto es un cambio incompatible.
Cerrar el hold
Dos salidas, y hay que tomar una:
POST /bookings/{booking}/confirm — convierte el hold en venta. Quita el
reloj y deja la reserva en confirmed. Necesita bookings:write, el mismo
alcance con el que se creó.
POST /bookings/{booking}/cancel — devuelve la habitación al mercado ahora.
Necesita bookings:cancel, que es un alcance aparte a propósito: una key emitida
para vender no debería poder cancelar.
Y si no se toma ninguna, el hold se devuelve solo. Un barrido corre cada
cinco minutos y cancela todo hold cuyo hold_expires_at ya pasó. No hay que
hacer nada para limpiarlo, y no hay forma de dejar una habitación bloqueada por
olvido.
Las dos llamadas son idempotentes por estado
Ninguna necesita Idempotency-Key. Reenviar el mismo confirm a una reserva ya
confirmada devuelve 200 con el mismo cuerpo; lo mismo cancel sobre una ya
cancelada. Un reintento después de una respuesta perdida es el caso normal, no
un error.
Esto es distinto de POST /bookings, donde la key sí
es obligatoria — ahí el reintento podría crear una segunda reserva, y aquí no hay
nada que duplicar.
Un hold retiene la habitación, no el precio
Es la confusión que más cuesta. hold_expires_at acota cuánto tiempo la
habitación queda fuera del mercado. No congela la tarifa: el precio que se cobra
es el que trae la reserva, y hay que leerlo de ahí y no de la cotización que se
le mostró antes al huésped.
Tampoco resuelve la carrera entre cotizar y reservar. Entre POST /quote y
POST /bookings sigue sin haber nada retenido, y esa llamada puede responder
409 UNIT_NOT_AVAILABLE como siempre. El hold empieza cuando la reserva existe.
Los holds cuentan contra un techo
Cada key tiene un tope de holds vivos simultáneos — 25 por defecto. Al toparlo,
crear otro responde 429 KEY_HOLD_LIMIT_REACHED.
Confirmar o cancelar libera el cupo de inmediato. Dejar que el reloj venza
también, pero tarda lo que le quede. Una integración que cierra sus holds en vez
de abandonarlos rara vez ve este techo — ver
Límites de uso.
Errores
| Código | Estado | Significado |
|---|---|---|
BOOKING_HOLD_EXPIRED | 409 | El reloj 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 confirma. Si ya empezó la estadía, o el hold lo produjo otro sistema, es decisión del hotel. |
BOOKING_NOT_CANCELLABLE | 422 | La estadía ya empezó o ya terminó. |
KEY_HOLD_LIMIT_REACHED | 429 | La key ya mantiene su máximo de holds vivos. |
Relacionado
- Idempotencia — por qué
POST /bookingssí exige una key. - Límites de uso — los dos techos y las dos clases de
429. - Alcances — por qué
bookings:cancelestá separado.
Updated 9 days ago