# Appointment Settings API > **Prefix:** `/api/v1/appointment-settings` > **Permission:** All write endpoints require `AUTH` — must be the doctor owner or `ROLE_ADMIN` Doctors configure their availability via three resources: **weekly schedule**, **date overrides**, and **holidays**. --- ## Weekly Schedule Each doctor has **one** weekly schedule (upsert). The schedule is keyed by **day index** (0=Saturday ... 6=Friday), each day containing a `sessions` array. ### Day Index Convention | Index | Day (EN) | Day (FA) | |-------|----------|----------| | `"0"` | Saturday | شنبه | | `"1"` | Sunday | یکشنبه | | `"2"` | Monday | دوشنبه | | `"3"` | Tuesday | سه‌شنبه | | `"4"` | Wednesday | چهارشنبه | | `"5"` | Thursday | پنجشنبه | | `"6"` | Friday | جمعه | --- ### POST `/api/v1/appointment-settings/weekly-schedule` Create or update the weekly schedule for a doctor (upsert). **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body (`application/json`) ```json { "doctor_uuid": "550e8400-e29b-41d4-a716-446655440000", "schedule": { "0": { "sessions": [ { "active": true, "location_id": 1973, "start_time": "09:00", "end_time": "13:00", "duration_per_patient": 20, "has_rest": true, "rest_interval": 60, "time_to_rest": 10, "patient_limit": null }, { "active": true, "location_id": 1973, "start_time": "15:00", "end_time": "18:00", "duration_per_patient": 20, "has_rest": false, "rest_interval": 0, "time_to_rest": 0, "patient_limit": 5 } ] }, "1": { "sessions": [] }, "2": { "sessions": [] }, "3": { "sessions": [] }, "4": { "sessions": [] }, "5": { "sessions": [] }, "6": { "sessions": [] } } } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `doctor_uuid` | string (UUID) | ✅ | Doctor UUID | | `schedule` | object | ✅ | Keys `"0"` through `"6"` (day indices) | | `schedule.{n}.sessions` | array | ✅ | Array of session config objects | **Session Config Object:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `active` | boolean | ✅ | Whether this session is active | | `location_id` | integer\|null | ❌ | Doctor address/location ID | | `start_time` | string | ✅ | Session start `"HH:MM"` | | `end_time` | string | ✅ | Session end `"HH:MM"` | | `duration_per_patient` | integer | ✅ | Minutes per appointment slot | | `has_rest` | boolean | ❌ | Whether to insert rest breaks | | `rest_interval` | integer | ❌ | Work minutes before taking a rest break | | `time_to_rest` | integer | ❌ | Duration of each rest break (minutes) | | `patient_limit` | integer\|null | ❌ | Max patients per session (`null` = unlimited) | > Multiple sessions per day are supported (e.g., morning + afternoon). Sessions are sorted by `start_time` and overlapping ones are skipped. ### Response `201` ```json { "success": true, "data": { "data": { "uuid": "sched-uuid-...", "doctor_uuid": "550e8400-...", "schedule": { "0": { "sessions": [ { "active": true, "start_time": "09:00", ... } ] }, "1": { "sessions": [] }, "2": { "sessions": [] }, "3": { "sessions": [] }, "4": { "sessions": [] }, "5": { "sessions": [] }, "6": { "sessions": [] } }, "created_at": 1717000000, "updated_at": 1717000000 } } } ``` > ⚠️ **Double-nested:** Frontend extracts with `data?.data?.data` > This is an **upsert** — if a schedule already exists for the doctor, it is overwritten. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_VALIDATION_002` | 404 | Doctor not found | | `ERR_AUTH_006` | 403 | Not the doctor owner | --- ### GET `/api/v1/appointment-settings/weekly-schedule/{uuid}` Get weekly schedule. `{uuid}` can be either the **schedule UUID** or the **doctor UUID** — the controller tries both. **Permission:** `AUTH` (class-level `IS_AUTHENTICATED_FULLY`) ### Response `200` Same structure as POST response. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_VALIDATION_002` | 404 | Schedule not found | --- ### PATCH `/api/v1/appointment-settings/weekly-schedule/{uuid}` Update weekly schedule. `{uuid}` can be schedule UUID or doctor UUID. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body ```json { "schedule": { "0": { "sessions": [ { "active": true, "location_id": 1973, "start_time": "10:00", "end_time": "14:00", "duration_per_patient": 30, "has_rest": false, "rest_interval": 0, "time_to_rest": 0, "patient_limit": null } ] } } } ``` > Replaces the entire `schedule` object if provided. ### Response `200` Updated schedule object (same structure as POST). ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not the doctor owner | | `ERR_VALIDATION_002` | 404 | Schedule not found | --- ### DELETE `/api/v1/booking-setting/{uuid}` Delete a weekly schedule. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` > Note: route is `/booking-setting/`, not `/appointment-settings/` ### Response `200` ```json { "success": true, "data": { "message": "برنامه هفتگی با موفقیت حذف شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not the doctor owner | | `ERR_VALIDATION_002` | 404 | Schedule not found | --- ## Date Overrides Override a specific date — mark it inactive (day off) or give it custom sessions. ### GET `/api/v1/appointment-settings/date-override/list/{doctorUuid}` Get all date overrides for a doctor. **Permission:** `AUTH` (class-level) ### Response `200` ```json { "success": true, "data": { "data": [ { "uuid": "...", "doctor_uuid": "...", "date": 1718476800, "active": false, "reason": "تعطیل خاص", "custom_slots": [], "created_at": 1717000000 } ] } } ``` > `date` is a Unix timestamp. `active: false` = entire day blocked. `active: true` with `custom_slots` = custom session schedule. --- ### POST `/api/v1/appointment-settings/date-override` Create a date override. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body ```json { "doctor_uuid": "550e8400-...", "date": "2024-06-20", "active": false, "reason": "تعطیل رسمی", "custom_slots": null } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `doctor_uuid` | string (UUID) | ✅ | Doctor UUID | | `date` | string | ✅ | Date in `Y-m-d` format (e.g. `"2024-06-20"`) | | `active` | boolean | ❌ | `false` = full day off (default); `true` = use custom_slots | | `reason` | string | ❌ | Reason for override | | `custom_slots` | array\|null | ❌ | Custom sessions (same SessionConfig format as weekly schedule, see below) | **`custom_slots` format when `active: true`:** ```json { "custom_slots": [ { "start_time": "14:00", "end_time": "18:00", "duration_per_patient": 20, "location_id": 1973, "has_rest": false, "rest_interval": 0, "time_to_rest": 0, "patient_limit": null } ] } ``` **Legacy format (backward compatible):** ```json { "custom_slots": [ { "start": "14:00", "end": "18:00", "duration": 20 } ] } ``` ### Response `201` ```json { "success": true, "data": { "data": { "uuid": "override-uuid-...", "doctor_uuid": "...", "date": 1718476800, "active": false, "reason": "تعطیل رسمی", "custom_slots": [], "created_at": 1717000000 } } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not the doctor owner | | `ERR_VALIDATION_002` | 404 | Doctor not found | | `ERR_VALIDATION_001` | 422 | Invalid date format | --- ### GET `/api/v1/appointment-settings/date-override/{uuid}` Get a single date override. **Permission:** `AUTH` (class-level) ### Response `200` Override object (same structure as above). --- ### PATCH `/api/v1/appointment-settings/date-override/{uuid}` Update a date override. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body (all optional) ```json { "active": true, "reason": "جبران مرخصی", "custom_slots": [ { "start_time": "14:00", "end_time": "18:00", "duration_per_patient": 20, "location_id": 1973, "has_rest": false, "rest_interval": 0, "time_to_rest": 0, "patient_limit": null } ], "date": "2024-06-21" } ``` ### Response `200` Updated override object. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not the doctor owner | | `ERR_VALIDATION_002` | 404 | Override not found | --- ### DELETE `/api/v1/appointment-settings/date-override/{uuid}` Delete a date override. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Response `200` ```json { "success": true, "data": { "message": "Override با موفقیت حذف شد" } } ``` --- ## Holidays Mark a date range as holiday — all slots blocked, no overrides apply. ### GET `/api/v1/appointment-settings/holidays/list/{doctorUuid}` Get all holidays for a doctor. **Permission:** `AUTH` (class-level) ### Response `200` ```json { "success": true, "data": { "data": [ { "uuid": "...", "doctor_uuid": "...", "start_date": 1719792000, "end_date": 1720656000, "reason": "تعطیلات تابستانی", "active": true, "created_at": 1717000000 } ] } } ``` > `start_date` and `end_date` are Unix timestamps. Holidays take **highest priority** — they block the day even if a date override exists. --- ### POST `/api/v1/appointment-settings/holidays` Create a holiday range. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body ```json { "doctor_uuid": "550e8400-...", "start_date": "2024-07-01", "end_date": "2024-07-10", "reason": "تعطیلات تابستانی" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `doctor_uuid` | string (UUID) | ✅ | Doctor UUID | | `start_date` | string | ✅ | Start date `Y-m-d` | | `end_date` | string | ✅ | End date `Y-m-d` (must be ≥ start_date) | | `reason` | string | ❌ | Holiday reason | ### Response `201` ```json { "success": true, "data": { "data": { "uuid": "holiday-uuid-...", "doctor_uuid": "...", "start_date": 1719792000, "end_date": 1720656000, "reason": "تعطیلات تابستانی", "active": true, "created_at": 1717000000 } } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not the doctor owner | | `ERR_VALIDATION_002` | 404 | Doctor not found | | `ERR_VALIDATION_001` | 422 | end_date before start_date or invalid format | --- ### GET `/api/v1/appointment-settings/holidays/{uuid}` Get a single holiday. **Permission:** `AUTH` (class-level) --- ### PATCH `/api/v1/appointment-settings/holidays/{uuid}` Update a holiday. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Request Body (all optional) ```json { "start_date": "2024-07-02", "end_date": "2024-07-12", "reason": "تمدید تعطیلات", "active": false } ``` ### Response `200` Updated holiday object. --- ### DELETE `/api/v1/appointment-settings/holidays/{uuid}` Delete a holiday. **Permission:** `AUTH` — must be the doctor owner or `ROLE_ADMIN` ### Response `200` ```json { "success": true, "data": { "message": "تعطیلات حذف شد" } } ``` --- ## Slot Calculation Logic (Reference) The `SlotCalculatorService` calculates available slots in this priority order: 1. **Holiday** — if date falls in a holiday range → return empty (no slots) 2. **Date Override** — if a date override exists for this date: - `active: false` → return empty - `active: true` → use `custom_slots` sessions 3. **Weekly Schedule** — use the day's `sessions` array (only `active: true` sessions, sorted by `start_time`, overlapping sessions skipped) **Slot output format:** ```json [ { "start": 1718438400, "end": 1718439600, "start_time": "09:00", "end_time": "09:20", "location_id": 1973 } ] ``` --- ## Available Locations ### `GET /api/v1/appointment-settings/available-locations/{doctorUuid}` **Permission:** Public Returns all locations a doctor can assign as `location_id` in their schedule sessions. Includes both the doctor's personal addresses and the addresses of all clinics they belong to. #### Response `200` ```json { "success": true, "data": [ { "id": "5", "uuid": "...", "type": "personal", "clinic_id": null, "clinic_name": null, "name": "مطب شخصی", "address": "تهران، ...", "telephone": "09121234567", "map": { "latitude": "35.7", "longitude": "51.4" }, "city": { "id": "1", "name": "تهران" }, "province": { "id": "8", "name": "تهران" } }, { "id": "12", "uuid": "...", "type": "clinic", "clinic_id": "3", "clinic_name": "کلینیک نور", "name": "شعبه مرکزی", "address": "تهران، خیابان ولیعصر...", "telephone": "02112345678", "map": { "latitude": "35.699", "longitude": "51.337" }, "city": { "id": "1", "name": "تهران" }, "province": { "id": "8", "name": "تهران" } } ] } ``` | Field | Description | |-------|-------------| | `type` | `personal` = doctor's own address; `clinic` = clinic address | | `clinic_id` | ID of the clinic (only for type=clinic) | | `clinic_name` | Name of the clinic (only for type=clinic) | > Use `id` as the `location_id` value in weekly schedule sessions or date override sessions. #### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_002` | 404 | Doctor not found |