# Technician

Routes that **require Technician authentication** (`user_type: technician`) and are specific to the Technician role: session management, profile/availability, assigned-order lifecycle (accept/reject → status updates → completion), and earnings/wallet/withdrawal. Support conversations and FAQs/pages live in `06-shared-general.md` (identical routes/shape as Client/Company); media upload is the public `client.upload` endpoint, also in `06-shared-general.md`, since technicians use it pre-authentication during registration.

**18 endpoints in this section.**

## 01. Authentication (Session)

#### Logout

**What this does:** Invalidates the current JWT (blacklists it) and, if `device_id` is supplied, removes that device's push-notification registration.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/auth/logout` |
| **Route name** | `technician.auth.logout` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
Send as `application/json`.

**Field documentation:**
### `device_id`
- **Description:** Device ID
- **Type:** `integer`
- **Required:** optional
- **Example:** `device-uuid-123`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "device_id": "device-uuid-123"
}
```

**Actual Success Response(s):**

*{ 200 OK — logged out }*
```json
{
    "status": "success",
    "message": "Logged out successfully.",
    "data": null
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** The token is no longer usable — call login again to obtain a new one.

---

## 02. Profile

#### Update Profile

**What this does:** Updates the technician's own editable profile fields (specialty, coverage areas, capacity, etc.).

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/technician/profile` |
| **Route name** | `technician.profile.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
Send as `application/json`.

**Field documentation:**
### `name`
- **Description:** Name
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:160`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:160` — must not exceed 160.
### `email`
- **Description:** Email
- **Type:** `string`
- **Required:** optional
- **Example:** `user@example.com`
- **Validation rules:** `sometimes|email|unique:users,email,1`
- `sometimes` — validated only when the field is present.
- `email` — must be a valid email address.
- `unique:users,email,1` — must be unique (users, email, 1).
### `phone`
- **Description:** Phone
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `sometimes|string|max:20|unique:users,phone,1`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:20` — must not exceed 20.
- `unique:users,phone,1` — must be unique (users, phone, 1).
### `full_name`
- **Description:** Full Name
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:150`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:150` — must not exceed 150.
### `id_number`
- **Description:** ID Number
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `sometimes|nullable|string|max:50`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `string` — must be text.
- `max:50` — must not exceed 50.
### `address`
- **Description:** Address
- **Type:** `string`
- **Required:** optional
- **Example:** `123 Main Street, Building A`
- **Validation rules:** `sometimes|nullable|string|max:255`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `country_id`
- **Description:** Country
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|nullable|integer|exists:countries,id`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `integer` — must be a whole number.
- `exists:countries,id` — must reference an existing record (countries, id).
### `governorate_id`
- **Description:** City
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|nullable|integer|exists:governorates,id`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `integer` — must be a whole number.
- `exists:governorates,id` — must reference an existing record (governorates, id).
### `region_id`
- **Description:** District
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|nullable|integer|exists:regions,id`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `integer` — must be a whole number.
- `exists:regions,id` — must reference an existing record (regions, id).
### `category_id`
- **Description:** Service Category
- **Type:** `integer`
- **Required:** optional
- **Example:** `2`
- **Validation rules:** `sometimes|integer|exists:categories,id`
- `sometimes` — validated only when the field is present.
- `integer` — must be a whole number.
- `exists:categories,id` — must reference an existing record (categories, id).
### `coverage_areas`
- **Description:** Coverage Areas (Regions)
- **Type:** `array`
- **Required:** optional
- **Example:** `[1]`
- **Validation rules:** `sometimes|nullable|array`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `array` — must be an array.
- Distinct from `region_id` (in the profile's home-address geo-hierarchy, `country_id` → `governorate_id` → `region_id`): `coverage_areas` is a separate list of regions the technician is willing to travel to for job assignment, not where they live.
### `coverage_areas.*`
- **Description:** Coverage areas item
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `integer|exists:regions,id`
- `integer` — must be a whole number.
- `exists:regions,id` — must reference an existing record (regions, id).
### `years_experience`
- **Description:** Years of Experience
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|nullable|integer|min:0|max:50`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `integer` — must be a whole number.
- `min:0` — must be at least .
- `max:50` — must not exceed 50.
### `daily_order_capacity`
- **Description:** Daily Order Capacity
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|nullable|integer|min:1|max:50`
- `sometimes` — validated only when the field is present.
- `nullable` — may be null.
- `integer` — must be a whole number.
- `min:1` — must be at least 1.
- `max:50` — must not exceed 50.

**Example Request Body:**
```json
{
    "full_name": "Updated Name",
    "years_experience": 8
}
```

**Actual Success Response(s):**

*{ 200 OK — update full_name and years_experience }*
```json
{
    "status": "success",
    "message": "Profile updated successfully",
    "data": {
        "id": 213,
        "name": "Jared Becker",
        "email": "drew03@example.net",
        "phone": "1084266685",
        "username": null,
        "user_type": "technician",
        "status": "active",
        "email_verified_at": "2026-08-20T11:20:52.000000Z",
        "phone_verified_at": null,
        "locale": "ar",
        "allow_notifications": true,
        "timezone": "Asia/Riyadh",
        "avatar_path": null,
        "last_login_at": null,
        "suspended_at": null,
        "suspension_reason": null,
        "created_at": "2026-08-20T11:20:52.000000Z",
        "updated_at": "2026-08-20T11:20:52.000000Z",
        "deleted_at": null
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - invalid email }*
```json
{
    "status": "fail",
    "message": "Please enter a valid email address.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Fetch `GET /technician/profile` to see the updated record.

---

#### Change Password Profile

**What this does:** Changes the account password after verifying `current_password`.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/profile/change-password` |
| **Route name** | `technician.profile.change-password` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `current_password`
- **Description:** Current password
- **Type:** `string`
- **Required:** optional
- **Example:** `OldPassword123!`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.
### `password`
- **Description:** Password
- **Type:** `string`
- **Required:** optional
- **Example:** `NewPassword123!`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.
### `password_confirmation`
- **Description:** Password confirmation
- **Type:** `string`
- **Required:** optional
- **Example:** `NewPassword123!`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "current_password": "oldpassword123",
    "password": "newpassword456",
    "password_confirmation": "newpassword456"
}
```

**Actual Success Response(s):**

*{ 200 OK — password changed }*
```json
{
    "status": "success",
    "message": "Password changed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - wrong current password }*
```json
{
    "status": "fail",
    "message": "Invalid credentials. Please check your email/phone and password.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Validation Error - confirmation mismatch }*
```json
{
    "status": "fail",
    "message": "Password confirmation does not match.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** The existing token remains valid; no re-login is required unless you also want a fresh token.

---

#### Show Profile Details

**What this does:** Returns the technician's own profile — base account fields plus role-specific profile data (and, for admin, `roles`/`permissions` when loaded).

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/profile` |
| **Route name** | `technician.profile.view` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No request body. Use the documented path and query parameters.

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Profile retrieved successfully",
    "data": {
        "user": {
            "id": 212,
            "name": "Alfred Ankunding DVM",
            "email": "hveum@example.com",
            "phone": "1019568932",
            "user_type": "technician",
            "status": "active",
            "created_at": "2026-08-20 11:20:52"
        },
        "profile": {
            "full_name": "Elliot Emard",
            "id_number": null,
            "address": null,
            "country": null,
            "governorate": null,
            "region": null,
            "category_id": 46,
            "category_name": "Plumbing",
            "coverage_areas": [],
            "years_experience": null,
            "daily_order_capacity": 5,
            "employment_type": "freelance",
            "availability_status": "available",
            "avg_rating": 0,
            "completed_orders_count": 0,
            "personal_photo": null,
            "national_id_front": null,
            "national_id_back": null,
            "cv_file": null
        }
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Use `PUT` on the same endpoint to update editable fields.

---

## 03. Availability

#### Toggle Availability Profile

**What this does:** Flips the technician's `availability_status` between available/unavailable for new assignments.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/profile/toggle-availability` |
| **Route name** | `technician.profile.toggle-availability` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `404 Not Found` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No fields. Send an empty JSON object `{}`.

**Example Request Body:**
```json
[]
```

**Actual Success Response(s):**

*{ 200 OK — toggled to unavailable }*
```json
{
    "status": "success",
    "message": "Availability status updated",
    "data": {
        "availability_status": "unavailable"
    }
}
```

*{ 200 OK — toggled back to available }*
```json
{
    "status": "success",
    "message": "Availability status updated",
    "data": {
        "availability_status": "available"
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

*{ 404 Not Found — no technician profile }*
```json
{
    "status": "fail",
    "message": "Technician profile not found",
    "data": null
}
```

**What happens next:** While unavailable, the technician is skipped by admin manual assignment and by the auto-reassignment matcher.

---

## 04. Assigned Orders & Jobs

#### List All Orders

**What this does:** Returns a paginated, filterable, searchable list of orders.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `technician.orders.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No request body. Use the documented path and query parameters.

**Query parameters:**
- `page` — Page number (supported)
- `per_page` — Items per page (max: 100) (optional)
- `search` — Search term (searches across searchable fields) (optional)
- `sort[created_at]` — Sort by field: asc or desc (optional)
- `paginate` — Set to false to disable pagination (optional)
- `filters[is_active]` — Filter by active status: 1 or 0 (optional)

**Actual Success Response(s):**

*{ 200 OK — list assigned orders }*
```json
{
    "status": "success",
    "message": "Orders retrieved successfully.",
    "data": {
        "data": [
            {
                "id": 53,
                "uuid": "02bd4c0a-8587-4608-afdc-728a63259d5c",
                "order_number": "ORD-260820-MMGY",
                "status": "accepted",
                "scheduled_date": "2026-08-23",
                "address": {
                    "id": 57,
                    "title": "Home"
                },
                "items": [
                    {
                        "service_name": "sequi consequatur consectetur",
                        "quantity": 2
                    }
                ],
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/technician/orders?page=1",
            "last": "http://localhost/api/v1/technician/orders?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
                {
                    "url": null,
                    "label": "&laquo; Previous",
                    "page": null,
                    "active": false
                },
                {
                    "url": "http://localhost/api/v1/technician/orders?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/orders",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

*{ 200 OK — scope=active }*
```json
{
    "status": "success",
    "message": "Orders retrieved successfully.",
    "data": {
        "data": [
            {
                "id": 53,
                "uuid": "02bd4c0a-8587-4608-afdc-728a63259d5c",
                "order_number": "ORD-260820-MMGY",
                "status": "accepted",
                "scheduled_date": "2026-08-23",
                "address": {
                    "id": 57,
                    "title": "Home"
                },
                "items": [
                    {
                        "service_name": "sequi consequatur consectetur",
                        "quantity": 2
                    }
                ],
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/technician/orders?page=1",
            "last": "http://localhost/api/v1/technician/orders?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
                {
                    "url": null,
                    "label": "&laquo; Previous",
                    "page": null,
                    "active": false
                },
                {
                    "url": "http://localhost/api/v1/technician/orders?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/orders",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

*{ 200 OK — scope=history (empty) }*
```json
{
    "status": "success",
    "message": "Orders retrieved successfully.",
    "data": {
        "data": [],
        "links": {
            "first": "http://localhost/api/v1/technician/orders?page=1",
            "last": "http://localhost/api/v1/technician/orders?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "from": null,
            "last_page": 1,
            "links": [
                {
                    "url": null,
                    "label": "&laquo; Previous",
                    "page": null,
                    "active": false
                },
                {
                    "url": "http://localhost/api/v1/technician/orders?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/orders",
            "per_page": 15,
            "to": null,
            "total": 0
        }
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Use `show` on a specific id for full details.

---

#### Show Order Details

**What this does:** Returns full details for one order.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:order` |
| **Route name** | `technician.orders.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `404 Not Found` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `order` — Order UUID (orders.uuid) — copy the `uuid` field from a List/Show response, not the numeric id. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
No request body. Use the documented path and query parameters.

**Actual Success Response(s):**

*{ 200 OK — real uuid resolves }*
```json
{
    "status": "success",
    "message": "Order retrieved successfully.",
    "data": {
        "id": 54,
        "uuid": "a43bfbc6-d4a1-4054-8d3a-4be3c0def5b9",
        "order_number": "ORD-260820-GZLK",
        "status": "accepted",
        "scheduled_date": "2026-08-27",
        "total_amount": "800.14",
        "customer": {
            "id": 220,
            "name": "Mark Glover",
            "phone": "1371432353"
        },
        "address": {
            "id": 58,
            "title": "Home"
        },
        "items": [
            {
                "id": 50,
                "service_name": "et ut repellendus",
                "quantity": 2,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": null,
                "to_status": "accepted",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 404 Not Found — integer id in uuid slot (uuid-vs-id regression check) }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

*{ 404 Not Found — unknown uuid }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

*{ 404 Not Found — another technician's order }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Use `update` to modify it, or `destroy` to remove it.

---

## 05. Accept & Reject Assignment

#### Accept Assignment Order

**What this does:** Accepts a pending assignment — only valid while `status: pending` **and** `technician_id` is already set to this technician. Moves the order to `accepted`.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/accept-assignment` |
| **Route name** | `technician.orders.accept-assignment` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `404 Not Found`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "key": "value"
}
```

**Actual Success Response(s):**

*{ 200 OK — assignment accepted }*
```json
{
    "status": "success",
    "message": "Assignment accepted.",
    "data": {
        "id": 56,
        "uuid": "30c2a7c2-d68c-4fde-9251-77aab3e27f0c",
        "order_number": "ORD-260820-MKXW",
        "status": "accepted",
        "scheduled_date": null,
        "total_amount": "272.55",
        "customer": {
            "id": 223,
            "name": "Dante Cruickshank",
            "phone": "1759462792"
        },
        "address": {
            "id": 59,
            "title": "Home"
        },
        "items": [
            {
                "id": 52,
                "service_name": "eaque nostrum vitae",
                "quantity": 3,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "pending",
                "to_status": "accepted",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": "pending",
                "to_status": "accepted",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "pending",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - not awaiting assignment response }*
```json
{
    "status": "fail",
    "message": "This order has no pending assignment to accept or reject.",
    "data": null
}
```

*{ 404 Not Found — integer id in uuid slot }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Start heading to the customer with `technician.orders.start-heading` (Statuses section) when ready.

---

#### Reject Assignment Order

**What this does:** Rejects a pending assignment — unassigns the technician, order returns to `pending` with `needs_attention: true`.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/reject-assignment` |
| **Route name** | `technician.orders.reject-assignment` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "key": "value"
}
```

**Actual Success Response(s):**

*{ 200 OK — assignment rejected }*
```json
{
    "status": "success",
    "message": "Assignment rejected.",
    "data": {
        "id": 57,
        "uuid": "e0f11e04-6e6a-4416-ad9c-aba2e5a7cb6e",
        "order_number": "ORD-260820-CBIU",
        "status": "pending",
        "scheduled_date": "2026-09-01",
        "total_amount": "265.43",
        "customer": {
            "id": 225,
            "name": "Maia Wintheiser DDS",
            "phone": "1334210049"
        },
        "address": {
            "id": 60,
            "title": "Home"
        },
        "items": [
            {
                "id": 53,
                "service_name": "pariatur quis quia",
                "quantity": 1,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "pending",
                "to_status": "pending",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "pending",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - not awaiting assignment response }*
```json
{
    "status": "fail",
    "message": "This order has no pending assignment to accept or reject.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** An admin must reassign via `admin.orders.assign-technician`.

---

## 06. Status Updates

#### Start Execution Order

**What this does:** Marks work as begun — `on_the_way → start`. Customer receives an `order_in_progress` notification. **This is also the point past which the customer can no longer self-cancel the order.**

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/start-execution` |
| **Route name** | `technician.orders.start-execution` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "key": "value"
}
```

**Actual Success Response(s):**

*{ 200 OK — on_the_way -> start }*
```json
{
    "status": "success",
    "message": "Order updated successfully.",
    "data": {
        "id": 60,
        "uuid": "a9c7873f-042a-42b8-b586-c3af35135a0a",
        "order_number": "ORD-260820-GBPT",
        "status": "start",
        "scheduled_date": "2026-08-26",
        "total_amount": "637.24",
        "customer": {
            "id": 229,
            "name": "Gilbert Klein",
            "phone": "1132036270"
        },
        "address": {
            "id": 62,
            "title": "Home"
        },
        "items": [
            {
                "id": 56,
                "service_name": "ullam error ad",
                "quantity": 1,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "on_the_way",
                "to_status": "start",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": "on_the_way",
                "to_status": "start",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "on_the_way",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - invalid transition (still pending) }*
```json
{
    "status": "fail",
    "message": "This status change is not allowed from the order's current status.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Call `finish` once the work is complete.

---

#### Start Heading Order

**What this does:** Marks the technician en route to the customer — `accepted → on_the_way`. Customer receives an `order_en_route` notification.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/start-heading` |
| **Route name** | `technician.orders.start-heading` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "key": "value"
}
```

**Actual Success Response(s):**

*{ 200 OK — accepted -> on_the_way }*
```json
{
    "status": "success",
    "message": "Order updated successfully.",
    "data": {
        "id": 59,
        "uuid": "ec442760-9125-4280-9974-1768f77cd8c1",
        "order_number": "ORD-260820-DNOQ",
        "status": "on_the_way",
        "scheduled_date": "2026-08-30",
        "total_amount": "711.64",
        "customer": {
            "id": 227,
            "name": "Mrs. Fanny Luettgen",
            "phone": "1575458589"
        },
        "address": {
            "id": 61,
            "title": "Home"
        },
        "items": [
            {
                "id": 55,
                "service_name": "doloribus aperiam labore",
                "quantity": 2,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "accepted",
                "to_status": "on_the_way",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": "accepted",
                "to_status": "on_the_way",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "accepted",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - invalid transition }*
```json
{
    "status": "fail",
    "message": "This status change is not allowed from the order's current status.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Once on-site, either start work (`start-execution`) or, if the customer can't be reached, report it (`unreachable`).

---

#### Unreachable Order

**What this does:** Reports being unable to reach the customer — only valid while `status: on_the_way`. Unassigns the technician; order returns to `pending` with `needs_attention: true`.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/unreachable` |
| **Route name** | `technician.orders.unreachable` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Send as `application/json`.

**Field documentation:**
### `reason`
- **Description:** Reason
- **Type:** `string`
- **Required:** required
- **Example:** `Manual adjustment by admin.`
- **Validation rules:** `required|string|max:500`
- `required` — must be provided.
- `string` — must be text.
- `max:500` — must not exceed 500.

**Example Request Body:**
```json
{
    "reason": "Customer is not answering calls or the door."
}
```

**Actual Success Response(s):**

*{ 200 OK — reported unreachable }*
```json
{
    "status": "success",
    "message": "Reported — the order has been unassigned for reassignment.",
    "data": {
        "id": 63,
        "uuid": "794043c5-64f9-47e7-b083-8a07e2a7727c",
        "order_number": "ORD-260820-JIVG",
        "status": "pending",
        "scheduled_date": null,
        "total_amount": "697.73",
        "customer": {
            "id": 233,
            "name": "Dora Parker",
            "phone": "1214689814"
        },
        "address": {
            "id": 64,
            "title": "Home"
        },
        "items": [
            {
                "id": 59,
                "service_name": "aut qui quia",
                "quantity": 1,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "on_the_way",
                "to_status": "pending",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": "on_the_way",
                "to_status": "pending",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "on_the_way",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - invalid state }*
```json
{
    "status": "fail",
    "message": "The customer can only be reported unreachable while en route.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Validation Error - missing reason }*
```json
{
    "status": "fail",
    "message": "The Reason field is required.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** An admin must reassign via `admin.orders.assign-technician`, same recovery path as a rejected assignment.

---

#### Upload Order Attachment

**What this does:** Attaches an already-uploaded media file (by `hash`) to the order as evidence/documentation — before/after photos, a signed report, a signature, etc. Finalizes the media record (`is_temp: false`, linked to the order) so it's no longer subject to temp-media cleanup.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/attachments` |
| **Route name** | `technician.orders.attachments.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) — order must be assigned to the authenticated technician |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response.

**Payload:**
Send as `application/json`. `hash` must come from the shared media-upload endpoint (`shared.media.*`) first — this route only attaches an existing, not-yet-associated media record; it does not accept a file directly.

**Field documentation:**
### `hash`
- **Description:** File
- **Type:** `string`
- **Required:** required
- **Example:** `f3a1c9...` (hash returned by the media upload endpoint)
- **Validation rules:** `required|string`, plus a `MediaExists` check
- `required` — must be provided.
- `string` — must be text.
- `MediaExists(type: ['image', 'application'])` — the media must exist by hash, not already be attached to a different record, have a mime type starting with `image/` or `application/`, and its physical file must actually exist on disk.
### `type`
- **Description:** Attachment type
- **Type:** `string`
- **Required:** required
- **Example:** `before`
- **Validation rules:** `required|string|in:before,after,report,issue,signature,other`
- `required` — must be provided.
- `in:before,after,report,issue,signature,other` — must be one of: before, after, report, issue, signature, other.
- **Only `after` and `report` count as completion evidence (BR-05).** Finishing an order (`POST /orders/:id/finish`) is blocked with `422` unless at least one attachment of type `after` or `report` exists on it — uploading `before`/`issue`/`signature`/`other` does not satisfy that requirement, even though all 6 types are valid for this endpoint.
### `note`
- **Description:** Note
- **Type:** `string`
- **Required:** optional
- **Example:** `Leak visible under the sink before repair.`
- **Validation rules:** `nullable|string|max:500`
- `nullable` — may be null.
- `string` — must be text.
- `max:500` — must not exceed 500.

**Example Request Body:**
```json
{
    "hash": "f3a1c9d8e7b6a5c4d3e2f1a0b9c8d7e6",
    "type": "before",
    "note": "Leak visible under the sink before repair"
}
```

**Example Success Response:**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Attachment uploaded successfully.",
    "data": {
        "id": 1,
        "type": "before",
        "file_url": "https://example.com/storage/attachments/f3a1c9d8.jpg",
        "note": "Leak visible under the sink before repair",
        "uploaded_by": 231,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Example Error Response(s):**

*{ 422 Unprocessable Entity — media hash not found }*
```json
{
    "status": "fail",
    "message": "The uploaded file was not found. Please upload it again.",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Attachment appears in the order's `attachments[]` list on subsequent Show Order Details / admin order detail calls.

---

## 07. Job Completion

#### Finish Order

**What this does:** Marks work as complete — `start → finish`. Customer receives an `order_work_submitted` notification.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/orders/:id/finish` |
| **Route name** | `technician.orders.finish` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Path Parameters:**
- `id` — Order UUID (`orders.uuid`) — NOT the numeric `orders.id`. Copy the `uuid` field from a List/Show response. — chained from an earlier request in this flow ({{order_id}})

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "key": "value"
}
```

**Actual Success Response(s):**

*{ 200 OK — start -> finish }*
```json
{
    "status": "success",
    "message": "Order updated successfully.",
    "data": {
        "id": 62,
        "uuid": "e05bc1e8-e520-4e58-8100-748323a70ea0",
        "order_number": "ORD-260820-VTRK",
        "status": "finish",
        "scheduled_date": null,
        "total_amount": "135.67",
        "customer": {
            "id": 231,
            "name": "Prof. Aletha Wiza Jr.",
            "phone": "1840597267"
        },
        "address": {
            "id": 63,
            "title": "Home"
        },
        "items": [
            {
                "id": 58,
                "service_name": "iusto doloremque voluptatem",
                "quantity": 1,
                "notes": null
            }
        ],
        "status_history": [
            {
                "from_status": "start",
                "to_status": "finish",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": "start",
                "to_status": "finish",
                "created_at": "2026-08-20T11:20:53+00:00"
            },
            {
                "from_status": null,
                "to_status": "start",
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "rating": null,
        "created_at": "2026-08-20T11:20:53+00:00"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - invalid transition (already finished) }*
```json
{
    "status": "fail",
    "message": "This status change is not allowed from the order's current status.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 no completion evidence (BR-05) }*
```json
{
    "status": "fail",
    "message": "Upload at least one completion photo or report before finishing this order.",
    "data": null
}
```
Triggered when the order has zero attachments of type `after` or `report` — see `type` under Upload Order Attachment above. Attachments of type `before`/`issue`/`signature`/`other` don't count, even if present.

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Awaits the customer's `confirm-completion` call (Requests / Jobs section) to reach the terminal `confirmed` status.

---

## 08. Earnings, Wallet & Withdrawal

#### Balance Wallet

**What this does:** Returns the technician's current wallet balance.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/wallet` |
| **Route name** | `technician.wallet.balance` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No request body. Use the documented path and query parameters.

**Actual Success Response(s):**

*{ 200 OK — freelance technician (can_withdraw=true) }*
```json
{
    "status": "success",
    "message": "Balance retrieved successfully",
    "data": {
        "balance": 250.5,
        "currency": "SAR",
        "is_locked": false,
        "can_withdraw": true
    }
}
```

*{ 200 OK — no wallet row yet (defaults to zero balance) }*
```json
{
    "status": "success",
    "message": "Balance retrieved successfully",
    "data": {
        "balance": 0,
        "currency": "SAR",
        "is_locked": false
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** See wallet/transactions for the ledger, or withdraw to request a payout.

---

#### Transactions Wallet

**What this does:** Returns the technician's wallet transaction history.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/wallet/transactions` |
| **Route name** | `technician.wallet.transactions` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No request body. Use the documented path and query parameters.

**Actual Success Response(s):**

*{ 200 OK — list transactions }*
```json
{
    "status": "success",
    "message": "Transactions retrieved successfully",
    "data": {
        "data": [
            {
                "id": 9,
                "type": "reversal",
                "type_label": "Transaction reversal",
                "amount": "50.00",
                "balance_after": "300.00",
                "status": "completed",
                "notes": null,
                "related_order": null,
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/technician/wallet/transactions?page=1",
            "last": "http://localhost/api/v1/technician/wallet/transactions?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
                {
                    "url": null,
                    "label": "&laquo; Previous",
                    "page": null,
                    "active": false
                },
                {
                    "url": "http://localhost/api/v1/technician/wallet/transactions?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/wallet/transactions",
            "per_page": 20,
            "to": 1,
            "total": 1
        }
    }
}
```

*{ 200 OK — filtered by status }*
```json
{
    "status": "success",
    "message": "Transactions retrieved successfully",
    "data": {
        "data": [
            {
                "id": 9,
                "type": "reversal",
                "type_label": "Transaction reversal",
                "amount": "50.00",
                "balance_after": "300.00",
                "status": "completed",
                "notes": null,
                "related_order": null,
                "created_at": "2026-08-20T11:20:53+00:00"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/technician/wallet/transactions?page=1",
            "last": "http://localhost/api/v1/technician/wallet/transactions?page=1",
            "prev": null,
            "next": null
        },
        "meta": {
            "current_page": 1,
            "from": 1,
            "last_page": 1,
            "links": [
                {
                    "url": null,
                    "label": "&laquo; Previous",
                    "page": null,
                    "active": false
                },
                {
                    "url": "http://localhost/api/v1/technician/wallet/transactions?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/wallet/transactions",
            "per_page": 20,
            "to": 1,
            "total": 1
        }
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Cross-reference entries with completed orders / withdrawal requests.

---

#### Withdraw Wallet

**What this does:** Submits a payout request — **debits the wallet immediately** (not on approval) and creates a `pending` `WithdrawRequest`. Only freelance technicians pass eligibility (`WalletValidationService::validateWithdrawal()`); individuals and in-house technicians get a 422.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/wallet/withdraw` |
| **Route name** | `technician.wallet.withdraw` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `201 Created`, `401 Unauthorized`, `422 Unprocessable Entity` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
Send as `application/json`.

**Field documentation:**
### `amount`
- **Description:** Amount
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|min:1`
- `required` — must be provided.
- `numeric` — must be numeric.
- `min:1` — must be at least 1.
### `bank_name`
- **Description:** Bank Name
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:100`
- `required` — must be provided.
- `string` — must be text.
- `max:100` — must not exceed 100.
### `account_number`
- **Description:** Account Number
- **Type:** `string`
- **Required:** required
- **Example:** `1234567890123`
- **Validation rules:** `required|string|max:100`
- `required` — must be provided.
- `string` — must be text.
- `max:100` — must not exceed 100.
### `iban`
- **Description:** IBAN
- **Type:** `string`
- **Required:** required
- **Example:** `EG123456789012345678901234`
- **Validation rules:** `required|string|max:50`
- `required` — must be provided.
- `string` — must be text.
- `max:50` — must not exceed 50.

**Example Request Body:**
```json
{
    "amount": 200,
    "bank_name": "Al Rajhi Bank",
    "account_number": "1234567890",
    "iban": "SA0380000000608010167519"
}
```

**Actual Success Response(s):**

*{ 201 Created — withdrawal submitted }*
```json
{
    "status": "success",
    "message": "Withdrawal request submitted successfully",
    "data": {
        "user_id": 237,
        "amount": 200,
        "bank_name": "Al Rajhi Bank",
        "account_number": "1234567890",
        "iban": "SA0380000000608010167519",
        "updated_at": "2026-08-20T11:20:53.000000Z",
        "created_at": "2026-08-20T11:20:53.000000Z",
        "id": 2
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - insufficient balance }*
```json
{
    "status": "fail",
    "message": "Insufficient wallet balance",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Validation Error - missing bank fields }*
```json
{
    "status": "fail",
    "message": "Please enter the bank name.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Validation Error - in-house technician cannot withdraw }*
```json
{
    "status": "fail",
    "message": "Wallet balance changes are not available for this account type yet",
    "data": null
}
```

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** An admin must approve or reject it — see `admin.withdraw-requests.approve` / `.reject` in the Admin section. Rejection credits the debited amount back.

---

## 09. Home

#### Home Technician

**What this does:** Returns the technician's home-screen summary (active assignment counts, etc.).

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/home` |
| **Route name** | `technician.home` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Technician (`user_type: technician`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `404 Not Found` |

**Headers:**
- `Accept: application/json`
- `Content-Type: application/json`
- `Accept-Language: {{locale}}`

**Payload:**
No request body. Use the documented path and query parameters.

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Home data retrieved successfully.",
    "data": {
        "availability_status": "available",
        "is_available": true,
        "current_orders": [],
        "today_summary": {
            "completed_orders": 0,
            "upcoming_orders": 0
        }
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

*{ 404 Not Found — no technician profile }*
```json
{
    "status": "fail",
    "message": "Technician profile not found.",
    "data": null
}
```

**What happens next:** Drill into specific orders via the Requests / Jobs and Statuses sections.

---
