Files
clinicpro/docs/api/doctor.md
T
hamed 9f56f4aa08 feat(migrations): add ownership fields to doctors table for IRIMC import
- Introduced new columns: owner_status, source, source_ref, managed_by, and claimed_at to the doctors table.
- Created indexes for owner_status and source to optimize queries related to unclaimed doctors.

feat(auth): implement SystemOwnerCommand for managing system-owner user

- Added command to create, activate, and deactivate a system-owner user for IRIMC crawler.
- Ensured the user has ROLE_ADMIN to access import endpoints.
- Handled password setting and user status management within the command.
2026-07-11 08:59:36 +03:30

14 KiB
Raw Blame History

Doctor API

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)

{
  "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

{
  "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

{
  "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:0013:00",
      "hours_of_work": "شنبه: 09:0013:00 و 14:0018:00 | یکشنبه: 09:0013:00",
      "active": true,
      "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

{
  "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

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "name": "دکتر علی احمدی",
      "gender": "man",
      "degree": "specialist",
      "img": [],
      "specialties": [{ "uuid": "...", "id": "1", "name": "قلب و عروق" }],
      "satisfaction": "60",
      "point": "3.5",
      "free_turn": "دوشنبه 09:0013:00",
      "hours_of_work": "شنبه: 09:0013:00 و 14:0018:00 | یکشنبه: 09:0013:00",
      "active": true
    }
  ],
  "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.
{
  "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

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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

{ "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