Reservas
Estados
Sección titulada «Estados»apartada ──confirmar──▶ confirmada ──(boleto emitido)──▶ … │ ├──expirar──▶ expirada (plazo vencido, job) ├──cancelar─▶ cancelada (participante) └──liberar──▶ liberada_admin (operador)Solo hay una reserva vigente por participante y una por lugar
(constraints únicos parciales en DB: cualquier carrera se resuelve con
409, nunca con doble reserva).
Apartar
Sección titulada «Apartar»curl -X POST http://localhost:5000/reservas/ \ -H 'content-type: application/json' -H "authorization: Bearer $TOKEN" \ -d '{ "eventoId": "<uuid>", "participanteId": "<uuid>", "lugarId": "<uuid>", "canal": "web", "idempotencyKey": "pedido-web-123" }'| Campo | Notas |
|---|---|
canal |
web, admin o importacion |
creadoPorAdmin |
Requerido cuando el canal es admin |
grupoId |
Para apartar dentro de un grupo |
idempotencyKey |
Hace la creación idempotente (ver abajo) |
Respuesta (201) con contexto (participante, lugar, mesa,
zona) y expiraAt (fecha de vencimiento). Condiciones: evento
publicado/en_curso, lugar libre, participante registrado y elegible,
mesa no bloqueada — si no, 400/409 con el motivo.
Idempotencia
Sección titulada «Idempotencia»Repetir la misma idempotencyKey devuelve la reserva existente con
"repetida": true, sin crear otra. Es el mecanismo seguro para
reintentos de red.
Carreras
Sección titulada «Carreras»Dos clientes apartando el mismo lugar a la vez: uno recibe 201 y el
otro 409 "El lugar ya tiene reserva vigente". El 409 es esperado:
elige otro lugar y reintenta.
Confirmar
Sección titulada «Confirmar»curl -X POST http://localhost:5000/reservas/{id}/confirmar -H "authorization: Bearer $TOKEN"apartada → confirmada; guardaconfirmadaAt(incluso si después se cancela, el historial conserva el timestamp).- Emite el boleto QR automáticamente (y encola el correo
qr_emitido). Ver Boletos QR.
Cancelar y liberar
Sección titulada «Cancelar y liberar»# El participante (o quien opera por él) cancela su apartadoPOST /reservas/{id}/cancelar
# Operador libera manualmente (p. ej. mesas ocupadas por retención)POST /reservas/{id}/liberarAmbas devuelven la reserva con estado final y liberan la silla.
Expiración
Sección titulada «Expiración»apartada vence a los minutosApartado del evento (default 15). La
expira el job reservas.expirar que corre cada minuto
(verbatim: Jobs programados); también existe
POST /reservas/expirar para dispararlo a mano con
header x-cron-secret. El endpoint es idempotente: una reserva ya
expirada no se toca.
Consultas
Sección titulada «Consultas»GET /reservas/?eventoId=<uuid>&estado=confirmada&participanteId=…&limit=50GET /reservas/vigente?eventoId=<uuid>&participanteId=<uuid> # la vigente o 404GET /reservas/{id} # con contextoGET /reservas/{id}/movimientos # historial del lugar/reservas/vigente es el atajo típico del front: “¿ya tiene sitio este
participante?”.
Grupos (reserva grupal)
Sección titulada «Grupos (reserva grupal)»Si el evento permite permiteReservaGrupal, un líder abre un grupo con
cupo en una mesa:
curl -X POST http://localhost:5000/grupos/ \ -H 'content-type: application/json' -H "authorization: Bearer $TOKEN" \ -d '{"eventoId":"<uuid>","liderParticipanteId":"<uuid>","mesaId":"<uuid>","maxIntegrantes":4}'- Cada integrante aparta con
grupoId; el cupo se llena con la misma protección de carreras: al llegar al último lugar, un201y el resto409. GET /grupos/{id}devuelveintegrantesycupoUsado; al llenarse el estado pasa acompleto.POST /grupos/{id}/cancelarcancela el grupo.- El integrante no puede superar
maxAcompanantesdel evento.