# Provider Registration Cycle — Dashboard API Flow

This document outlines the complete 5-phase registration cycle for a **Provider Dashboard** (web/mobile dashboard, not an end-user application). It details the strictly enforced business rules, expected payloads, and API responses at every step.

> **Context:** All endpoints prefixed `/api/provider/...` are consumed by the **Provider Dashboard**, not a client-facing app. Providers are professionals (e.g. lawyers) who go through a gated onboarding flow before they can offer services.

---

## 🚦 Resume / Status Check

> **Is this endpoint required, or can we rely on the step responses?**
>
> **Yes — this endpoint is required and cannot be replaced by step responses.** Here's why:
>
> | Situation | Step response covers it? | Status endpoint needed? |
> |---|---|---|
> | Provider closes and reopens the dashboard (cold start) | ❌ No step was just submitted | ✅ Yes — only way to know where they left off |
> | Provider is in Phase 3/4 (those return `null` data) | ❌ Phases 3 & 4 return `null` | ✅ Yes — only way to know phase |
> | Provider is blocked by exam cooldown | ❌ Not in step responses | ✅ Yes — returns `exam_status.retake_after` |
> | Provider's interview status / type | ❌ Not in step responses | ✅ Yes — returns `interview_type`, `interview_status` |
> | Provider has been accepted/rejected | ❌ Not surfaced by steps | ✅ Yes — returns `account_status` |
>
> **Bottom line:** The step responses only tell you the result of the step just submitted. The status endpoint is the app's **boot-time router** — always call it first when the provider dashboard loads.

At any time, the frontend can check where the provider left off by hitting the status endpoint.

**Endpoint:** `POST /api/provider/register/status`
**Payload:** `phone_code`, `phone`
**Response:**
```json
{
    "status": "success",
    "message": "Registration status retrieved.",
    "data": {
        "id": 12,
        "full_name": "John Doe",
        "email": "john@example.com",
        "phone_code": "+966",
        "phone": "512345678",
        "user_type": "provider",
        "avatar": null,
        "national_id_type": "national_id",
        "national_id": "1234567890",
        "gender": "male",
        "nationality": "saudi",
        "professional_license_number": "LIC-2024-001",
        "license_expiration_date": "2028-01-01",
        "licensing_authority": "Ministry of Justice",
        "degree_specialty": "law",
        "degree_grade": "excellent",
        "gpa_or_percentage": "3.9",
        "years_of_experience": 5,
        "specialty_experience_years": 3,
        "has_criminal_record": false,
        "address": "123 Main Street",
        "lat": "24.7136",
        "lng": "46.6753",
        "brief": "Experienced contract lawyer.",
        "can_perform_urgent_order": false,
        "provider_type_data": {"id": 1, "name": "Lawyer"},
        "category_data": {"id": 2, "name": "Legal"},
        "subcategory_data": {"id": 5, "name": "Contract Law"},
        "city_data": {"id": 1, "name": "Riyadh"},
        "district_data": {"id": 3, "name": "Al Olaya"},
        "services_data": [],
        "national_id_doc": null,
        "professional_license_doc": null,
        "university_degree_doc": null,
        "wages_certificate": [],
        "criminal_record_certificate": null,
        "cv": null,
        "intro_video": null,
        "other_licenses": [],
        "current_registration_phase": "phase_2_exam",
        "current_registration_step": 1,
        "interview_type": null,
        "interview_status": null,
        "account_status": "unverified",
        "phone_verified": true,
        "exam_status": {
            "attempts_used": 1,
            "attempts_remaining": 2,
            "can_retake": false,
            "retake_after": "2026-10-08"
        }
    }
}
```
> **Note:** `exam_status` is only present when `current_registration_phase` is `phase_2_exam`. For all other phases it is omitted.
> The routing keys (`current_registration_phase`, `account_status`, etc.) always overwrite any duplicate keys from the provider profile.

### 📍 Phase & Status Routing Reference

Use `current_registration_phase` as the **primary router** when the dashboard loads. Use `account_status` for the final outcome states.

#### `current_registration_phase` values

| Value | What it means | Dashboard should show |
|---|---|---|
| `phase_1_application` | Provider is filling out the registration form | Registration form — resume at `current_registration_step` |
| `phase_2_exam` | Provider passed Phase 1 and must complete the exam | Exam screen — check `exam_status` for cooldown/blocking |
| `phase_3_video` | Provider passed the exam and must upload CV & intro video | Video/CV upload screen |
| `phase_4_interview` | Provider uploaded CV/video and must select interview type | Interview selection screen |
| `phase_5_review` | Provider selected interview type — awaiting admin decision | "Under review" waiting screen |
| `completed` | Provider has been fully accepted and activated | Dashboard home / provider portal |

#### `current_registration_step` values (only relevant in `phase_1_application`)

| Value | What it means |
|---|---|
| `1` | Step 1 done — resume at Step 2 (Professional Details) |
| `2` | Step 2 done — resume at Step 3 (Location & Services) |
| `3` | Step 3 done — resume at Step 4 (Legal Documents) |
| `4` | Step 4 done — provider moved to `phase_2_exam` |

> **⚠️ OTP screen routing (cold start):** When `current_registration_phase` is `phase_1_application` and `current_registration_step` is `1`, check `phone_verified` to distinguish between two states:
> - `phone_verified: false` → provider has not yet verified their OTP — show the OTP verification screen
> - `phone_verified: true` → OTP done, proceed to Step 2

#### `phone_verified` field

A boolean derived from `phone_verified_at`. Present in all registration responses and the status endpoint.

| Value | Meaning |
|---|---|
| `false` | Phone OTP not yet confirmed — show OTP screen |
| `true` | Phone confirmed — OTP screen can be skipped |

#### `account_status` values

| Value | What it means | Dashboard should show |
|---|---|---|
| `unverified` | Phone OTP not yet verified | OTP verification screen |
| `pending` | Application submitted, waiting for admin decision | "Under review" screen |
| `accepted` | Admin accepted the provider | Full dashboard access |
| `rejected` | Admin rejected the application | Rejection message + reapply cooldown info |

#### `interview_type` values

> Present from `phase_4_interview` onwards. `null` in all earlier phases.

| Value | Phase it appears in | Meaning |
|---|---|---|
| `null` | `phase_1_application`, `phase_2_exam`, `phase_3_video` | Interview not yet selected |
| `in_person` | `phase_4_interview`, `phase_5_review`, `completed` | Provider chose an in-person interview |
| `online` | `phase_4_interview`, `phase_5_review`, `completed` | Provider chose an online/video interview |

#### `interview_status` values

> Present from `phase_4_interview` onwards. `null` in all earlier phases.

| Value | Phase it appears in | Meaning |
|---|---|---|
| `null` | `phase_1_application` → `phase_3_video` | No interview activity yet |
| `pending` | `phase_4_interview`, `phase_5_review` | Interview type selected, admin hasn't scheduled it yet |
| `scheduled` | `phase_5_review` | Admin has scheduled the interview date/time |
| `completed` | `phase_5_review`, `completed` | Interview was conducted |

---

## 🟢 Phase 1: Application (4 Steps)

This phase collects all personal, professional, and legal data required for background checks.

### Step 1: Personal Info & Account Creation
**Endpoint:** `POST /api/provider/register/step-1`
- **Fields:** `full_name`, `email`, `phone_code`, `phone`, `password`, `password_confirmation`, `national_id_type`, `national_id`, `gender`, `nationality`.
- **Strict Validations (Enums):**
  - `national_id_type` (must be `national_id`, `iqama`, or `passport`)
  - `gender` (must be `male` or `female`)
  - `nationality` (must be `saudi` or `resident`)
- **Optional Field:** `avatar` (Media ID, optional in registration/creation/update).
- **Terms & Privacy Agreement (Frontend-Only):** Display a checkbox on this screen requiring the provider to agree to the Terms & Conditions and Privacy Policy before submitting. The backend does **not** validate this field — enforcement is entirely on the frontend. Use `GET /api/general/pages/terms` and `GET /api/general/pages/privacy` to fetch the page content for the links.
- **Note:** Creates an unverified user. **An OTP is automatically sent immediately after this step.** Triggers a 403 if they are already accepted.
**Response:**
```json
{
    "status": "success",
    "message": "Step 1 completed successfully.",
    "data": {
        "id": 12,
        "full_name": "John Doe",
        "email": "john@example.com",
        "phone_code": "+966",
        "phone": "512345678",
        "user_type": "provider",
        "account_status": "unverified",
        "phone_verified": false,
        "avatar": null,
        "current_registration_phase": "phase_1_application",
        "current_registration_step": 1,
        "national_id_type": "national_id",
        "national_id": "1234567890",
        "gender": "male",
        "nationality": "saudi",
        "professional_license_number": null,
        "license_expiration_date": null,
        "licensing_authority": null,
        "degree_specialty": null,
        "degree_grade": null,
        "gpa_or_percentage": null,
        "years_of_experience": null,
        "specialty_experience_years": null,
        "has_criminal_record": null,
        "address": null,
        "lat": null,
        "lng": null,
        "brief": null,
        "can_perform_urgent_order": false,
        "provider_type_data": null,
        "category_data": null,
        "subcategory_data": null,
        "city_data": null,
        "district_data": null,
        "services_data": [],
        "national_id_doc": null,
        "professional_license_doc": null,
        "university_degree_doc": null,
        "wages_certificate": [],
        "criminal_record_certificate": null,
        "cv": null,
        "intro_video": null,
        "other_licenses": []
    }
}
```

### Step 2: Professional Details
**Endpoint:** `POST /api/provider/register/step-2`
- **Fields:** `provider_type_id`, `category_id`, `subcategory_id`, `services`, `professional_license_number`, `license_expiration_date`, `licensing_authority`, `degree_specialty`, `degree_grade`, `gpa_or_percentage`, `years_of_experience`, `specialty_experience_years`, `has_criminal_record`, `brief`.
- **Strict Validations (Enums):**
  - `degree_specialty` (must be `law` or `sharia`)
  - `degree_grade` (must be `excellent` or `very_good`)
  - `has_criminal_record` (must be boolean)
**Response:**
```json
{
    "status": "success",
    "message": "Step 2 completed successfully.",
    "data": {
        "id": 12,
        "full_name": "John Doe",
        "email": "john@example.com",
        "phone_code": "+966",
        "phone": "512345678",
        "user_type": "provider",
        "account_status": "unverified",
        "phone_verified": true,
        "avatar": null,
        "current_registration_phase": "phase_1_application",
        "current_registration_step": 2,
        "national_id_type": "national_id",
        "national_id": "1234567890",
        "gender": "male",
        "nationality": "saudi",
        "professional_license_number": "LIC-2024-001",
        "license_expiration_date": "2028-01-01",
        "licensing_authority": "Ministry of Justice",
        "degree_specialty": "law",
        "degree_grade": "excellent",
        "gpa_or_percentage": "3.9",
        "years_of_experience": 5,
        "specialty_experience_years": 3,
        "has_criminal_record": false,
        "address": null,
        "lat": null,
        "lng": null,
        "brief": null,
        "can_perform_urgent_order": false,
        "provider_type_data": {"id": 1, "name": "Lawyer"},
        "category_data": {"id": 2, "name": "Legal"},
        "subcategory_data": {"id": 5, "name": "Contract Law"},
        "city_data": null,
        "district_data": null,
        "services_data": [
            {"id": 10, "name": "Contract Drafting"}
        ],
        "national_id_doc": null,
        "professional_license_doc": null,
        "university_degree_doc": null,
        "wages_certificate": [],
        "criminal_record_certificate": null,
        "cv": null,
        "intro_video": null,
        "other_licenses": []
    }
}
```

### Step 3: Location
**Endpoint:** `POST /api/provider/register/step-3`
- **Fields:** `city_id`, `district_id`, `lat`, `lng`, `address`.
**Response:**
```json
{
    "status": "success",
    "message": "Step 3 completed successfully.",
    "data": {
        "id": 12,
        "full_name": "John Doe",
        "email": "john@example.com",
        "phone_code": "+966",
        "phone": "512345678",
        "user_type": "provider",
        "account_status": "unverified",
        "phone_verified": true,
        "avatar": null,
        "current_registration_phase": "phase_1_application",
        "current_registration_step": 3,
        "national_id_type": "national_id",
        "national_id": "1234567890",
        "gender": "male",
        "nationality": "saudi",
        "professional_license_number": "LIC-2024-001",
        "license_expiration_date": "2028-01-01",
        "licensing_authority": "Ministry of Justice",
        "degree_specialty": "law",
        "degree_grade": "excellent",
        "gpa_or_percentage": "3.9",
        "years_of_experience": 5,
        "specialty_experience_years": 3,
        "has_criminal_record": false,
        "address": "123 Main Street",
        "lat": "24.7136",
        "lng": "46.6753",
        "brief": "Experienced contract lawyer with 5 years in corporate law.",
        "can_perform_urgent_order": false,
        "provider_type_data": {"id": 1, "name": "Lawyer"},
        "category_data": {"id": 2, "name": "Legal"},
        "subcategory_data": {"id": 5, "name": "Contract Law"},
        "city_data": {"id": 1, "name": "Riyadh"},
        "district_data": {"id": 3, "name": "Al Olaya"},
        "services_data": [
            {"id": 10, "name": "Contract Drafting"},
            {"id": 11, "name": "Legal Consultation"}
        ],
        "national_id_doc": null,
        "professional_license_doc": null,
        "university_degree_doc": null,
        "wages_certificate": [],
        "criminal_record_certificate": null,
        "cv": null,
        "intro_video": null,
        "other_licenses": []
    }
}
```

### Step 4: Legal Documents
**Endpoint:** `POST /api/provider/register/step-4`
- **Fields:** `national_id_doc`, `professional_license_doc`, `university_degree_doc`, `criminal_record_certificate`.
- **Array Documents:** `wages_certificate` (array of IDs) and `other_licenses` (array of IDs).
- **Note:** Successfully completing this step automatically advances the user to `phase_2_exam`.
**Response:**
```json
{
    "status": "success",
    "message": "Step 4 attachments uploaded successfully.",
    "data": {
        "id": 12,
        "full_name": "John Doe",
        "email": "john@example.com",
        "phone_code": "+966",
        "phone": "512345678",
        "user_type": "provider",
        "account_status": "unverified",
        "avatar": null,
        "current_registration_phase": "phase_2_exam",
        "current_registration_step": 4,
        "national_id_type": "national",
        "national_id": "1234567890",
        "gender": "male",
        "nationality": "SA",
        "professional_license_number": "LIC-2024-001",
        "license_expiration_date": "2028-01-01",
        "licensing_authority": "Ministry of Justice",
        "degree_specialty": "law",
        "degree_grade": "excellent",
        "gpa_or_percentage": "3.9",
        "years_of_experience": 5,
        "specialty_experience_years": 3,
        "has_criminal_record": false,
        "address": "123 Main Street",
        "lat": "24.7136",
        "lng": "46.6753",
        "brief": "Experienced contract lawyer with 5 years in corporate law.",
        "can_perform_urgent_order": false,
        "provider_type_data": {"id": 1, "name": "Lawyer"},
        "category_data": {"id": 2, "name": "Legal"},
        "subcategory_data": {"id": 5, "name": "Contract Law"},
        "city_data": {"id": 1, "name": "Riyadh"},
        "district_data": {"id": 3, "name": "Al Olaya"},
        "services_data": [
            {"id": 10, "name": "Contract Drafting"}
        ],
        "national_id_doc": {"id": 20, "url": "https://..."},
        "professional_license_doc": {"id": 21, "url": "https://..."},
        "university_degree_doc": {"id": 22, "url": "https://..."},
        "wages_certificate": [
            {"id": 23, "url": "https://..."}
        ],
        "criminal_record_certificate": {"id": 24, "url": "https://..."},
        "cv": null,
        "intro_video": null,
        "other_licenses": null
    }
}
```
> **🔑 Key Transition:** Notice `current_registration_phase` changes from `phase_1_application` → **`phase_2_exam`** in the Step 4 response. This signals the frontend to navigate the provider to the exam screen.
> **Note:** The provider account is NOT marked as `pending` yet, and the admin is not notified. They remain `unverified` until they finish the interview booking in Phase 4.

### 📂 How to Upload Media (Files / Images / Videos)
For all endpoints that require a file (like `avatar`, `cv`, `intro_video`, `national_id_doc`, `wages_certificate`, etc.), you must first upload the file using the general attachments endpoint:

**Endpoint:** `POST /api/general/attachments`
**Payload (multipart/form-data):**
- `model`: `User` (Always use `User` for provider documents)
- `media_key`: The exact name of the document field (e.g., `national_id_doc`, `cv`, `intro_video`, `wages_certificate`)
- `file`: The actual file

**Usage:** The attachments endpoint will return an `id` (or array of IDs). You will send this integer `id` (e.g., `"national_id_doc": 12`) in your registration/update payloads, or an array of integers for multiple files (e.g., `"wages_certificate": [13, 14]`).

---

## 🟡 Phase 2: Examination

The provider must pass a category-specific exam within a time limit. The exact rules (`passing_score`, `duration_minutes`, `exam_max_attempts`, `exam_cooldown_days`) are **per-category** and configured by the admin when creating/editing the category — not global settings.

> **⚠️ Important:** All exam rules are scoped per-category. Different specialties can have different passing scores, time limits, and attempt limits.

1. **Start Exam:** `POST /api/provider/register/exam/start`
   - **Payload:** `phone_code`, `phone`
   - **Response:**
   ```json
   {
       "status": "success",
       "message": "Exam started successfully.",
       "data": {
           "total_questions": 20,
           "duration_minutes": 10,
           "started_at": 1727175600,
           "attempts_allowed": 3,
           "attempts_used": 1,
           "questions": [
               {
                   "id": 1,
                   "question": "Sample Question?",
                   "options": [
                       {"id": 1, "answer": "Option A"},
                       {"id": 2, "answer": "Option B"},
                       {"id": 3, "answer": "Option C"},
                       {"id": 4, "answer": "Option D"}
                   ]
               }
           ]
       }
   }
   ```
   > **Note:** `started_at` is a **Unix timestamp** (integer). The frontend should use this alongside `duration_minutes` to run a countdown timer. The backend also validates the time server-side with a 2-minute grace period.

2. **Submit Exam:** `POST /api/provider/register/exam/submit`
   - **Payload:**
   ```json
   {
       "phone_code": "+966",
       "phone": "512345678",
       "answers": [
           {"question_id": 1, "selected_option_id": 4},
           {"question_id": 2, "selected_option_id": 7}
       ]
   }
   ```
   - **Note:** If passed, the user is automatically advanced to `phase_3_video`.
   - **Response (passed):**
   ```json
   {
       "status": "success",
       "message": "Congratulations, you passed the exam!",
       "data": {
           "score": 19,
           "total_questions": 20,
           "passing_score": 18,
           "passed": true,
           "duration_minutes": 10,
           "attempts_allowed": 3,
           "attempts_used": 1,
           "attempts_remaining": 2,
           "can_retake": false,
           "retake_after": null
       }
   }
   ```
   - **Response (failed):**
   ```json
   {
       "status": "success",
       "message": "Unfortunately, you did not pass the exam.",
       "data": {
           "score": 14,
           "total_questions": 20,
           "passing_score": 18,
           "passed": false,
           "duration_minutes": 10,
           "attempts_allowed": 3,
           "attempts_used": 2,
           "attempts_remaining": 1,
           "can_retake": true,
           "retake_after": "2026-10-08"
       }
   }
   ```
   > **Field Guide:**
   > - `passing_score`: Minimum correct answers needed (set per-category by admin).
   > - `attempts_allowed`: Total allowed attempts for this category.
   > - `attempts_used`: How many attempts this provider has consumed so far.
   > - `attempts_remaining`: Remaining retries before they are permanently blocked.
   > - `can_retake`: `true` only if they failed AND still have remaining attempts.
   > - `retake_after`: The earliest date they can retake (ISO date string), `null` if they passed or exhausted all attempts.

---

## 🟠 Phase 3: Personal Marketing (Video & CV)

After proving technical competence, the provider must upload their CV and an introductory video.

**Endpoint:** `POST /api/provider/register/phase-3`
- **Fields:** `cv` (PDF), `intro_video` (MP4, 1-2 mins).
- **Note:** Successfully completing this advances the user to `phase_4_interview`.
**Response:**
```json
{
    "status": "success",
    "message": "Video and CV uploaded successfully.",
    "data": null
}
```

---

## 🔵 Phase 4: Interview Booking

The provider chooses how they want to conduct the final interview with the administration.

**Endpoint:** `POST /api/provider/register/phase-4`
- **Fields:** `interview_type` (`online` or `in_person`).
- **Frontend Instructions (UI Text):** 
  - If the user selects `in_person` (Offline), display a message: *"We will contact you shortly with the interview details and location."* (سيتم التواصل معك لمعرفة التفاصيل).
  - If the user selects `online`, display a message: *"The meeting link will be sent to your registered email."* (سيتم إرسال رابط الاجتماع على بريدك الإلكتروني).
- **Note:** This sets their phase to `phase_5_review`. This is the final step of the registration journey. **The account status now becomes `pending` and the Admin is finally notified via the `ProviderRegistered` event.**
**Response:**
```json
{
    "status": "success",
    "message": "Interview type selected. Your request is now under review.",
    "data": null
}
```

---

## 🟣 Phase 5: Admin Review & Decision

The provider's dashboard will now show a "Pending Review" screen. 

1. **Admin Review:** The admin sees the full profile, documents, and exam results (e.g. passed on attempt 2, score 19/20) in the dashboard.
2. **Acceptance:** If accepted, the provider's `account_status` becomes `accepted`. They can now log in, configure their schedule, and set exact prices for their approved services within the admin's `price_from` and `price_to` bounds.
3. **Rejection:** If rejected, a cooldown timer (default: 3 months) starts. During this time, they cannot reapply. Once it expires, they can start the cycle over from Step 1.

---

## ⚙️ Relevant Settings & Configurations
The frontend should be aware of these configuration keys to handle UI rules correctly:

1. **Category Exam Settings (Returned in Category object):**
   - `exam_passing_score`: Minimum score to pass (e.g., 18).
   - `exam_time_minutes`: Time limit for the exam (e.g., 10 minutes).
   - `exam_max_attempts`: Max allowed attempts (e.g., 3).
   - `exam_cooldown_days`: Days to wait before retrying if failed (e.g., 14 days).

2. **Global Settings (`GET /api/general/settings`):**
   - `provider_rejection_cooldown_months`: How many months a rejected provider is blocked from reapplying (default: 3).

---

## 📝 Provider Profile Update
Once accepted, a provider can update their profile. The following fields are allowed to be updated. All fields are **optional** (`sometimes`).

**Endpoint:** `POST /api/provider/profile` (Using `POST` with `_method=PUT` if uploading form-data, or just `PUT` with JSON, depending on frontend setup).

**Allowed Fields:**
- **Basic Info:** `full_name`, `locale` (`ar`/`en`), `allow_notifications` (boolean), `is_available` (boolean).
- **Location & Bio:** `brief`, `address`, `lat`, `lng`, `city_id`, `district_id`.
- **Media IDs (Integers):** `avatar`, `cv`, `intro_video`.
- **Media Arrays (Array of IDs):** `other_licenses`.

*Note: Core identity fields (National ID, License Number, Degree) are **locked** after registration and cannot be modified by the provider.*

---

## 💼 Service Orders (Integrated Cases)
When fetching a service order (`GET /api/provider/services/orders/{id}` or the client equivalent), you will see two very important financial keys if the order `is_integrated_case` is `true`.

### 1. `completion_amount` (إجمالي المؤخر)
**What it represents:** This is the gross backend payment (المؤخر) that the Provider requested and was ruled/judged (المبلغ اللي اتحكم بيه). 
- **Client Side:** This is the total amount the client is required to pay when the integrated case finishes.
- **Example:** `5000.00`

### 2. `calculated_provider_payment` (المؤخر الصافي / المستحق للمحامي)
**What it represents:** This is the exact net value of the المؤخر that the Provider will receive in their wallet, *after* their agreed-upon percentage has been applied (الرقم اللي هياخده البروفايدر بناءً على نسبته).
- **Provider Side:** This provides total transparency so the lawyer knows exactly how much cash they will earn from the completion payment.
- **Example:** If `completion_amount` is 5000 and the provider's share is 80%, this key will return `4000.00`.

### 3. `has_work_files` (مرفقات الإنجاز)
**What it represents:** A boolean flag (`true`/`false`) returned in both the list and show endpoints for provider orders. It checks if the provider has uploaded at least one work file (المخرجات) to the order.
- **Frontend Usage:** Use this flag to conditionally show or enable the "Mark as Finished" (إنهاء الطلب) button. The provider cannot complete an order until they upload at least one work file.