# Doctor API > فیلد `owner_status` (`claimed` | `unclaimed` | `pending_transfer`) به خروجی لیست و جزئیات پزشک > اضافه شده است — پروفایل `unclaimed` (ایمپورت نظام پزشکی) در سایت دکمهٔ «تصاحب پروفایل» می‌گیرد > (`docs/api/doctor-claim.md`) و نوبت‌دهی آنلاینش غیرفعال است. > **Prefix:** `/api/v1/doctor`, `/api/v1/doctors`, `/api/v1/clinic-pro/doctor-address*` > > Numeric path params on the address routes (`doctor-address/{id}`, `doctor-addresses/{doctorId}`) require `\d+`; a non-numeric value returns a clean `404` instead of a `500`. --- ## POST `/api/v1/doctor` Create a doctor profile for the authenticated user. **Permission:** `AUTH` — any authenticated user ### Request Body (`application/json`) ```json { "title": "دکتر علی احمدی", "gender": "male", "medical_system_code": "12345", "degree": "متخصص", "info": "توضیحات درباره پزشک", "specialties": [1, 2], "doctor_services": [3, 4] } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | ✅ | Full name with title | | `gender` | string | ❌ | `"male"` or `"female"` | | `medical_system_code` | string | ❌ | Nظام پزشکی code | | `degree` | string | ❌ | Academic degree | | `info` | string | ❌ | Bio/description | | `specialties` | integer[] | ❌ | Array of specialty IDs | | `doctor_services` | integer[] | ❌ | Array of doctor service IDs | | `activity_time` | integer | ❌ | Unix timestamp (ثانیه) تاریخ شروع فعالیت؛ مبنای محاسبهٔ `experience` (سال تجربه) در پاسخ | ### Response `201` ```json { "success": true, "data": { "uuid": "550e8400-e29b-41d4-a716-446655440000", "title": "دکتر علی احمدی", "gender": "male", "medical_system_code": "12345", "degree": "متخصص", "info": "...", "doctor_rate": null, "active_doctor_appointment": false, "specialties": [], "doctor_services": [], "created_at": 1717000000 } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing or invalid token | | `ERR_CONFLICT_001` | 409 | Doctor profile already exists for this user | | `ERR_VALIDATION_002` | 422 | Missing required field | --- ## GET `/api/v1/doctor/{uuid}` Get doctor detail with clinics. **Permission:** `PUBLIC` ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `uuid` | string (UUID) | Doctor UUID | ### Response `200` ```json { "success": true, "data": { "data": { "uuid": "550e8400-...", "name": "دکتر علی احمدی", "gender": "man", "medical_system_code": "12345", "degree": "specialist", "detail": "...", "img": [], "social_media": { "instagram": "https://instagram.com/dr.example", "telegram": "https://t.me/dr_example", "aparat": null, "youtube": null, "linkedin": null }, "satisfaction": "60", "point": "3.5", "free_turn": "دوشنبه 09:00–13:00", "hours_of_work": "شنبه: 09:00–13:00 و 14:00–18:00 | یکشنبه: 09:00–13:00", "active": true, "owner_status": "claimed", "specialties": [{ "uuid": "...", "id": "1", "name": "قلب و عروق", "parent_id": null }], "expertise": [{ "uuid": "...", "id": "3", "name": "نوار قلب" }], "address": [], "state": [], "city": [], "clinics": [{ "uuid": "...", "name": "کلینیک الوند", "address": "...", "telephone": "..." }] } } } ``` > ⚠️ **Double-nested:** Frontend extracts with `data?.data?.data` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_NOT_FOUND_001` | 404 | Doctor not found | --- ## GET `/api/v1/clinic/my-doctor/{doctorUuid}` Get doctor detail for clinic owner — only doctors who are members of the authenticated clinic. **Permission:** `ROLE_CLINIC` ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `doctorUuid` | string (UUID) | Doctor UUID | ### Response `200` ```json { "success": true, "data": { "data": { "uuid": "...", "title": "دکتر علی احمدی", "specialties": [...], "clinics": [{ "uuid": "...", "name": "کلینیک نور", "address": "...", "telephone": "..." }] } } } ``` > ⚠️ **Double-nested:** Frontend extracts with `data?.data?.data` > **Side note:** Returns only the authenticated clinic's data in the `clinics` array (not all clinics of the doctor). ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_002` | 404 | Clinic or doctor not found | | `ERR_AUTH_006` | 403 | Doctor is not a member of this clinic | --- ### Schedule Fields Notes | Field | When schedule exists | When no schedule | |-------|---------------------|-----------------| | `free_turn` | نزدیک‌ترین روز/ساعت کاری از امروز (مثلاً «دوشنبه ۹:۰۰–۱۳:۰۰») | «نوبت آزادی موجود نیست» | | `hours_of_work` | خلاصه ساعت‌های روزهای فعال با `\|` جداشده | «برنامه کاری تنظیم نشده» | | `active` | `online_booking_enabled && has_active_sessions` | `false` — نوبت‌دهی غیرفعال | > **نوبت‌دهی آنلاین غیرفعال:** منبعِ فعال/غیرفعال بودن نوبت‌دهی آنلاین، فیلد `meta.online_booking_enabled` در `WeeklySchedule` پزشک است. اگر `false` باشد، صرف‌نظر از سشن‌های برنامه‌ی هفتگی، `free_turn` همیشه `"نوبت‌دهی آنلاین غیرفعال است"` و `active` برابر `false` برمی‌گردد؛ `hours_of_work` در صورت وجود برنامه حفظ می‌شود. چنین پزشکی در لیست عمومی `GET /api/v1/doctors` (پیش‌فرض `active=true`) نمایش داده نمی‌شود، ولی صفحه‌ی تکی `GET /api/v1/doctor/{slug}` همچنان قابل دسترسی است. --- ## GET `/api/v1/doctors` List doctors with pagination and filters. **Permission:** `PUBLIC` ### Query Parameters | Param | Type | Required | Description | |-------|------|----------|-------------| | `page` | integer | ❌ | Default: 1 | | `limit` | integer | ❌ | Default: 20 | | `search` | string | ❌ | Search in title | | `specialty_id` | integer | ❌ | Filter by specialty ID | | `city_id` | integer | ❌ | Filter by city ID — شامل دکترهایی که آدرس شخصی‌شان (`doctor_addresses.city_id`, با `doctor_id` مقداردار) در آن شهر است یا از طریق کلینیکی که آدرس آن در آن شهر است (`doctor_addresses.clinic_id`) | | `state_id` | integer | ❌ | Filter by province ID — بر اساس آدرس شخصی پزشک (`doctor_addresses.province_id`) یا آدرس کلینیک | | `domain` | string | ❌ | دامنه‌ی سایتِ درخواست‌کننده. اگر دامنه‌ی یک **نماینده سراسری** باشد، فقط پزشکانِ همان نماینده برمی‌گردند و `city_id`/`state_id` نادیده گرفته می‌شوند؛ دامنه شهری/ناشناخته اثری ندارد | ### Response `200` ```json { "success": true, "data": [ { "uuid": "...", "name": "دکتر علی احمدی", "gender": "man", "degree": "specialist", "img": [], "specialties": [{ "uuid": "...", "id": "1", "name": "قلب و عروق" }], "satisfaction": "60", "point": "3.5", "free_turn": "دوشنبه 09:00–13:00", "hours_of_work": "شنبه: 09:00–13:00 و 14:00–18:00 | یکشنبه: 09:00–13:00", "active": true, "owner_status": "claimed" } ], "meta": { "totalRecords": 50, "totalPages": 3, "currentPage": 1 } } ``` --- ## PATCH `/api/v1/doctor/{uuid}` Update doctor profile. **Permission:** `AUTH` — must be the owner (or `ROLE_ADMIN`) ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `uuid` | string (UUID) | Doctor UUID | ### Request Body (`application/json`) Same fields as POST (all optional), plus: | Field | Type | Description | |-------|------|-------------| | `social_media` | object | Keys: `instagram`, `telegram`, `aparat`, `youtube`, `linkedin`. Each value must be a full valid URL or `null`. Any value that fails `FILTER_VALIDATE_URL` is silently stored as `null`. | ```json { "social_media": { "instagram": "https://instagram.com/dr.example", "telegram": "https://t.me/dr_example", "aparat": null, "youtube": null, "linkedin": null } } ``` ### Response `200` Updated doctor object (same structure as GET single). ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_FORBIDDEN_001` | 403 | Not the owner | | `ERR_NOT_FOUND_001` | 404 | Doctor not found | --- ## DELETE `/api/v1/doctor/{uuid}` Delete a doctor profile. **Permission:** `ROLE_ADMIN` > **Side effect:** the doctor's insurance configuration (`tenant_insurances`, `entity_insurance_pricing`, and their `tenant_service_coverages`) is purged in the same request — these reference the doctor via a polymorphic `entity_id` with no DB FK, so the cleanup is enforced in the application. > **Guard:** a doctor with existing appointments cannot be deleted (the `appointments.doctor_id` FK would otherwise raise a `500`). The endpoint pre-checks and returns `409 ERR_CONFLICT_001` instead. ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `uuid` | string (UUID) | Doctor UUID | ### Response `200` ```json { "success": true, "data": { "message": "پزشک با موفقیت حذف شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not admin | | `ERR_NOT_FOUND_001` | 404 | Doctor not found | | `ERR_CONFLICT_001` | 409 | Doctor has existing appointments and cannot be deleted | --- ## POST `/file/upload/clinic_pro/doctor/field_image` Upload doctor profile image. **Permission:** `AUTH` ### Request `Content-Type: multipart/form-data` | Field | Type | Required | Description | |-------|------|----------|-------------| | `file` | binary | ✅ | Image file (max 5MB) | ### Response `200` ```json { "success": true, "data": { "url": "https://clinic-pro.ddev.site/uploads/doctor/abc123.jpg", "uuid": "...", "filename": "abc123.jpg", "filemime": "image/jpeg", "filesize": 204800 } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_FILE_001` | 422 | Invalid file type | | `ERR_AUTH_001` | 401 | Missing token | --- ## GET `/api/v1/clinic-pro/doctor-addresses/{doctorId}` Get all practice addresses for a doctor, including addresses of clinics the doctor is a member of. **Permission:** `PUBLIC` ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `doctorId` | integer | Doctor's numeric ID (route requires `\d+`; non-numeric → 404) | ### Response `200` ```json { "success": true, "data": [ { "id": "1", "uuid": "...", "type": "personal", "clinic_id": null, "clinic_name": null, "name": "مطب تهران", "address": "تهران، خیابان...", "telephone": "02112345678", "map": { "latitude": "35.6892", "longitude": "51.3890" }, "city": { "id": "1", "name": "تهران" }, "province": { "id": "1", "name": "تهران" } }, { "id": "5", "uuid": "...", "type": "clinic", "clinic_id": 12, "clinic_name": "کلینیک الوند", "name": null, "address": "اصفهان، خیابان...", "telephone": "03112345678", "map": { "latitude": null, "longitude": null }, "city": { "id": "3", "name": "اصفهان" }, "province": { "id": "2", "name": "اصفهان" } } ] } ``` > **نکته:** آدرس‌های با `type: "clinic"` از کلینیک‌هایی که پزشک عضو آن‌هاست می‌آیند و `clinic_name` نام کلینیک را نشان می‌دهد. --- ## POST `/api/v1/clinic-pro/doctor-address` Add a new practice address. **Permission:** `AUTH` — must own the doctor profile ### Request Body ```json { "name": "مطب تهران", "address": "تهران، خیابان ولیعصر", "telephone": "02112345678", "province_id": 1, "city_id": 3, "latitude": 35.6892, "longitude": 51.3890 } ``` | Field | Type | Required | |-------|------|----------| | `name` | string | ❌ | | `address` | string | ✅ (frontend validation) | | `telephone` | string | ✅ (frontend validation) | | `province_id` | integer | ✅ (frontend validation) | | `city_id` | integer | ✅ (frontend validation) | | `latitude` | float | ❌ | | `longitude` | float | ❌ | ### Response `201` ```json { "success": true, "data": { "id": 1, "name": "مطب تهران", "address": "تهران، خیابان ولیعصر", "telephone": "02112345678", "latitude": 35.6892, "longitude": 51.3890 } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_FORBIDDEN_001` | 403 | Not the doctor owner | | `ERR_NOT_FOUND_001` | 404 | Doctor not found | --- ## PATCH `/api/v1/clinic-pro/doctor-address/{id}` Update a practice address. **Permission:** `AUTH` — must own the doctor profile ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `id` | integer | Address ID | ### Request Body Same fields as POST — all optional. ### Response `200` Updated address object. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_FORBIDDEN_001` | 403 | Not the owner | | `ERR_NOT_FOUND_001` | 404 | Address not found | --- ## DELETE `/api/v1/clinic-pro/doctor-address/{id}` Delete a practice address. **Permission:** `AUTH` — must own the doctor profile ### Response `200` ```json { "success": true, "data": { "message": "آدرس حذف شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_FORBIDDEN_001` | 403 | Not the owner | | `ERR_NOT_FOUND_001` | 404 | Address not found | --- ## POST `/api/v1/clinic-pro/doctor-address/from-clinic/{clinicUuid}` Create a doctor address automatically from a clinic's location. **Permission:** `AUTH` — must own the doctor profile and be associated with the clinic ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `clinicUuid` | string (UUID) | Clinic UUID | ### Response `201` Address object created from clinic data. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_FORBIDDEN_001` | 403 | Not associated with this clinic | | `ERR_NOT_FOUND_001` | 404 | Clinic not found |