# Admin API Reference

> Base URL: `{{base_url}}/api/v1/admin`  
> All protected routes require: `Authorization: Bearer {token}`  
> All requests/responses use `Content-Type: application/json`  
> Locale is controlled via `Accept-Language: ar` or `Accept-Language: en`

---

## Authentication

### Public

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/auth/login` | Login with email + password |
| POST | `/auth/forgot-password` | Step 1 — send OTP |
| POST | `/auth/resend-code` | Resend OTP |
| POST | `/auth/verify-otp` | Step 2 — verify OTP |
| POST | `/auth/reset-password` | Step 3 — set new password |

### Protected

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/auth/logout` | Logout |
| GET | `/auth/profile` | Get own profile |
| PUT | `/auth/profile` | Update own profile (full_name, email, locale, allow_notifications) |
| PUT | `/auth/change-password` | Change password |

> **Note:** Admin profile updates allow direct email changes (no OTP required).  
> Provider profiles require OTP verification for email changes via separate endpoints.

---

## Roles & Permissions

> Admin role and permission management system for granular access control.

### Roles Management

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/roles` | List admin roles |
| POST | `/roles` | Create new role |
| GET | `/roles/{id}` | Show role details |
| PUT | `/roles/{id}` | Update role |
| DELETE | `/roles/{id}` | Delete role |

#### List Admin Roles
**`GET /roles`**

**Query Parameters:**
- `paginate` (boolean, default: true) - Enable/disable pagination
- `per_page` (integer, default: 15) - Items per page

**Response:**
```json
{
  "status": "success",
  "message": "Data retrieved successfully", 
  "data": [
    {
      "id": 5,
      "name": "مدير المحتوى",
      "type": "admin",
      "provider_id": null,
      "permissions_count": 8,
      "users_count": 3,
      "created_at": 1725198388
    }
  ]
}
```

#### Create Admin Role
**`POST /roles`**

**Request:**
```json
{
  "name": "مدير المحتوى",
  "permissions": [1, 3, 7, 12, 15, 18, 22, 25]
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Role created successfully",
  "data": {
    "id": 5,
    "name": "مدير المحتوى",
    "type": "admin", 
    "provider_id": null,
    "permissions_count": 8,
    "users_count": 0,
    "permissions": [
      {
        "id": 1,
        "title": "عرض الفئات"
      }
    ],
    "created_at": 1725198388
  }
}
```

### Permissions Management

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/permissions` | List available permissions |
| GET | `/permissions/grouped` | Permissions grouped by module |
| GET | `/permissions/my-permissions` | Current user permissions |

#### List Admin Permissions
**`GET /permissions`**

**Response:**
```json
{
  "status": "success", 
  "message": "Data retrieved successfully",
  "data": [
    {
      "id": 1,
      "title": "عرض الفئات"
    },
    {
      "id": 2,
      "title": "إنشاء فئة"
    }
  ]
}
```

#### Permissions Grouped
**`GET /permissions/grouped`**

**Response:**
```json
{
  "status": "success",
  "message": "Data retrieved successfully",
  "data": {
    "Content Management": [
      {
        "id": 1,
        "title": "عرض الفئات"
      }
    ],
    "User Management": [
      {
        "id": 15,
        "title": "عرض العملاء"
      }
    ]
  }
}
```

---

## Categories & Subcategories

> Same CRUD, differentiated by `level` field.  
> `level: 0` = main category (requires icon)  
> `level: 1` = subcategory (requires parent_id)

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/categories` | List all (paginated). Filter: `?filters[level]=0`, `?filters[parent_id]=5`, `?search=name` |
| GET | `/categories/list` | Non-paginated dropdown list. Params: `?level=0`, `?parent_id=5` |
| GET | `/categories/{id}` | Show single |
| POST | `/categories` | Create |
| PUT | `/categories/{id}` | Update |
| DELETE | `/categories/{id}` | Delete |
| PATCH | `/categories/{id}/status` | Toggle is_active |
| PATCH | `/categories/{id}/icon` | Update icon |

### Create Category Payload
```json
{
  "level": 0,
  "order": 1,
  "is_active": true,
  "icon": 12,
  "ar": { "name": "الاستشارات" },
  "en": { "name": "Consultations" }
}
```

### Create Subcategory Payload
```json
{
  "level": 1,
  "parent_id": 3,
  "order": 1,
  "is_active": true,
  "ar": { "name": "تجاري" },
  "en": { "name": "Commercial" }
}
```

---

## Provider Types

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/provider-types` | List (paginated). Filter: `?filters[is_active]=1`, `?search=name` |
| GET | `/provider-types/{id}` | Show single |
| POST | `/provider-types` | Create |
| PUT | `/provider-types/{id}` | Update |
| DELETE | `/provider-types/{id}` | Delete |
| PATCH | `/provider-types/{id}/status` | Toggle is_active |

### Payload
```json
{
  "is_active": true,
  "ar": { "name": "محامي" },
  "en": { "name": "Lawyer" }
}
```

---

## Languages

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/languages` | List (paginated). Filter: `?filters[is_active]=1`, `?search=name` |
| GET | `/languages/{id}` | Show single |
| POST | `/languages` | Create |
| PUT | `/languages/{id}` | Update |
| DELETE | `/languages/{id}` | Delete |
| PATCH | `/languages/{id}/status` | Toggle is_active |

### Payload
```json
{
  "is_active": true,
  "ar": { "name": "العربية" },
  "en": { "name": "Arabic" }
}
```

---

## Provider Registration Requests

> Providers self-register through the mobile app. Once they complete all 4 steps and verify their OTP, their `account_status` becomes `pending` and they appear here for admin review.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/provider-requests` | List requests. Default: pending. Filter: `?status=pending\|accepted\|rejected` |
| GET | `/provider-requests/{id}` | Show full registration details |
| POST | `/provider-requests/{id}/accept` | Accept — no body required |
| POST | `/provider-requests/{id}/reject` | Reject — requires `rejection_note` |

### Reject Payload
```json
{
  "rejection_note": "License document image is unclear. Please resubmit."
}
```

### Status values
| Value | Meaning |
|---|---|
| `pending` | OTP verified, waiting for admin review |
| `accepted` | Admin approved — provider can now log in |
| `rejected` | Admin rejected — provider cannot log in |

### What happens on accept
- `account_status` → `accepted`
- `is_admin_active` → `true`
- Provider receives FCM push + in-app notification

### What happens on reject
- `account_status` → `rejected`
- `is_admin_active` → `false`
- Provider receives FCM push (email when configured)

### Show response includes
All 4 registration steps: personal info, professional info, location/languages/services, and all uploaded documents.

---

## Clients Management

> Manage client accounts. Create, update, activate/deactivate, and view client details including wallet balance and broker information.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/clients` | List all clients (paginated). Filter: `?filters[is_active]=1`, `?filters[broker_code]=`, `?search=name` |
| GET | `/clients/{id}` | Show single client details |
| POST | `/clients` | Create new client |
| PUT | `/clients/{id}` | Update client profile |
| DELETE | `/clients/{id}` | Delete client (soft delete) |
| PATCH | `/clients/{id}/status` | Toggle `is_admin_active_user` (activate/deactivate account) |

### Create Client Payload

#### Individual Account
```json
{
  "account_type": "individual",
  "full_name": "محمد أحمد",
  "email": "mohammed@example.com",
  "phone_code": "966",
  "phone": "512345678",
  "password": "Secret@123",
  "password_confirmation": "Secret@123",
  "avatar": 123
}
```

#### Company Account
```json
{
  "account_type": "company",
  "full_name": "محمد أحمد",
  "company_name": "شركة النخبة للاستشارات القانونية",
  "commercial_register_number": "1234567890",
  "email": "info@nokhba-legal.com",
  "phone_code": "966",
  "phone": "512345678",
  "password": "Secret@123",
  "password_confirmation": "Secret@123",
  "avatar": 123
}
```

### Update Client Payload
```json
{
  "account_type": "company",
  "full_name": "محمد أحمد علي",
  "company_name": "شركة النخبة المحدثة",
  "commercial_register_number": "1234567890",
  "email": "mohammed@example.com",
  "phone_code": "966",
  "phone": "512345678",
  "avatar": 124
}
```

> All fields are optional on update. Only send what needs to be updated.  
> Admin can update email and phone directly without OTP verification.  
> Password is optional on update — only include if changing password.

### Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
| `account_type` | string | ✅ (create) | "individual" or "company" |
| `full_name` | string | ✅ (create) | Client full name (2-100 chars) |
| `company_name` | string | ✅ (if company) | Company name (2-255 chars) |
| `commercial_register_number` | string | ✅ (if company) | Unique commercial register number (5-50 chars) |
| `email` | string | ✅ (create) | Unique email |
| `phone_code` | string | ✅ (create) | Country phone code (e.g., "966") |
| `phone` | string | ✅ (create) | Phone number (7-15 digits) |
| `password` | string | ✅ (create) | Min 8 chars, requires confirmation |
| `password_confirmation` | string | ✅ (create) | Must match password |
| `avatar` | integer | ❌ | Media ID for profile picture |

### Client Response Example
```json
{
  "status": "success",
  "message": "Data retrieved successfully",
  "data": {
    "id": 42,
    "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "account_type": "company",
    "full_name": "محمد أحمد",
    "email": "info@nokhba-legal.com",
    "phone_code": "966",
    "phone": "512345678",
    "user_type": "client",
    "locale": "ar",
    "allow_notifications": true,
    "is_active": true,
    "is_admin_active_user": true,
    "avatar": "https://...",
    "company_name": "شركة النخبة للاستشارات القانونية",
    "commercial_register_number": "1234567890",
    "broker_code": "BRK-A3F2G1",
    "broker_clients_count": 12,
    "broker_orders_count": 45,
    "broker_total_earnings": 1250.5,
    "wallet_balance": 500.0,
    "created_at": 1725198388,
    "last_login_at": 1725284788
  }
}
```

### List Clients Filters
```
?filters[is_active]=1              # Active accounts only
?search=ahmed                      # Search by name, email, phone
?sort[created_at]=desc             # Sort by registration date
?sort[wallet_balance]=desc         # Sort by wallet balance
```

### Client Response Fields Description

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Client unique ID |
| `uuid` | string | Client UUID |
| `account_type` | string | "individual" or "company" |
| `full_name` | string | Client full name |
| `email` | string | Email address |
| `phone_code` | string | Country phone code (e.g., "966") |
| `phone` | string | Phone number |
| `user_type` | string | Always "client" |
| `locale` | string | Preferred language (ar/en) |
| `allow_notifications` | boolean | Push notification preference |
| `is_active` | boolean | Account active status |
| `is_admin_active_user` | boolean | Admin approval status |
| `avatar` | string\|null | Profile picture URL |
| `company_name` | string\|null | Company name (if account_type = "company") |
| `commercial_register_number` | string\|null | CR number (if account_type = "company") |
| `broker_code` | string | Unique referral code (e.g., "BRK-A3F2G1") |
| `broker_clients_count` | integer | Number of clients who used this broker code |
| `broker_orders_count` | integer | Number of orders created by referred clients |
| `broker_total_earnings` | float | Total earnings from broker referrals in SAR |
| `wallet_balance` | float | Current wallet balance in SAR |
| `created_at` | integer | Unix timestamp of account creation |
| `last_login_at` | integer\|null | Unix timestamp of last login |

**Note**: `updated_at` field is not included in responses. `phone_verified_at` is handled internally.

### Client Status Control
Toggle `is_admin_active_user`:
- `true` — Client can login and use the app
- `false` — Client is blocked, all active tokens invalidated

---

## Providers Management

> Manage accepted providers. Update profiles, toggle availability, activate/deactivate accounts.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/providers` | List all providers (paginated). Filter: `?filters[is_active]=1`, `?filters[is_available]=1`, `?filters[city_id]=1`, `?search=name` |
| GET | `/providers/{id}` | Show single provider details |
| GET | `/providers/{id}/schedule` | Show provider weekly schedule and hours |
| PUT | `/providers/{id}` | Update provider profile |
| DELETE | `/providers/{id}` | Delete provider (soft delete) |
| PATCH | `/providers/{id}/toggle-active` | Toggle `is_admin_active_user` (activate/deactivate account) |
| PATCH | `/providers/{id}/toggle-availability` | Toggle `is_available` (available for new orders) |

### Update Provider Payload
```json
{
  "full_name": "أحمد محمد علي",
  "email": "ahmed@example.com",
  "phone_code": "966",
  "phone": "512345678",
  "is_available": true,
  "brief": "محامي متخصص في القضايا التجارية",
  "address": "حي العليا، الرياض",
  "lat": 24.7136,
  "lng": 46.6753,
  "city_id": 1,
  "district_id": 4,
  "years_of_experience": 10,
  "license_expiration_date": "2027-12-31",
  "linkedin_link": "https://linkedin.com/in/ahmed",
  "website_link": "https://ahmed-law.com",
  "languages": [1, 2, 3]
}
```

> All fields are optional. Only send what needs to be updated.  
> Admin can update email and phone directly without OTP verification.

### Provider Status Fields
| Field | Type | Description |
|---|---|---|
| `is_admin_active_user` | boolean | Account activation — if `false`, provider cannot login |
| `is_available` | boolean | Availability status — if `false`, provider won't appear in search results |
| `account_status` | enum | `pending` / `accepted` / `rejected` — registration approval status |

### List Providers Filters
```
?filters[is_active]=1              # Active accounts only
?filters[is_available]=1           # Available providers only
?filters[city_id]=1                # Filter by city
?filters[provider_type_id]=2       # Filter by provider type
?filters[category_id]=3            # Filter by category
?search=ahmed                      # Search by name, email, phone
?sort[created_at]=desc             # Sort by registration date
```

---

## Services (updated)

> `price_from` and `price_to` define the allowed range for provider pricing.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/services` | List (paginated). Filter: `?filters[subcategory_id]=5`, `?search=title` |
| GET | `/services/list` | Non-paginated dropdown. Param: `?subcategory_id=5` |
| GET | `/services/{id}` | Show single |
| POST | `/services` | Create |
| PUT | `/services/{id}` | Update |
| DELETE | `/services/{id}` | Delete |
| PATCH | `/services/{id}/status` | Toggle is_active |
| PATCH | `/services/{id}/icon` | Update icon |

### Create Service Payload
```json
{
  "subcategory_id": 5,
  "order": 1,
  "is_active": true,
  "price_from": 100.00,
  "price_to": 500.00,
  "icon": 14,
  "ar": { "title": "استشارة قانونية" },
  "en": { "title": "Legal Consultation" }
}
```

> `price_from` and `price_to` are optional. When set, providers setting their prices for this service will be validated against this range.

---

## Service Form Fields

> Nested under a service. The service is identified in the URL.  
> **There is one create endpoint only — bulk. All fields are created in a single request.**

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/services/{service}/form-fields` | List all fields for this service (ordered) |
| POST | `/services/{service}/form-fields` | **Create fields** (bulk — send all fields at once) |
| GET | `/services/{service}/form-fields/{field}` | Show single field |
| PATCH | `/services/{service}/form-fields/{field}` | Update a single field (send only changed fields) |
| DELETE | `/services/{service}/form-fields/{field}` | Delete a single field |
| PATCH | `/services/{service}/form-fields/{field}/status` | Toggle field is_active (hide/show without deleting) |
| POST | `/services/{service}/form-fields/reorder` | Reorder fields |

### Field Types
`text` `textarea` `number` `email` `phone` `url` `radio` `select` `checkbox` `switch` `date` `time` `datetime` `file`

### Single Field Payload
```json
{
  "field_key": "consultation_type",
  "field_type": "radio",
  "is_required": true,
  "is_active": true,
  "is_repeatable": false,
  "has_media": false,
  "order": 1,
  "ar": { "label": "نوع الاستشارة", "placeholder": null, "help_text": null },
  "en": { "label": "Consultation Type", "placeholder": null, "help_text": null },
  "options": [
    { "value": "written", "label": { "ar": "كتابية", "en": "Written" }, "description": { "ar": null, "en": null } },
    { "value": "audio",   "label": { "ar": "صوتية",  "en": "Audio"   }, "description": { "ar": null, "en": null } }
  ]
}
```

### Textarea with meta
```json
{
  "field_key": "order_details",
  "field_type": "textarea",
  "is_required": true,
  "has_media": false,
  "order": 2,
  "ar": { "label": "تفاصيل الطلب", "placeholder": "اشرح طلبك", "help_text": "الحد الأقصى 500 حرف" },
  "en": { "label": "Order Details",  "placeholder": "Describe your request", "help_text": "Max 500 characters" },
  "meta": { "min_length": 20, "max_length": 500 }
}
```

### File field
```json
{
  "field_key": "attachments",
  "field_type": "file",
  "is_required": false,
  "is_repeatable": true,
  "has_media": true,
  "order": 6,
  "ar": { "label": "المستندات المرفقة", "help_text": "PDF, صورة, صوت, فيديو" },
  "en": { "label": "Attachments", "help_text": "PDF, image, audio, video" },
  "meta": {
    "max_size": 10240,
    "allowed_types": ["pdf", "jpg", "jpeg", "png", "mp3", "mp4"],
    "multiple": true
  }
}
```

### Bulk payload
```json
{
  "replace_all": false,
  "fields": [ ...array of field objects above... ]
}
```

### Reorder payload
```json
{
  "orders": [
    { "id": 1, "order": 0 },
    { "id": 2, "order": 1 },
    { "id": 3, "order": 2 }
  ]
}
```

### Meta keys by field type
| Field type | Supported meta keys |
|---|---|
| `text` / `textarea` | `min_length`, `max_length` |
| `number` | `min_value`, `max_value`, `step` |
| `date` / `datetime` | `min_date`, `max_date`, `date_format` |
| `time` | `min_time`, `max_time` |
| `file` | `max_size` (KB), `allowed_types`, `multiple` |
| `select` | `multiple_select` |
| `phone` | `phone_code` |
| `switch` | `on_label`, `off_label` |

---

## Ready Templates — Categories

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/ready-templates/categories` | List (paginated) |
| GET | `/ready-templates/categories/list` | Non-paginated dropdown |
| GET | `/ready-templates/categories/{id}` | Show single |
| POST | `/ready-templates/categories` | Create |
| PUT | `/ready-templates/categories/{id}` | Update |
| DELETE | `/ready-templates/categories/{id}` | Delete |
| PATCH | `/ready-templates/categories/{id}/status` | Toggle is_active |

### Payload
```json
{
  "is_active": true,
  "order": 1,
  "ar": { "name": "عقود" },
  "en": { "name": "Contracts" }
}
```

---

## Ready Templates

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/ready-templates` | List (paginated). Filter: `?filters[template_category_id]=2`, `?search=title` |
| GET | `/ready-templates/{id}` | Show single |
| POST | `/ready-templates` | Create |
| PUT | `/ready-templates/{id}` | Update |
| DELETE | `/ready-templates/{id}` | Delete |
| PATCH | `/ready-templates/{id}/status` | Toggle is_active |

### Payload
```json
{
  "template_category_id": 2,
  "price": 149.00,
  "pages_count": 6,
  "is_fillable": true,
  "is_active": true,
  "order": 1,
  "cover": 15,
  "pdf": 16,
  "ar": {
    "title": "عقد إيجار سكني",
    "description": "نموذج عقد إيجار سكني معتمد شامل جميع البنود القانونية...",
    "features": "عند شراء هذا النموذج ستحصل على:\n1. مستند جاهز بصيغة PDF قابل للتعديل والطباعة.\n2. صياغة متوافقة كلياً مع شبكة إيجار الوطنية والأنظمة السعودية.\n3. بنود قانونية متكاملة لحفظ حقوق المؤجر والمستأجر.\n4. تحميل مباشر وإمكانية استخدام فورية."
  },
  "en": {
    "title": "Residential Lease",
    "description": "Approved template for residential lease agreements...",
    "features": "Upon purchasing this template you will receive:\n1. Editable & printable document in PDF format.\n2. Fully compliant with Saudi Ejar network & housing regulations.\n3. Comprehensive legal clauses protecting lessor and lessee rights.\n4. Direct download with instant fillable PDF access."
  }
}
```

> `features` field is optional and supports multiline text with `\n` for line breaks.

---

## Emergency — Authorities (الجهة)

> Each authority has its own price. The client sees the price before submitting.

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/emergency/authorities` | List (paginated). Filter: `?filters[is_active]=1`, `?search=name` |
| GET | `/emergency/authorities/{id}` | Show single |
| POST | `/emergency/authorities` | Create |
| PUT | `/emergency/authorities/{id}` | Update |
| DELETE | `/emergency/authorities/{id}` | Delete |
| PATCH | `/emergency/authorities/{id}/status` | Toggle is_active |

### Payload
```json
{
  "price": 500.00,
  "is_active": true,
  "order": 1,
  "ar": { "name": "الشرطة" },
  "en": { "name": "Police" }
}
```

> `price` is **required** on create, optional on update.

### Predefined authorities (seed these)
الشرطة · النيابة العامة · مكافحة المخدرات · مكافحة الفساد · السجون · المرور · الجوازات · الدفاع المدني · البلدية · وزارة العمل · أخرى

---

## Emergency — Case Types (نوع الحالة / الإجراء)

> The type of legal action or procedure (e.g. قبض, تحقيق, مراجعة).

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/emergency/case-types` | List (paginated). Filter: `?filters[is_active]=1`, `?search=name` |
| GET | `/emergency/case-types/{id}` | Show single |
| POST | `/emergency/case-types` | Create |
| PUT | `/emergency/case-types/{id}` | Update |
| DELETE | `/emergency/case-types/{id}` | Delete |
| PATCH | `/emergency/case-types/{id}/status` | Toggle is_active |

### Payload
```json
{
  "is_active": true,
  "order": 1,
  "ar": { "name": "قبض" },
  "en": { "name": "Arrest" }
}
```

### Predefined case types (seed these)
قبض · استيقاف · تحقيق · مراجعة · متابعة إدارية

---

## Ready Templates & Categories

### Template Categories

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/ready-templates/categories` | List all categories (paginated). Filter: `?filters[is_active]=1`, `?search=name` |
| GET | `/ready-templates/categories/list` | Non-paginated dropdown list (active only) |
| GET | `/ready-templates/categories/{id}` | Show single category |
| POST | `/ready-templates/categories` | Create category |
| PUT | `/ready-templates/categories/{id}` | Update category |
| DELETE | `/ready-templates/categories/{id}` | Delete category |
| PATCH | `/ready-templates/categories/{id}/status` | Toggle is_active |

#### Payload
```json
{
  "order": 1,
  "is_active": true,
  "ar": { "name": "عقود" },
  "en": { "name": "Contracts" }
}
```

### Ready Templates

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/ready-templates` | List all templates (paginated). Filter: `?filters[template_category_id]=1`, `?filters[is_active]=1`, `?search=title` |
| GET | `/ready-templates/{id}` | Show single template |
| POST | `/ready-templates` | Create template |
| PUT | `/ready-templates/{id}` | Update template |
| DELETE | `/ready-templates/{id}` | Delete template |
| PATCH | `/ready-templates/{id}/status` | Toggle is_active |

#### Create / Update Payload
```json
{
  "template_category_id": 1,
  "price": 149.00,
  "pages_count": 4,
  "is_fillable": true,
  "is_active": true,
  "order": 1,
  "cover": 123,
  "pdf": 124,
  "ar": {
    "title": "عقد إيجار سكني",
    "description": "نموذج عقد إيجار سكني معتمد شامل جميع البنود القانونية...",
    "features": "عند شراء هذا النموذج ستحصل على:\n1. مستند جاهز بصيغة PDF قابل للتعديل والطباعة.\n2. صياغة متوافقة كلياً مع شبكة إيجار الوطنية والأنظمة السعودية.\n3. بنود قانونية متكاملة لحفظ حقوق المؤجر والمستأجر.\n4. تحميل مباشر وإمكانية استخدام فورية.\n5. دعم فني متاح عبر الواتساب."
  },
  "en": {
    "title": "Residential Lease Agreement",
    "description": "Standard residential lease agreement with all legal clauses...",
    "features": "Upon purchasing this template you will receive:\n1. Editable & printable document in PDF format.\n2. Fully compliant with Saudi Ejar network & housing regulations.\n3. Comprehensive legal clauses protecting lessor and lessee rights.\n4. Direct download with instant fillable PDF access.\n5. Technical support available via WhatsApp."
  }
}
```

#### Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
| `template_category_id` | integer | ✅ | Must exist in template_categories |
| `price` | decimal | ✅ | Template price in SAR |
| `pages_count` | integer | ✅ | Number of pages |
| `is_fillable` | boolean | ✅ | Whether PDF is editable/fillable |
| `is_active` | boolean | ❌ | Default: true |
| `order` | integer | ❌ | Display order |
| `cover` | integer | ✅ | Media ID for cover image |
| `pdf` | integer | ✅ | Media ID for PDF file |
| `ar.title` | string | ✅ | Arabic title |
| `ar.description` | string | ✅ | Arabic description |
| `ar.features` | text | ❌ | Arabic features list (supports line breaks `\n`) |
| `en.title` | string | ✅ | English title |
| `en.description` | string | ✅ | English description |
| `en.features` | text | ❌ | English features list (supports line breaks `\n`) |

> **Note:** `features` field supports multiline text. Use `\n` for line breaks to create bullet points or numbered lists.

---

## Cities

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/cities` | List (paginated). Filter: `?filters[status]=1`, `?search=name` |
| GET | `/cities/list` | Non-paginated dropdown |
| GET | `/cities/{id}` | Show single |
| POST | `/cities` | Create |
| PUT | `/cities/{id}` | Update |
| DELETE | `/cities/{id}` | Delete (soft) |
| PATCH | `/cities/{id}/status` | Toggle status |

### Payload
```json
{
  "status": true,
  "ar": { "name": "الرياض" },
  "en": { "name": "Riyadh" }
}
```

> `country_id` is assigned automatically to the single seeded country — no need to pass it.

---

## Districts

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/districts` | List (paginated). Filter: `?filters[city_id]=1`, `?filters[status]=1`, `?search=name` |
| GET | `/districts/{id}` | Show single |
| POST | `/districts` | Create |
| PUT | `/districts/{id}` | Update |
| DELETE | `/districts/{id}` | Delete (soft) |
| PATCH | `/districts/{id}/status` | Toggle status |

### Payload
```json
{
  "city_id": 1,
  "status": true,
  "ar": { "name": "النخيل" },
  "en": { "name": "Al Nakheel" }
}
```

---

## Provider Registration Lookups

> These are the lookup tables providers select from during registration. All support `?search=` and return `{ id, name }` items.

### Provider Types

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/provider-types` | List (paginated) |
| GET | `/provider-types/{id}` | Show single |
| POST | `/provider-types` | Create |
| PUT | `/provider-types/{id}` | Update |
| DELETE | `/provider-types/{id}` | Delete |
| PATCH | `/provider-types/{id}/status` | Toggle is_active |

```json
{
  "is_active": true,
  "ar": { "name": "محامي" },
  "en": { "name": "Lawyer" }
}
```

### Languages

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/languages` | List (paginated) |
| GET | `/languages/{id}` | Show single |
| POST | `/languages` | Create |
| PUT | `/languages/{id}` | Update |
| DELETE | `/languages/{id}` | Delete |
| PATCH | `/languages/{id}/status` | Toggle is_active |

```json
{
  "is_active": true,
  "ar": { "name": "العربية" },
  "en": { "name": "Arabic" }
}
```

---

## Static Pages

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/types` | List available page types |
| GET | `/list/{type?}` | List pages (optionally filtered by type) |
| GET | `/pages` | List all pages (paginated) |
| GET | `/pages/{id}` | Show single page |
| POST | `/pages` | Create page |
| POST | `/pages/about` | Upsert the About page (single record) |
| PUT | `/pages/{id}` | Update page |
| DELETE | `/pages/{id}` | Delete page |

### Page Types
`privacy` · `about` · `terms` · `refund` · `off_platform`

### Payload
```json
{
  "type": "privacy",
  "ar": { "title": "سياسة الخصوصية", "desc": "..." },
  "en": { "title": "Privacy Policy", "desc": "..." }
}
```

---

## Contacts

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/contacts` | List contact submissions |
| GET | `/contacts/{id}` | Show single |
| DELETE | `/contacts/{id}` | Delete |
| POST | `/contacts/{id}/reply` | Reply to contact |

---

## Common Patterns

### Pagination
All index endpoints support: `?per_page=15&page=1`

### Filtering
```
?filters[is_active]=1
?filters[parent_id]=3
?filters[subcategory_id]=5
?filters[template_category_id]=2
```

### Search
`?search=keyword` — searches translatable fields

### Sorting
```
?sort[name]=asc
?sort[created_at]=desc
?sort[order]=asc
```

### Paginate toggle
`?paginate=false` — returns all records without pagination (use carefully)

### Standard Response Shape
```json
{
  "status": "success",
  "message": "Data retrieved successfully",
  "data": [...],
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." },
  "meta": { "current_page": 1, "last_page": 5, "per_page": 15, "total": 72 }
}
```

### Error Response Shape
```json
{
  "status": "fail",
  "message": "The name field is required.",
  "data": {
    "ar.name": ["The name field is required."]
  }
}
```


---

## Service Orders

> Admin can view all service orders system-wide and manage Golden Guarantee complaints.
>
> 📘 Full lifecycle details: [Service Order System](./SERVICE_ORDER_SYSTEM.md)

### List & Show

| Method | Endpoint | Description |
|---|---|---|
| GET | `/service-orders` | List all orders (paginated) |
| GET | `/service-orders/{order}` | Full order detail |

#### Filters
```
?status=pending|accepted|active|provider_finished|pending_admin_review|completed|cancelled|rejected
?service_id=3
?client_id=10
?provider_id=42
?search=NK-S0042       # Search by order_number
?date_from=2026-09-01
?date_to=2026-09-30
?per_page=15
```

#### Show Response Sections
| Section | Description |
|---|---|
| Identity | `id`, `order_number` (`NK-SXXXX#`), `status`, `status_trans` |
| Schedule | `scheduled_date`, `scheduled_time` |
| Form Data | `form_data[]` |
| Pricing | `service_price`, `discount_amount`, `vat_percentage`, `vat_amount`, `app_fee_percentage`, `app_fee_amount`, `total_price`, `broker_commission_amount`, `broker_code_used` |
| Payment | `payment_method`, `paid_at` |
| Timeline | All lifecycle timestamps (created, accepted, started, provider_finished, completed, cancelled, rejected) |
| Latest Delivery | `completion_note`, `provider_finished_at`, `work_files[]` |
| Revision History | `total_rounds`, `max_allowed`, `rounds[]` (each round: client note, timestamps, provider response) |
| Golden Guarantee | `is_submitted`, `reason`, `description`, `filed_at`, `redo_count`, `attachments[]` |
| Admin Decision | `decision` (`approved`/`rejected`/`null`), `notes`, `reviewed_at`, `reviewer` |
| Client Review | `rating`, `comment`, `sub_ratings`, `submitted_at` |
| Parties | `service_data`, `client_data` (with email, phone), `provider_data` (with type, avg_rating) |

---

## Golden Guarantee Management

> Queue of service orders where the client filed a complaint after the provider finished.
>
> **APPROVE** → order goes back to `active`, provider must redo work (`redo_count` increments)  
> **REJECT** → order becomes `completed`, provider gets paid, admin note shown to client (required)

| Method | Endpoint | Description |
|---|---|---|
| GET | `/golden-guarantee` | List pending complaints |
| GET | `/golden-guarantee/{order}` | Full complaint detail |
| POST | `/golden-guarantee/{order}/approve` | Approve — provider redoes work |
| POST | `/golden-guarantee/{order}/reject` | Reject — order completed, provider paid |

#### List Filters
```
?service_id=3
?provider_id=42
?client_id=10
?date_from=2026-09-01    # Filters on golden_guarantee_filed_at
?date_to=2026-09-30
?per_page=15
```

#### Approve Payload
```json
{
  "notes": "Client complaint is valid. Provider must revise section 3."
}
```

#### Reject Payload (notes required)
```json
{
  "notes": "The delivered work meets the agreed specifications. No redo required."
}
```

> **Note:** `notes` is **required** when rejecting — the client will see this explanation.

---

## Urgent Orders

| Method | Endpoint | Description |
|---|---|---|
| GET | `/urgent-orders` | List all urgent orders (paginated) |
| GET | `/urgent-orders/{order}` | Full urgent order detail |

#### Filters
```
?status=searching_for_provider|accepted|completed|cancelled
?service_id=3
?client_id=10
?provider_id=42
?search=NK-U0015       # Search by order_number
?date_from=2026-09-01
?date_to=2026-09-30
?per_page=15
```

#### Show Response Sections
| Section | Description |
|---|---|
| Identity | `id`, `uuid`, `order_number` (`NK-UXXXX#`), `status` |
| Location | `lat`, `lng` |
| Pricing | `total_price`, `provider_amount`, `vat_amount` |
| Timeline | `paid_at`, `arrived_at`, `completed_at`, `cancelled_at`, `created_at` |
| Parties | `service_data`, `client_data`, `provider_data` |
| Emergency | `emergency_authority`, `emergency_case_type` |

#### Agency & Najz Steps Update
| Method | Endpoint | Description |
|---|---|---|
| GET | `/urgent-orders/{order}/agency` | Show agency info and index |
| PUT | `/urgent-orders/{order}/agency` | Update agency info |
| PUT | `/urgent-orders/{order}/najz-steps` | Update najz steps |

---

## Order Reports

> Clients can report problems with both service and urgent orders (fraud, no-show, wrong service, harassment, other). Admin reviews and resolves each report.

| Method | Endpoint | Description |
|---|---|---|
| GET | `/order-reports` | List all reports (paginated) |
| GET | `/order-reports/{report}` | Full report detail |
| PATCH | `/order-reports/{report}/review` | Mark as reviewed or dismissed |

#### List Filters
```
?status=pending|reviewed|dismissed
?reason=no_show|fraud|wrong_service|harassment_or_abuse|other
?order_type=ServiceOrder|UrgentOrder
?reporter_id=10
?date_from=2026-09-01
?date_to=2026-09-30
?per_page=15
```

#### Review Payload
```json
{
  "status": "reviewed",
  "admin_note": "Provider account has been warned. Refund issued to client."
}
```

| `status` | Meaning |
|---|---|
| `reviewed` | Admin acknowledged, took action |
| `dismissed` | Admin reviewed, no action needed |

#### Report Response
```json
{
  "id": 1,
  "reason": "no_show",
  "description": "Provider accepted but never showed up.",
  "status": "pending",
  "order_type": "ServiceOrder",
  "order_id": 42,
  "order_number": "NK-S0042#",
  "reporter": { "id": 10, "full_name": "Ahmed Ali", "phone": "...", "email": "...", "avatar": "..." },
  "admin_note": null,
  "reviewed_at": null,
  "reviewed_by": null,
  "created_at": 1725379200
}
```

---

## Wallet — Transactions

> System-wide transaction history across all users (providers and clients).

| Method | Endpoint | Description |
|---|---|---|
| GET | `/wallet/transactions` | All transactions, filterable |

#### Filters
```
?user_id=42          # Scope to one user (provider or client)
?type=deposit|withdrawal|withdraw_request
?status=completed|pending|failed
?date_from=2026-09-01
?date_to=2026-09-30
?per_page=15
```

---

## Withdrawal Requests

> Both providers and clients can submit withdrawal requests. Providers have a saved bank account; clients enter their bank details each time. All requests land in the same queue.

| Method | Endpoint | Description |
|---|---|---|
| GET | `/withdrawals` | List all requests (paginated) |
| GET | `/withdrawals/{withdrawal}` | Show single request |
| PATCH | `/withdrawals/{withdrawal}/approve` | Approve with optional note |
| PATCH | `/withdrawals/{withdrawal}/reject` | Reject with required note |

#### List Filters
```
?status=pending|approved|rejected
?user_id=42
?user_type=provider|client
?date_from=2026-09-01
?date_to=2026-09-30
?per_page=15
```

#### Approve Payload
```json
{
  "admin_note": "Transferred via bank on 2026-09-15."
}
```

#### Reject Payload
```json
{
  "admin_note": "IBAN verification failed. Please resubmit with correct IBAN."
}
```

#### Withdrawal Response
```json
{
  "id": 1,
  "amount": 500.00,
  "status": "pending",
  "bank_account_data": {
    "bank_name": "Al Rajhi Bank",
    "account_name": "Ahmed Ali",
    "account_number": "1234567890",
    "iban": "SA0380000000608010167519"
  },
  "admin_note": null,
  "approved_at": null,
  "rejected_at": null,
  "created_at": 1725379200,
  "user_data": {
    "id": 42,
    "full_name": "Ahmed Ali",
    "user_type": "provider"
  }
}
```

---

## Chats

| Method | Endpoint | Description |
|---|---|---|
| GET | `/chats` | List all chats (paginated) |
| GET | `/chats/{chat}` | Show single chat with messages |

---

## Provider Reviews

| Method | Endpoint | Description |
|---|---|---|
| GET | `/provider-reviews` | List all provider reviews |
| GET | `/provider-reviews/{review}` | Show single review |
| DELETE | `/provider-reviews/{review}` | Delete a review |

---

## Admins

| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | `/admins` | List admins (paginated) | `admins.index` |
| GET | `/admins/{id}` | Show single admin | `admins.show` |
| POST | `/admins` | Create admin | `admins.store` |
| PUT | `/admins/{id}` | Update admin | `admins.update` |
| DELETE | `/admins/{id}` | Delete admin | `admins.destroy` |
| POST | `/admins/{id}/status` | Toggle status | `admins.update.status` |
