# Location API (Province & City) > **Prefix:** `/api/v1/provinces`, `/api/v1/cities`, `/api/v1/admin/province`, `/api/v1/admin/city` --- ## GET `/api/v1/provinces` List all active provinces. **Permission:** `PUBLIC` ### Response `200` ```json { "success": true, "data": [ { "id": 1, "name": "تهران", "status": "active", "weight": 1 }, { "id": 2, "name": "اصفهان", "status": "active", "weight": 2 } ] } ``` --- ## GET `/api/v1/cities` List cities, optionally filtered by province. **Permission:** `PUBLIC` ### Query Parameters | Param | Type | Required | Description | |-------|------|----------|-------------| | `province_id` | integer | ❌ | Filter cities by province | ### Response `200` ```json { "success": true, "data": [ { "id": 10, "name": "تهران", "province_id": 1, "status": "active", "weight": 1 }, { "id": 11, "name": "کرج", "province_id": 1, "status": "active", "weight": 2 } ] } ``` --- ## GET `/api/v1/admin/provinces` List all provinces with pagination (admin view — includes inactive). **Permission:** `ROLE_ADMIN` ### Query Parameters | Param | Type | Required | Description | |-------|------|----------|-------------| | `page` | integer | ❌ | Default: 1 | | `limit` | integer | ❌ | Default: 20 | | `search` | string | ❌ | Search in name | ### Response `200` ```json { "success": true, "data": [ ... ], "meta": { "totalRecords": 31, "totalPages": 2, "currentPage": 1 } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not admin | --- ## GET `/api/v1/admin/cities` List all cities with pagination (admin view). **Permission:** `ROLE_ADMIN` ### Query Parameters | Param | Type | Required | Description | |-------|------|----------|-------------| | `page` | integer | ❌ | Default: 1 | | `limit` | integer | ❌ | Default: 20 | | `search` | string | ❌ | Matches `name`, `site_name`, `domain`, **or the parent province's name** (case-insensitive `LIKE`) | | `province_id` | integer | ❌ | Filter by province | ### Response `200` Paginated city list. Each row already carries `province_name` alongside `province_id`. --- ## POST `/api/v1/admin/province` Create a new province. **Permission:** `ROLE_ADMIN` ### Request Body (`application/json`) ```json { "name": "تهران", "status": "active", "weight": 1 } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | ✅ | Province name | | `status` | string | ❌ | `"active"` (default) or `"inactive"` | | `weight` | integer | ❌ | Sort weight | ### Response `201` Province object. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not admin | | `ERR_VALIDATION_002` | 422 | Missing name | --- ## PATCH `/api/v1/admin/province/{id}` Update a province. **Permission:** `ROLE_ADMIN` ### Path Parameters | Param | Type | Description | |-------|------|-------------| | `id` | integer | Province ID | All fields optional. ### Response `200` Updated province object. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not admin | | `ERR_NOT_FOUND_001` | 404 | Province not found | --- ## DELETE `/api/v1/admin/province/{id}` Delete a province. **Permission:** `ROLE_ADMIN` ### Response `200` ```json { "success": true, "data": { "message": "استان حذف شد" } } ``` --- ## POST `/api/v1/admin/city` Create a new city. **Permission:** `ROLE_ADMIN` ### Request Body (`application/json`) ```json { "name": "تهران", "site_name": "تهران نوبت", "province_id": 1, "status": "active", "weight": 1 } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | ✅ | City name | | `site_name` | string | ❌ | Public site name shown on the city's booking website (e.g. `"یزد نوبت"`) | | `title` | string | ❌ | SEO meta title for the city's booking website (e.g. `"نوبت‌دهی آنلاین پزشکان یزد | یزد نوبت"`) | | `province_id` | integer | ❌ | Parent province ID | | `status` | string | ❌ | `"active"` or `"inactive"` | | `weight` | integer | ❌ | Sort weight | | `contact_phone`, `email`, `description`, `slogan`, `domain`, `keywords`, `footer_description`, `social_media`, `logo_url` | mixed | ❌ | SEO / branding fields returned in the city object | ### Response `201` City object. ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing token | | `ERR_AUTH_006` | 403 | Not admin | | `ERR_VALIDATION_002` | 422 | Missing name | --- ## PATCH `/api/v1/admin/city/{id}` Update a city. **Permission:** `ROLE_ADMIN` All body fields optional. ### Response `200` Updated city object. --- ## DELETE `/api/v1/admin/city/{id}` Delete a city. **Permission:** `ROLE_ADMIN` ### Response `200` ```json { "success": true, "data": { "message": "شهر حذف شد" } } ``` --- ## GET `/api/v1/categorys/{bundle}` *(Legacy)* Legacy endpoint that proxies to the new endpoints. **Permission:** `PUBLIC` ### Path Parameters | Bundle | Maps to | |--------|---------| | `state` | `/api/v1/provinces` | | `city` | `/api/v1/cities` | | `specially_doctor` | `/api/v1/specialties` | | `doctor_services` | `/api/v1/doctor-services` | | `insurance_type` | `/api/v1/insurances?type=basic` | | `supplementary_insurance` | `/api/v1/insurances?type=supplementary` | | `tag` | `/api/v1/tags` | > ⚠️ Response is **triple-nested**: `data?.data?.data ?? []` > Note: `categorys` (not `categories`) is intentional — legacy route name. --- ## Bulk import / export Full-table JSON export and strict wipe+replace import for this category live under `/api/v1/admin/categories/{bundle}/{export|import}` — see [category-import.md](category-import.md). ### Sorting by id The admin list endpoint accepts `sort=id&order=asc|desc` to order by `id` (used by the admin «دسته‌بندی‌ها» page when clicking the «شناسه» column). Without `sort`, the default ordering (weight/name) is unchanged.