- appointment-settings.md: weekly-schedule meta (online_booking_enabled, booking_window value/unit), defaults, and the slot-gating behavior. - appointment.md: new public month-availability endpoint (Gregorian year/month, disabled/enabled dates) and the empty-slots conditions for appointment-slots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Appointment API
Prefix:
/api/v1/appointment*
GET /api/v1/appointment-slots
Get all appointment slots (available and booked) for a doctor on a specific date.
Permission: PUBLIC
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
doctor_uuid |
string (UUID) | ✅ | Doctor UUID |
date |
string | ✅ | Date in Y-m-d format (e.g. 2024-06-15) |
Response 200
{
"success": true,
"data": {
"doctor_uuid": "550e8400-...",
"date": "2024-06-15",
"sessions": [
{
"start_time": "09:00",
"end_time": "13:00",
"slots": [
{
"start": 1718438400,
"end": 1718439600,
"start_time": "09:00",
"end_time": "09:20",
"location_id": null,
"is_available": true
},
{
"start": 1718439600,
"end": 1718440800,
"start_time": "09:20",
"end_time": "09:40",
"location_id": null,
"is_available": false
}
]
},
{
"start_time": "15:00",
"end_time": "17:00",
"slots": [...]
}
]
}
}
Returns all slots grouped by work shift.
is_available: falsemeans the slot has an active (pending/confirmed) appointment. Session boundaries match the doctor'sWeeklyScheduleor date override config.Returns an empty
sessionsarray when the date is a holiday, a closed date override, in the past, beyond the doctor's booking window, or when online booking is disabled (seemetainappointment-settings.md).
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_NOT_FOUND_001 |
404 | Doctor not found |
ERR_VALIDATION_001 |
422 | Missing or invalid date/doctor_uuid |
GET /api/v1/appointment-settings/month-availability/{doctorUuid}
Which days of a month are bookable — used by the public calendar to grey out unavailable days.
Permission: PUBLIC
Path Parameters
| Param | Type | Description |
|---|---|---|
doctorUuid |
string (UUID) | Doctor UUID |
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
year |
integer | ✅ | Gregorian year (e.g. 2026) |
month |
integer | ✅ | Gregorian month 1–12 |
Input is Gregorian. A Jalali (Shamsi) front-end must convert the displayed month to the Gregorian month(s) it spans before calling.
Response 200
{
"success": true,
"data": {
"year": 2026,
"month": 6,
"disabled_dates": ["2026-06-01", "2026-06-17", "2026-06-26"],
"enabled_dates": ["2026-06-15", "2026-06-16", "2026-06-18"],
"online_booking_enabled": true,
"booking_window": { "value": 1, "unit": "month" }
}
}
| Field | Type | Description |
|---|---|---|
disabled_dates |
string[] | Y-m-d days with no bookable slot (holiday / closed override / non-working / past / out-of-window) |
enabled_dates |
string[] | Y-m-d days with at least one slot |
online_booking_enabled |
boolean | Doctor's online-booking flag |
booking_window |
object | { value, unit } — unit is week or month |
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_VALIDATION_002 |
404 | Doctor not found |
ERR_VALIDATION_001 |
422 | Invalid year/month |
POST /api/v1/appointment
Book an appointment slot.
Permission: AUTH — any authenticated user
Request Body (application/json)
{
"doctor_uuid": "550e8400-e29b-41d4-a716-446655440000",
"slot_start": 1718438400,
"slot_end": 1718439600,
"note": "لطفاً سریع ویزیت شوم"
}
| Field | Type | Required | Description |
|---|---|---|---|
doctor_uuid |
string (UUID) | ✅ | Doctor UUID |
slot_start |
integer | ✅ | Slot start (Unix timestamp) |
slot_end |
integer | ✅ | Slot end (Unix timestamp) |
note |
string | ❌ | Patient note |
Response 201
{
"success": true,
"data": {
"uuid": "appt-uuid-...",
"doctor": { "uuid": "...", "title": "دکتر علی احمدی" },
"user": { "uuid": "...", "real_name": "..." },
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "pending",
"note": "...",
"price": 500000,
"created_at": 1717000000
}
}
Appointment Status Values:
| Value | Description |
|---|---|
pending |
Awaiting payment |
confirmed |
Paid and confirmed |
cancelled |
Cancelled |
completed |
Visit completed |
no_show |
Patient did not show |
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_NOT_FOUND_001 |
404 | Doctor not found |
ERR_CONFLICT_001 |
409 | Slot already booked |
ERR_VALIDATION_001 |
422 | Invalid slot times |
GET /api/v1/appointment/{uuid}
Get appointment detail.
Permission: AUTH — must be the patient, the doctor, or ROLE_ADMIN
Path Parameters
| Param | Type | Description |
|---|---|---|
uuid |
string (UUID) | Appointment UUID |
Response 200
{
"success": true,
"data": {
"uuid": "appt-uuid-...",
"doctor": {
"uuid": "...",
"title": "دکتر علی احمدی",
"image": "https://..."
},
"user": {
"uuid": "...",
"real_name": "کاربر",
"mobile_number": "09..."
},
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "confirmed",
"note": "...",
"price": 500000,
"payment_uuid": "pay-uuid-...",
"created_at": 1717000000
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_FORBIDDEN_001 |
403 | Not the patient/doctor/admin |
ERR_NOT_FOUND_001 |
404 | Appointment not found |
GET /api/v1/appointments/doctor/{doctorUuid}
Get all appointments for a specific doctor.
Permission: AUTH — must be the doctor, their secretary, or ROLE_ADMIN
Path Parameters
| Param | Type | Description |
|---|---|---|
doctorUuid |
string (UUID) | Doctor UUID |
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
status |
string | ❌ | Filter: pending, confirmed, cancelled, completed, no_show |
Response 200
{
"success": true,
"data": [
{
"uuid": "...",
"user": { "uuid": "...", "real_name": "..." },
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "confirmed",
"price": 500000
}
]
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_FORBIDDEN_001 |
403 | Not authorized to view this doctor's appointments |
ERR_NOT_FOUND_001 |
404 | Doctor not found |
GET /api/v1/appointments/user
Get all appointments for the authenticated user.
Permission: AUTH
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
status |
string | ❌ | Filter by status |
Response 200
{
"success": true,
"data": [
{
"uuid": "...",
"doctor": { "uuid": "...", "title": "دکتر علی احمدی" },
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "confirmed",
"price": 500000
}
]
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
PATCH /api/v1/appointment/{uuid}/status
Change appointment status.
Permission: AUTH — patient can cancel; doctor/secretary can confirm/complete/no_show; admin can do all
Path Parameters
| Param | Type | Description |
|---|---|---|
uuid |
string (UUID) | Appointment UUID |
Request Body
{
"status": "cancelled",
"version": 3
}
| Field | Type | Required | Description |
|---|---|---|---|
status |
string | ✅ | New status value |
version |
integer | ❌ | Optimistic lock version (prevents double-submit) |
Allowed Transitions by Role:
| Actor | Allowed transitions |
|---|---|
| Patient | pending → cancelled |
| Doctor / Secretary | pending → confirmed, confirmed → completed, confirmed → no_show |
| Admin | Any transition |
Response 200
Updated appointment object.
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_FORBIDDEN_001 |
403 | Not authorized for this transition |
ERR_NOT_FOUND_001 |
404 | Appointment not found |
ERR_CONFLICT_001 |
409 | Version mismatch (optimistic lock) |
ERR_VALIDATION_001 |
422 | Invalid status value |
POST /api/v1/my/appointment
Create a new appointment for a patient. Used by doctor/clinic/secretary to book appointments on behalf of patients. If no user exists with the given mobile, a new user account is created automatically.
Auth: IS_AUTHENTICATED_FULLY — Roles: ROLE_DOCTOR, ROLE_CLINIC, ROLE_SECRETARY, ROLE_ADMIN
Request Body
{
"doctor_uuid": "doctor-uuid",
"slot_start": 1718438400,
"slot_end": 1718439600,
"patient_mobile": "09123456789",
"patient_name": "علی محمدی",
"note": "optional note"
}
اگر کاربری با این شماره موبایل وجود نداشته باشد، یک کاربر جدید با نقش
ROLE_USERساخته میشود.
Response 201
{
"success": true,
"data": {
"uuid": "appt-uuid",
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "pending"
}
}
Error Responses
| Code | HTTP | Description |
|---|---|---|
FORBIDDEN |
403 | Role not allowed |
VALIDATION |
422 | Missing required fields |
DOCTOR_NOT_FOUND |
404 | Doctor UUID not found |
SLOT_TAKEN |
409 | Slot already booked |
GET /api/v1/my/appointments
Role-aware paginated list of appointments. Returns only what the authenticated user is authorized to see.
Auth: IS_AUTHENTICATED_FULLY (any role)
Role behavior:
| Role | Scope |
|---|---|
ROLE_ADMIN |
All appointments |
ROLE_CLINIC |
Appointments for doctors in this clinic |
ROLE_DOCTOR |
Appointments for this doctor |
ROLE_SECRETARY |
Appointments for the linked doctor (empty if appointments.view permission is false) |
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
page |
int | 1 | Page number |
limit |
int | 15 | Items per page (max 100) |
search |
string | — | Search by mobile, real name, or doctor name |
status |
string | — | Filter by appointment status |
date |
string | — | Filter by date in Y-m-d format |
Response 200
{
"success": true,
"data": [
{
"uuid": "string",
"patient_name": "string",
"patient_mobile": "string",
"doctor_name": "string",
"clinic_name": "string | null",
"appointment_date": "2026-07-25",
"appointment_time": "14:30",
"slot_start": 1700000000,
"status": "reserved",
"amount": 0,
"created_at": "ISO 8601 string"
}
],
"meta": {
"totalRecords": 8000,
"totalPages": 533,
"currentPage": 1
}
}