Contracts — 003 Recordatorios de cita
1. POST /api/recordatorios/ejecutar (FR-015, disparador manual)
Auth: recepción (exigirClinica, cookie de panel). Mismo comportamiento y no-duplicidad que el cron.
- Request:
{ "ejecucion"?: string }(ejecucionISO opcional solo para pruebas con reloj controlado; en producción se ignora y se usacapturarAhora()). - Response
200:{ "generados": number, "omitidos": number, "incidencias": number, "ejecucion": string }. - Errores:
401sin sesión (heredadoexigirClinica).
2. GET /api/citas/[id]/recordatorio (FR-011, señal en ficha)
Auth: recepción. Nunca expone rutas, hashes ni nombres de fichero (Q17).
- Response
200enviado:{ "estado": "enviado", "texto": "Recordatorio enviado el 29/09/2026 a las 00:05" }(fecha =creadoEndel envío vigente, formatoformatearDia+formatearHora). - Response
200pendiente:{ "estado": "pendiente", "texto": "Recordatorio pendiente" }. - Errores:
404cita inexistente o de otra clínica;401sin sesión.
3. Página GET /cancelar/[id]?token= + PATCH /api/citas/[id]/cancelar {origen:'email', token} (FR-006/007/009)
Reutiliza el caso de uso único 005 y el enlace verificado 007; esta feature no define transición ni token propios.
GETmuestra datos vigentes (clínica, tramo, profesional, servicio, precio) + botón «Confirmar cancelación». Nunca escribe (005 FR-003). Sin token válido →ENLACE_INVALIDO(«Este enlace ya no es válido; entra al portal o llama a la clínica al …»).- Confirmar →
PATCHcon{origen:'email', token}.aplicada→ «Tu cita ha quedado cancelada…»;PLAZO_VENCIDO→ «fuera de plazo, llama a la clínica al » sin mutar;YA_CANCELADA→ «Esta cita ya está cancelada»;ESTADO_FINAL→ aviso de estado final con teléfono.