Crédito monetario de fidelización: vista general
Permite a los socios gastar sus puntos como crédito monetario en reservas directas, cargos durante la estancia y al salir. Cada canje sigue un único ciclo de cálculo → retención → confirmación/liberación. La retención reserva los puntos de forma atómica mientras tu integración completa la reserva o la factura.
Tu integración aplica el crédito confirmado a la reserva o factura. El crédito monetario no crea una tarea de entrega ni un correo «Recompensa por entregar».
Si tu proceso de compra canjea una recompensa concreta (reward), como un desayuno o una mejora de habitación, mediante POST /api/v1/loyalty/redeem, envía status: "fulfilled" cuando tu integración ya la haya entregado. Si omites status, se aplica el ajuste auto_fulfill de la recompensa; en caso contrario, el canje queda pendiente. Un valor explícito de "pending" sustituye ese ajuste cuando una persona aún deba entregar la recompensa.
Los canales permitidos se eligen entre direct, website, phone y walk_in. Cada cuenta activa los que le corresponden.
Las fuentes siguientes están siempre bloqueadas, independientemente de allowed_channels: booking.com, expedia, agoda, hotelbeds, hotusa, roiback, paraty, mirai, travelclick, siteminder, ota, tour_operator, ttoo.
Motivo: esas reservas ya tienen comisión y aplicar además crédito de puntos supone un coste adicional para el hotel.
GET /credit/quote
│ (read-only, no DB write)
▼
POST /credit/hold ─────────► loyalty.credit.held
│ (active, expires in hold_minutes)
│
┌───────┼────────┐
│ │ │
▼ ▼ ▼
confirm release expire (cron)
│ │ │
▼ ▼ ▼
loyalty loyalty loyalty
.credit .credit .credit
.confirmed .released .released (cause=expired)
│
▼
loyalty.balance.changed
│
│ (cancellation path)
▼
POST /credit/reverse ─────────► loyalty.credit.reversed
loyalty.balance.changedLas retenciones duran por defecto 10 minutos (booking_direct) o 5 minutos (quiosco/recepción). La tarea cron expire_loyalty_credit_holds comprueba las retenciones activas vencidas según su programación y emite un webhook loyalty.credit.released por cada retención caducada (cause = expired).
{ error: { code, message } }.| Código | HTTP | Significado |
|---|---|---|
| CASH_CREDIT_DISABLED | 403 | La cuenta tiene desactivado el crédito monetario. |
| SURFACE_DISABLED | 403 | El área (booking_direct / in_stay / checkout) está desactivada para esta cuenta. |
| CHANNEL_NOT_ELIGIBLE | 403 | Coincidencia en la lista de OTA excluidas o canal ausente de allowed_channels. |
| INSUFFICIENT_SPENDABLE | 409 | Los points solicitados superan loyalty_member_spendable(). |
| BELOW_MINIMUM | 422 | points por debajo de cash_credit_config.min_points_per_redemption. |
| HOLD_EXPIRED | 410 | Se ha intentado confirmar después de expires_at de la retención. |
| HOLD_NOT_FOUND | 404 | No hay retención para el hold_id / external_reference_id indicado. |
| MEMBER_NOT_FOUND | 404 | Ningún socio de fidelización coincide con el identificador proporcionado. |
| VALIDATION_ERROR | 400 | El esquema Zod ha rechazado el payload. Se devuelve el campo incorrecto. |