Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Imjuanisss/proyecto-melika/llms.txt

Use this file to discover all available pages before exploring further.

The Appointments API powers the complete booking lifecycle in MELIKA. Patients can reserve time slots with a specific doctor and specialty, view their upcoming and past appointments, render a FullCalendar-compatible event feed, cancel a booking with a stated reason, and permanently delete cancelled records. Every route is protected by JWT authentication, and the database layer enforces slot availability with concurrency-safe triggers that prevent double-booking even under simultaneous requests.

State Machine

Every appointment begins in pendiente and moves through a strict set of transitions. Patients control cancellation and deletion; doctors and administrators control completion and no-show recording.
Current StateAllowed TransitionTriggered By
pendientecompletadaDoctor / Admin
pendienteno_asistioDoctor / Admin
pendientecanceladaPatient / Admin
cancelada(deleted)Patient
Once an appointment is marked completada or no_asistio it cannot be modified or cancelled through the patient-facing API. Only cancelada appointments may be permanently deleted.

Authentication

All /citas routes require a valid JWT passed in the Authorization header via the verifyToken middleware. The authenticated user’s identity is used automatically — patients always operate on their own data only.
Authorization: Bearer <token>

Endpoints

POST /citas — Create Appointment

Books a new appointment for the authenticated patient. The server looks up the doctor’s tarifa (fee) automatically and inserts the record with estado = 'pendiente'. The target franja_horaria (time slot) is locked during the insert to prevent concurrent double-bookings.
Concurrency Conflict (PgSQL error 45002): If two patients attempt to book the same slot simultaneously, the database raises error code 45002. Your client should catch this CONCURRENCY_CONFLICT error and prompt the user to choose a different available slot — do not retry the same slot automatically.
Authentication: Authorization: Bearer <token> — required

Request Body

id_medico
integer
required
Unique identifier of the doctor to book. The doctor must exist and have an active status; otherwise a 404 NOT_FOUND is returned.
id_especialidad
integer
required
Identifier of the medical specialty for this appointment.
id_franja
integer
required
Identifier of the time slot (franja_horaria) to reserve. The slot must exist and be currently available. If it does not exist the DB trigger raises PgSQL error 45001 (INVALID_SLOT); if it is already taken by a concurrent booking, error 45002 (CONCURRENCY_CONFLICT) is raised.
fecha
string
required
Appointment date in YYYY-MM-DD format.
hora_inicio
string
required
Appointment start time in HH:MM (24-hour) format.
tipo_consulta
string
default:"presencial"
Consultation modality. Accepted values: presencial or teleconsulta.
motivo
string
Optional free-text reason or chief complaint for the appointment.

Response — 201 Created

mensaje
string
Human-readable confirmation message: "Cita reservada exitosamente."
cita
object
The newly created appointment record.

Error Codes

HTTP StatusCodeCause
400BAD_REQUESTOne or more required body fields are missing.
400INVALID_SLOTThe id_franja does not exist (PgSQL 45001).
404NOT_FOUNDDoctor with id_medico not found or marked inactive.
409CONCURRENCY_CONFLICTAnother patient booked the same slot simultaneously (PgSQL 45002).
401UNAUTHORIZEDJWT missing or invalid.

Example

curl -X POST http://localhost:3000/citas \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "id_medico": 12,
    "id_especialidad": 3,
    "id_franja": 88,
    "fecha": "2025-09-15",
    "hora_inicio": "09:00",
    "tipo_consulta": "teleconsulta",
    "motivo": "Revisión de resultados de laboratorio"
  }'

GET /citas/mis-citas — List My Appointments

Returns all appointments belonging to the authenticated patient, ordered by date and start time (most recent first). Includes doctor name, specialty, and time-slot end time resolved from the franjas_horarias table. Authentication: Authorization: Bearer <token> — required

Request Parameters

No query or path parameters required. The patient is identified from the JWT automatically.

Response — 200 OK

An array of appointment objects, ordered by fecha DESC, hora_inicio DESC.
[]
array of objects

Error Codes

HTTP StatusCodeCause
401UNAUTHORIZEDJWT missing or invalid.

Example

curl -X GET http://localhost:3000/citas/mis-citas \
  -H "Authorization: Bearer <token>"

GET /citas/calendario — Calendar Events

Returns a list of appointment events formatted for direct consumption by FullCalendar. cancelada appointments are excluded. If no date range is supplied, the response defaults to events within the current calendar month. Authentication: Authorization: Bearer <token> — required

Query Parameters

inicio
string
Start of the date range in YYYY-MM-DD format. Also accepted as start (FullCalendar’s default param name). Defaults to the first day of the current month if omitted.
fin
string
End of the date range in YYYY-MM-DD format. Also accepted as end. Defaults to the last day of the current month if omitted.

Response — 200 OK

An array of FullCalendar-compatible event objects. cancelada appointments are never included.
[]
array of objects
Pass the inicio and fin parameters directly from FullCalendar’s datesSet callback to keep the calendar in sync with the user’s current view range without over-fetching.

Error Codes

HTTP StatusCodeCause
401UNAUTHORIZEDJWT missing or invalid.

Example

curl -X GET "http://localhost:3000/citas/calendario?inicio=2025-09-01&fin=2025-09-30" \
  -H "Authorization: Bearer <token>"

PATCH /citas/:id — Cancel Appointment

Cancels a pendiente appointment owned by the authenticated patient. A razon_cancelacion is required in the request body. The cancellation atomically updates the appointment state and — via a database trigger — automatically sets the corresponding franja_horaria.disponible = TRUE, making the slot immediately available for other patients without any additional API call.
Automatic Slot Liberation: The database trigger that releases the time slot fires on every UPDATE that sets estado = 'cancelada'. The slot is freed in the same transaction as the cancellation — there is no race window where the slot appears taken after a successful cancellation response.
Authentication: Authorization: Bearer <token> — required

Path Parameters

id
integer
required
The appointment identifier to cancel.

Request Body

razon_cancelacion
string
required
The patient’s stated reason for cancelling. Must be a non-blank string in the request body (not a query parameter).

Response — 200 OK

mensaje
string
Confirmation message: "Cita cancelada. La franja horaria fue liberada automáticamente por la base de datos."
cita
object
The full updated appointment record reflecting estado = 'cancelada' and the recorded razon_cancelacion.

Error Codes

HTTP StatusCodeCause
400BAD_REQUESTrazon_cancelacion missing or blank in request body.
400BAD_REQUESTAppointment is already cancelada.
400BAD_REQUESTAppointment is completada and cannot be cancelled.
403FORBIDDENThe appointment belongs to a different patient.
404NOT_FOUNDNo appointment found with the given id.
401UNAUTHORIZEDJWT missing or invalid.

Example

curl -X PATCH http://localhost:3000/citas/201 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "razon_cancelacion": "Conflicto de horario imprevisto"
  }'

DELETE /citas/:id — Delete Appointment

Permanently removes a cancelada appointment record belonging to the authenticated patient. Only cancelled appointments may be deleted — this prevents accidental loss of active or completed booking history. Authentication: Authorization: Bearer <token> — required

Path Parameters

id
integer
required
The appointment identifier to delete. Must be in cancelada state.

Response — 200 OK

mensaje
string
Confirmation: "Cita eliminada correctamente."

Error Codes

HTTP StatusCodeCause
400BAD_REQUESTAppointment state is not cancelada (e.g., still pendiente or completada).
403FORBIDDENThe appointment belongs to a different patient.
404NOT_FOUNDNo appointment found with the given id.
401UNAUTHORIZEDJWT missing or invalid.

Example

curl -X DELETE http://localhost:3000/citas/201 \
  -H "Authorization: Bearer <token>"

Full Booking Flow

The following example walks through the end-to-end lifecycle: creating a new appointment, confirming it appears on the calendar, then cancelling and deleting it.
1

Book an appointment

Reserve a slot by POSTing to /citas. Store the returned cita.id for subsequent calls.
curl -X POST http://localhost:3000/citas \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "id_medico": 12,
    "id_especialidad": 3,
    "id_franja": 88,
    "fecha": "2025-09-15",
    "hora_inicio": "09:00",
    "tipo_consulta": "teleconsulta",
    "motivo": "Control mensual"
  }'
# → 201 { "cita": { "id": 201, "estado": "pendiente", ... } }
2

Verify on the calendar

Fetch the calendar for the target month. The new appointment appears with the pendiente amber fill (#B45309) and darker amber border (#92400E).
curl -X GET "http://localhost:3000/citas/calendario?inicio=2025-09-01&fin=2025-09-30" \
  -H "Authorization: Bearer <token>"
# → 200 [{ "id": "201", "backgroundColor": "#B45309", "borderColor": "#92400E", ... }]
3

Cancel the appointment

Cancel with a reason in the request body. The database trigger fires and frees the slot automatically.
curl -X PATCH http://localhost:3000/citas/201 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "razon_cancelacion": "Ya no es necesario" }'
# → 200 { "cita": { "estado": "cancelada", ... } }
4

Delete the cancelled record

Permanently remove the record now that it is in cancelada state.
curl -X DELETE http://localhost:3000/citas/201 \
  -H "Authorization: Bearer <token>"
# → 200 { "mensaje": "Cita eliminada correctamente." }

Build docs developers (and LLMs) love