Appointments API
import { Badge } from ‘@astrojs/starlight/components’;
Appointments
Section titled “Appointments”GET /api/appointments
Section titled “GET /api/appointments”List appointments for the clinic with optional filters.
Query params:
| Param | Type | Description |
|---|---|---|
date |
YYYY-MM-DD | Filter by exact date |
start |
YYYY-MM-DD | Range start (use with end) |
end |
YYYY-MM-DD | Range end |
status |
string | Filter by status |
doctor_id |
UUID | Filter by doctor |
page |
int | Page number (default 1) |
limit |
int | Results per page (default 50) |
Response 200:
{ "appointments": [ { "id": "uuid", "clinic_id": "uuid", "doctor_id": "uuid", "patient_id": "uuid", "appointment_date": "2026-06-15", "start_time": "14:00:00", "end_time": "14:30:00", "status": "confirmed", "notes": "Revisión de rutina", "patients": { "id": "uuid", "full_name": "Juan Pérez", "phone": "+50377654321" }, "doctors": { "id": "uuid", "full_name": "Dr. García", "specialty": "Medicina General" }, "created_at": "2026-05-20T10:00:00Z", "updated_at": "2026-05-20T10:00:00Z" } ], "total": 45, "page": 1, "limit": 50}GET /api/appointments/notifications
Section titled “GET /api/appointments/notifications”Count of WhatsApp-booked appointments since a given timestamp. Used to power the calendar badge in the dashboard.
Auth: Required
Query params:
| Param | Description |
|---|---|
since |
ISO 8601 timestamp. Defaults to 24 hours ago. |
Response 200:
{ "count": 3, "since": "2026-05-30T00:00:00Z"}POST /api/appointments
Section titled “POST /api/appointments”Create a new appointment manually from the dashboard.
Request:
{ "doctorId": "uuid", "appointmentDate": "2026-06-15", "startTime": "14:00", "patientName": "Juan Pérez", "patientPhone": "+50377654321", "patientDui": "12345678-9", "notes": "Revisión anual"}| Field | Required | Notes |
|---|---|---|
doctorId |
Yes | Must belong to the same clinic |
appointmentDate |
Yes | YYYY-MM-DD |
startTime |
Yes | HH:MM |
patientName |
Yes | Used to create/update patient record |
patientPhone |
Yes | E.164 format. If patient exists (same phone), their record is updated. |
patientDui |
No | Encrypted at rest |
notes |
No | Internal notes |
Response 201:
{ "id": "uuid", "clinic_id": "uuid", "doctor_id": "uuid", "patient_id": "uuid", "appointment_date": "2026-06-15", "start_time": "14:00:00", "end_time": "14:30:00", "status": "confirmed", "idempotency_key": "MANUAL_...", "created_at": "2026-05-31T10:00:00Z"}Errors:
| Status | Message |
|---|---|
404 |
“Doctor not found” |
409 |
“Slot no longer available” (booked between your request and insert) |
400 |
Validation error |
Side effects:
- Patient record is upserted by
(clinic_id, phone) - Two BullMQ reminder jobs are enqueued: 24h before and 2h before the appointment
PATCH /api/appointments/:id/status
Section titled “PATCH /api/appointments/:id/status”Update an appointment’s status.
Request:
{ "status": "cancelled", "cancellation_reason": "Patient requested"}Valid status transitions:
| To | Notes |
|---|---|
cancelled |
Records cancelled_at, cancels reminder jobs |
completed |
Records completed_at |
no_show |
Also increments patients.no_show_count |
Response 200: Updated appointment object
Errors:
| Status | Message |
|---|---|
404 |
Appointment not found (or belongs to another clinic) |
400 |
Invalid status value |