Ir al contenido

Reservas

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).

Ventana de terminal
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.

Repetir la misma idempotencyKey devuelve la reserva existente con "repetida": true, sin crear otra. Es el mecanismo seguro para reintentos de red.

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.

Ventana de terminal
curl -X POST http://localhost:5000/reservas/{id}/confirmar -H "authorization: Bearer $TOKEN"
  • apartada → confirmada; guarda confirmadaAt (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.
Ventana de terminal
# El participante (o quien opera por él) cancela su apartado
POST /reservas/{id}/cancelar
# Operador libera manualmente (p. ej. mesas ocupadas por retención)
POST /reservas/{id}/liberar

Ambas devuelven la reserva con estado final y liberan la silla.

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.

Ventana de terminal
GET /reservas/?eventoId=<uuid>&estado=confirmada&participanteId=…&limit=50
GET /reservas/vigente?eventoId=<uuid>&participanteId=<uuid> # la vigente o 404
GET /reservas/{id} # con contexto
GET /reservas/{id}/movimientos # historial del lugar

/reservas/vigente es el atajo típico del front: “¿ya tiene sitio este participante?”.

Si el evento permite permiteReservaGrupal, un líder abre un grupo con cupo en una mesa:

Ventana de terminal
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, un 201 y el resto 409.
  • GET /grupos/{id} devuelve integrantes y cupoUsado; al llenarse el estado pasa a completo.
  • POST /grupos/{id}/cancelar cancela el grupo.
  • El integrante no puede superar maxAcompanantes del evento.

→ Boletos QR