# Shared / General

Functionality that is **not specific to one role**, or that multiple roles use through effectively the same route shape: media/file upload, notifications (the truly generic inbox module — role-specific notification actions like Client's device/preferences or Admin's broadcast-send stay in their own file), support conversations (Client/Company/Technician's identical "my own conversation" pattern — Admin's oversight actions on *any* conversation stay in `05-admin.md`), locations/geo hierarchy, branches, categories/services (the shared catalog), general app-wide settings and feature flags, catalog/content search, health/status, and reference content (FAQs, static pages, sliders). Every entry below states its own guard, allowed roles, and whether it's public or protected — "shared" describes the *functionality*, not a single uniform access rule.

**117 endpoints in this section.**

## 01. Media / File Upload

#### List All Media

**What this does:** Lists previously uploaded media belonging to the authenticated user.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/media?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `shared.media.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `200 OK` |

**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 — Example Response }*
```json
{
    "status": "success",
    "message": "Media list retrieved successfully",
    "data": []
}
```

**What happens next:** Reference a returned media `id` in any field that validates against `exists:media,id` (avatars, documents, contract signatures).

---

#### Create New Medium

**What this does:** Uploads a file (multipart `file`) and returns a media `id`/URL — the authenticated equivalent of `client.upload`, used once a user already has a token (e.g. a company uploading a signed contract PDF).

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/media` |
| **Route name** | `shared.media.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `422 Unprocessable Entity` |

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

**Payload:**
Send as `multipart/form-data`. Select a local file for fields marked as `file`.

**Field documentation:**
### `file`
- **Description:** File
- **Type:** `file`
- **Required:** required
- **Example:** `value`
- **Validation rules:** `required|file|max:10240|mimes:jpg,jpeg,png,gif,webp,pdf,avi,mov,wmv,flv,mkv,mp4,mp3,wav`
- `required` — must be provided.
- `file` — custom validation rule.
- `max:10240` — must not exceed 10240.
- `mimes:jpg,jpeg,png,gif,webp,pdf,avi,mov,wmv,flv,mkv,mp4,mp3,wav` — custom validation rule.
### `collection`
- **Description:** Collection
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```text
file:  [file]
collection: value [text]
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — Example Response }*
```json
{
    "status": "fail",
    "message": "Please select a file to upload.",
    "data": null
}
```

**What happens next:** Pass the returned `id` as the relevant `*_id` field on the next call (e.g. `signed_media_id` on contract creation).

---

#### Upload Client

**What this does:** **Public** file upload (multipart `file`) — used before an account exists, e.g. uploading technician registration documents (`personal_photo`, `national_id_front/back`, `cv_file`) prior to `POST /technician/auth/register`.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/client/upload` |
| **Route name** | `client.upload` |
| **Guard** | None — public endpoint |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

**Payload:**
Send as `multipart/form-data`. Select a local file for fields marked as `file`.

**Field documentation:**
### `file`
- **Description:** File
- **Type:** `file`
- **Required:** required
- **Example:** `value`
- **Validation rules:** `required|file|max:10240`
- `required` — must be provided.
- `file` — custom validation rule.
- `max:10240` — must not exceed 10240.
### `type`
- **Description:** File Type
- **Type:** `file`
- **Required:** optional
- **Example:** `image`
- **Validation rules:** `nullable|string|in:image,document,video,audio`
- `nullable` — may be null.
- `string` — must be text.
- `in:image,document,video,audio` — must be one of: image, document, video, audio.

**Example Request Body:**
```text
file:  [file]
type: image [text]
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "File uploaded successfully",
    "data": {
        "id": 2,
        "hash": "e428a7c4613e522faab63cdd1ef68f80",
        "url": "/storage/uploads/general/b7dc8979-b6bb-49ac-aa43-9a2939f420e0.webp",
        "mime_type": "image/webp",
        "size": 44,
        "original_name": "photo.jpg",
        "variants": {
            "thumbnail": "/storage/uploads/general/variants/b7dc8979-b6bb-49ac-aa43-9a2939f420e0_thumbnail.webp",
            "medium": "/storage/uploads/general/variants/b7dc8979-b6bb-49ac-aa43-9a2939f420e0_medium.webp",
            "large": "/storage/uploads/general/variants/b7dc8979-b6bb-49ac-aa43-9a2939f420e0_large.webp"
        }
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select a file to upload.",
    "data": null
}
```

**What happens next:** Use the returned media `id` in the relevant registration field.

---

#### Delete Medium

**What this does:** Deletes a previously uploaded media record.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/media/:media_id` |
| **Route name** | `shared.media.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `media_id` — Media_id ID — chained from an earlier request in this flow ({{media_id}})

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

**Actual Success Response(s):**

*{ 200 OK — Example Response }*
```json
{
    "status": "success",
    "message": "Resource updated/deleted successfully"
}
```

**What happens next:** Any field that referenced this media `id` will fail its `exists:media,id` validation on future calls.

---

## 02. Notifications

#### List All Notifications

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

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

**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 — Example Response }*
```json
{
    "status": "success",
    "message": "Notifications retrieved successfully",
    "data": {
        "unread_count": 0,
        "notifications": {
            "super_admin_notifications": [],
            "links": {
                "first": "http://localhost:8000/api/v1/notifications?page=1",
                "last": "http://localhost:8000/api/v1/notifications?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:8000/api/v1/notifications?page=1",
                        "label": "1",
                        "page": 1,
                        "active": true
                    },
                    {
                        "url": null,
                        "label": "Next &raquo;",
                        "page": null,
                        "active": false
                    }
                ],
                "path": "http://localhost:8000/api/v1/notifications",
                "per_page": 15,
                "to": null,
                "total": 0
            }
        }
    }
}
```

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

---

#### Show Notification Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/notifications/:id` |
| **Route name** | `shared.notifications.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Actual Error Response(s):**

*{ 404 Not Found — Example Response }*
```json
{
    "status": "fail",
    "message": "Notification not found",
    "data": null
}
```

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

---

#### Clear All Notification

**What this does:** Deletes every one of the caller's notifications.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/notifications/clear-all` |
| **Route name** | `shared.notifications.clear-all` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `200 OK` |

**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 — Example Response }*
```json
{
    "status": "success",
    "message": "Resource updated/deleted successfully"
}
```

**What happens next:** The list is now empty until new events occur.

---

#### Mark All Read Notification

**What this does:** Marks every one of the caller's notifications as read in one call.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/notifications/mark-all-read` |
| **Route name** | `shared.notifications.mark-all-read` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `200 OK` |

**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 — Example Response }*
```json
{
    "status": "success",
    "message": "All notifications marked as read",
    "data": null
}
```

**What happens next:** Unread count resets to zero.

---

#### Delete Notification

**What this does:** Permanently deletes a notification.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/notifications/:id` |
| **Route name** | `shared.notifications.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Any authenticated role (client, company, technician, admin) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — Example Response }*
```json
{
    "status": "success",
    "message": "Resource updated/deleted successfully"
}
```

**What happens next:** It no longer appears in `index`.

---

## 03. Conversations & Messaging

#### List All Conversations

**What this does:** Lists the caller's own support conversations.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/conversations?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `client.conversations.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "data": [
            {
                "id": 1,
                "subject": "Billing question",
                "status": "Open",
                "last_message": "Hi, I have a question.",
                "created_at": "2026-08-20T10:52:49.000000Z",
                "closed_at": null
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/client/conversations?page=1",
            "last": "http://localhost/api/v1/client/conversations?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/client/conversations?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/client/conversations",
            "per_page": 15,
            "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:** Open a specific thread with the matching `show` action, or start a new one with `store`.

---

#### List All Conversations

**What this does:** Lists the caller's own support conversations.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/company/conversations?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `company.conversations.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "data": [
            {
                "id": 6,
                "subject": "Invoice discrepancy",
                "status": "Open",
                "last_message": "Our latest invoice total does not match the contract terms.",
                "created_at": "2026-08-20T11:43:10.000000Z",
                "closed_at": null
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/company/conversations?page=1",
            "last": "http://localhost/api/v1/company/conversations?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/company/conversations?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/company/conversations",
            "per_page": 15,
            "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:** Open a specific thread with the matching `show` action, or start a new one with `store`.

---

#### List All Conversations

**What this does:** Lists the caller's own support conversations.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/conversations?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `technician.conversations.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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "data": [
            {
                "id": 6,
                "subject": "Payout delay",
                "status": "Open",
                "last_message": "My last payout has not arrived yet.",
                "created_at": "2026-08-20T11:20:53.000000Z",
                "closed_at": null
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/technician/conversations?page=1",
            "last": "http://localhost/api/v1/technician/conversations?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/conversations?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/technician/conversations",
            "per_page": 15,
            "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:** Open a specific thread with the matching `show` action, or start a new one with `store`.

---

#### Show Conversation Details

**What this does:** Returns one support conversation with its full message thread.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/conversations/:id` |
| **Route name** | `client.conversations.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "subject": "Billing question",
        "status": "Open",
        "created_at": "2026-08-20T10:52:53.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 3,
                "sender_type": "customer",
                "body": "Hi, I have a question.",
                "sent_at": "2026-08-20T10:52:53.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Another Customer's Conversation }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

**What happens next:** Reply with the `messages.store` action on the same conversation id.

---

#### Show Conversation Details

**What this does:** Returns one support conversation with its full message thread.

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

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

**Path Parameters:**
- `id` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 9,
        "subject": "Invoice discrepancy",
        "status": "Open",
        "created_at": "2026-08-20T11:43:10.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 9,
                "sender_type": "customer",
                "body": "Our latest invoice total does not match the contract terms.",
                "sent_at": "2026-08-20T11:43:10.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Another Company's Conversation }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

**What happens next:** Reply with the `messages.store` action on the same conversation id.

---

#### Show Conversation Details

**What this does:** Returns one support conversation with its full message thread.

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

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

**Path Parameters:**
- `id` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 8,
        "subject": "Payout delay",
        "status": "Open",
        "created_at": "2026-08-20T11:20:53.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 8,
                "sender_type": "technician",
                "body": "My last payout has not arrived yet.",
                "sent_at": "2026-08-20T11:20:53.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Another User's Conversation }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

**What happens next:** Reply with the `messages.store` action on the same conversation id.

---

#### Create New Message

**What this does:** Adds a message to an existing conversation the caller is party to.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/client/conversations/:id/messages` |
| **Route name** | `client.conversations.messages.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Field documentation:**
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "body": "Any update on this?"
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Message sent successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Not Found }*
```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:** An admin sees it in `GET /admin/conversations/{id}` and can reply.

---

#### Create New Conversation

**What this does:** Opens a new support conversation with an initial subject/message.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/client/conversations` |
| **Route name** | `client.conversations.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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:**
### `subject`
- **Description:** Subject
- **Type:** `string`
- **Required:** optional
- **Example:** `Inquiry about your service`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "subject": "Refund not received",
    "body": "I cancelled my order 3 days ago and still have not received my refund."
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "subject": "Refund not received",
        "status": "Open",
        "created_at": "2026-08-20T10:52:53.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 2,
                "sender_type": "customer",
                "body": "I cancelled my order 3 days ago and still have not received my refund.",
                "sent_at": "2026-08-20T10:52:53.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

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

**What happens next:** Continue the thread with the `messages.store` action, or await an admin reply (`admin.conversations.reply`).

---

#### Create New Message

**What this does:** Adds a message to an existing conversation the caller is party to.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/company/conversations/:id/messages` |
| **Route name** | `company.conversations.messages.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Field documentation:**
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "body": "Any update on this?"
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Message sent successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Not Found }*
```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:** An admin sees it in `GET /admin/conversations/{id}` and can reply.

---

#### Create New Conversation

**What this does:** Opens a new support conversation with an initial subject/message.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/company/conversations` |
| **Route name** | `company.conversations.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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:**
### `subject`
- **Description:** Subject
- **Type:** `string`
- **Required:** optional
- **Example:** `Inquiry about your service`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "subject": "Contract renewal question",
    "body": "We would like to renew our maintenance contract early — is that possible?"
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 7,
        "subject": "Contract renewal question",
        "status": "Open",
        "created_at": "2026-08-20T11:43:10.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 7,
                "sender_type": "customer",
                "body": "We would like to renew our maintenance contract early — is that possible?",
                "sent_at": "2026-08-20T11:43:10.000000Z"
            }
        ]
    }
}
```

*{ 201 Created — BUG: sender_type hardcoded to 'customer' for a company sender }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 8,
        "subject": "Sender type check",
        "status": "Open",
        "created_at": "2026-08-20T11:43:10.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 8,
                "sender_type": "customer",
                "body": "Verifying sender_type on the opening message.",
                "sent_at": "2026-08-20T11:43:10.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

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

**What happens next:** Continue the thread with the `messages.store` action, or await an admin reply (`admin.conversations.reply`).

---

#### Create New Message

**What this does:** Adds a message to an existing conversation the caller is party to.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/conversations/:id/messages` |
| **Route name** | `technician.conversations.messages.store` |
| **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` — Id ID — chained from an earlier request in this flow ({{conversation_id}})

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

**Field documentation:**
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "body": "Any update on this?"
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Message sent successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

*{ 404 Not Found — Not Found }*
```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:** An admin sees it in `GET /admin/conversations/{id}` and can reply.

---

#### Create New Conversation

**What this does:** Opens a new support conversation with an initial subject/message.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/technician/conversations` |
| **Route name** | `technician.conversations.store` |
| **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:**
### `subject`
- **Description:** Subject
- **Type:** `string`
- **Required:** optional
- **Example:** `Inquiry about your service`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `body`
- **Description:** Message
- **Type:** `string`
- **Required:** required
- **Example:** `Example message body text.`
- **Validation rules:** `required|string|max:2000`
- `required` — must be provided.
- `string` — must be text.
- `max:2000` — must not exceed 2000.

**Example Request Body:**
```json
{
    "subject": "Assignment issue",
    "body": "I was assigned an order outside my coverage area."
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 7,
        "subject": "Assignment issue",
        "status": "Open",
        "created_at": "2026-08-20T11:20:53.000000Z",
        "closed_at": null,
        "messages": [
            {
                "id": 7,
                "sender_type": "customer",
                "body": "I was assigned an order outside my coverage area.",
                "sent_at": "2026-08-20T11:20:53.000000Z"
            }
        ]
    }
}
```

**Actual Error Response(s):**

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

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

**What happens next:** Continue the thread with the `messages.store` action, or await an admin reply (`admin.conversations.reply`).

---

## 04. Locations (Geo Hierarchy)

#### List All Countries

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[continent]=asia` |
| **Route name** | `admin.countries.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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)
- `filters[continent]` — Filter by continent (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "countries": [
            {
                "id": 2,
                "name": "Egypt",
                "slug": null,
                "currency": "EGP",
                "nationality": "Egyptian",
                "phone_code": "20",
                "phone_starting_number": 1,
                "phone_length": 10,
                "short_name": "EG",
                "continent": "africa",
                "flag": "https://ui-avatars.com/api/?name=🏳&background=e5e7eb&color=6b7280&size=128",
                "is_active": true,
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/countries?page=1",
            "last": "http://localhost/api/v1/admin/countries?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/admin/countries?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/countries",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Governorates

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[country_id]=1` |
| **Route name** | `admin.governorates.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 (optional)
- `filters[country_id]` — Filter by country ID (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "governorates": [
            {
                "id": 2,
                "name": "Cairo",
                "slug": "cairo",
                "country_id": 7,
                "lat": 30,
                "lng": 31,
                "sort_order": 1,
                "is_active": true,
                "country": {
                    "id": 7,
                    "name": "Egypt",
                    "short_name": "EG",
                    "phone_code": "20"
                },
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/governorates?page=1",
            "last": "http://localhost/api/v1/admin/governorates?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/admin/governorates?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/governorates",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Regions

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[governorate_id]=1&filters[shipping_zone_id]=1` |
| **Route name** | `admin.regions.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 (optional)
- `filters[governorate_id]` — Filter by governorate ID (optional)
- `filters[shipping_zone_id]` — Filter by shipping zone ID (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "regions": [
            {
                "id": 2,
                "name": "Maadi",
                "slug": "maadi",
                "governorate_id": 7,
                "shipping_zone_id": 1,
                "lat": 30.05,
                "lng": 31.25,
                "sort_order": 1,
                "is_active": true,
                "governorate": {
                    "id": 7,
                    "name": "Cairo"
                },
                "shipping_zone": {
                    "id": 1,
                    "name": "Zone A"
                },
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/regions?page=1",
            "last": "http://localhost/api/v1/admin/regions?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/admin/regions?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/regions",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Shipping zones

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[country_id]=1` |
| **Route name** | `admin.shipping-zones.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 (optional)
- `filters[country_id]` — Filter by country ID (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "shipping_zones": [
            {
                "id": 2,
                "name": "Zone A",
                "slug": "zone-a",
                "country_id": 9,
                "shipping_cost": "15.00",
                "delivery_eta_min_days": 1,
                "delivery_eta_max_days": 2,
                "is_active": true,
                "country": {
                    "id": 9,
                    "name": "Egypt"
                },
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/shipping-zones?page=1",
            "last": "http://localhost/api/v1/admin/shipping-zones?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/admin/shipping-zones?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/shipping-zones",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### Show Country Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries/:country` |
| **Route name** | `admin.countries.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `country` — Country ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "name": "Saudi Arabia",
        "slug": null,
        "currency": "SAR",
        "nationality": "Saudi",
        "phone_code": "966",
        "phone_starting_number": 5,
        "phone_length": 9,
        "short_name": "SA",
        "continent": "asia",
        "flag": "https://ui-avatars.com/api/?name=🏳&background=e5e7eb&color=6b7280&size=128",
        "is_active": true,
        "created_at": "2026-08-20 12:09:43",
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Saudi Arabia",
            "slug": null,
            "currency": "SAR",
            "nationality": "Saudi"
        },
        "ar": {
            "name": "السعودية",
            "slug": null,
            "currency": "ريال",
            "nationality": "سعودي"
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Governorate Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates/:governorate` |
| **Route name** | `admin.governorates.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `governorate` — Governorate ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "name": "Alexandria",
        "slug": "alexandria",
        "country_id": 7,
        "lat": 31.2,
        "lng": 29.9,
        "sort_order": 2,
        "is_active": true,
        "country": {
            "id": 7,
            "name": "Egypt",
            "short_name": "EG",
            "phone_code": "20"
        },
        "created_at": "2026-08-20 12:09:43",
        "regions": [],
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Alexandria",
            "slug": "alexandria"
        },
        "ar": {
            "name": "الإسكندرية",
            "slug": "alex-ar"
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Region Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions/:region` |
| **Route name** | `admin.regions.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `region` — Region ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "name": "Nasr City",
        "slug": "nasr-city",
        "governorate_id": 7,
        "shipping_zone_id": 1,
        "lat": 30.06,
        "lng": 31.33,
        "sort_order": 2,
        "is_active": true,
        "governorate": {
            "id": 7,
            "name": "Cairo"
        },
        "shipping_zone": {
            "id": 1,
            "name": "Zone A"
        },
        "created_at": "2026-08-20 12:09:43",
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Nasr City",
            "slug": "nasr-city"
        },
        "ar": {
            "name": "مدينة نصر",
            "slug": "nasr-city-ar"
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Shipping zone Details

**What this does:** Returns full details for one shipping zone.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones/:shipping_zone` |
| **Route name** | `admin.shipping-zones.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `shipping_zone` — Shipping_zone ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "name": "Zone B",
        "slug": "zone-b",
        "country_id": 9,
        "shipping_cost": "25.00",
        "delivery_eta_min_days": 2,
        "delivery_eta_max_days": 5,
        "is_active": true,
        "country": {
            "id": 9,
            "name": "Egypt"
        },
        "created_at": "2026-08-20 12:09:43",
        "regions": [],
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Zone B",
            "slug": "zone-b"
        },
        "ar": {
            "name": "منطقة ب",
            "slug": "zone-b-ar"
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Create New Country

**What this does:** Creates a new country.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries` |
| **Route name** | `admin.countries.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `phone_code`
- **Description:** Phone Code
- **Type:** `string`
- **Required:** required
- **Example:** `20`
- **Validation rules:** `required|string|max:5|unique:countries,phone_code,NULL,id`
- `required` — must be provided.
- `string` — must be text.
- `max:5` — must not exceed 5.
- `unique:countries,phone_code,NULL,id` — must be unique (countries, phone_code, NULL, id).
### `phone_starting_number`
- **Description:** Phone Starting Number
- **Type:** `string`
- **Required:** required
- **Example:** `1234567890`
- **Validation rules:** `required|string|max:10`
- `required` — must be provided.
- `string` — must be text.
- `max:10` — must not exceed 10.
### `phone_length`
- **Description:** Phone Length
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|integer|between:5,15`
- `required` — must be provided.
- `integer` — must be a whole number.
- `between:5,15` — must be between 5, 15.
### `short_name`
- **Description:** Short Name
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:10`
- `required` — must be provided.
- `string` — must be text.
- `max:10` — must not exceed 10.
### `continent`
- **Description:** Continent
- **Type:** `string`
- **Required:** required
- **Example:** `africa`
- **Validation rules:** `required|in:"africa","europe","asia","south_america","north_america","australia"`
- `required` — must be provided.
- `in:"africa","europe","asia","south_america","north_america","australia"` — must be one of: "africa", "europe", "asia", "south_america", "north_america", "australia".
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `flag`
- **Description:** Flag
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.currency`
- **Description:** Currency (en)
- **Type:** `string`
- **Required:** required
- **Example:** `EGP`
- **Validation rules:** `required|string|max:50`
- `required` — must be provided.
- `string` — must be text.
- `max:50` — must not exceed 50.
### `en.nationality`
- **Description:** Nationality (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Egyptian`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.currency`
- **Description:** Currency (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `EGP`
- **Validation rules:** `required|string|max:50`
- `required` — must be provided.
- `string` — must be text.
- `max:50` — must not exceed 50.
### `ar.nationality`
- **Description:** Nationality (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Egyptian`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "phone_code": "966",
    "phone_starting_number": "5",
    "phone_length": 9,
    "short_name": "SA",
    "continent": "asia",
    "is_active": true,
    "en": {
        "name": "Saudi Arabia",
        "currency": "SAR",
        "nationality": "Saudi"
    },
    "ar": {
        "name": "السعودية",
        "currency": "ريال",
        "nationality": "سعودي"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "phone_code": "966",
        "phone_starting_number": 5,
        "phone_length": 9,
        "short_name": "SA",
        "continent": "asia",
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Saudi Arabia",
        "slug": null,
        "currency": "SAR",
        "nationality": "Saudi"
    }
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Duplicate Phone Code }*
```json
{
    "status": "fail",
    "message": "The Phone Code has already been taken.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this country.

---

#### Create New Governorate

**What this does:** Creates a new governorate.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates` |
| **Route name** | `admin.governorates.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `country_id`
- **Description:** Country
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|exists:countries,id`
- `required` — must be provided.
- `exists:countries,id` — must reference an existing record (countries, id).
### `lat`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-90,90`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `lng`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-180,180`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "country_id": 1,
    "lat": 31.2,
    "lng": 29.9,
    "is_active": true,
    "sort_order": 2,
    "en": {
        "name": "Alexandria",
        "slug": "alexandria"
    },
    "ar": {
        "name": "الإسكندرية",
        "slug": "alex-ar"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "country_id": 7,
        "lat": 31.2,
        "lng": 29.9,
        "is_active": true,
        "sort_order": 2,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Alexandria",
        "slug": "alexandria"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select a country.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Invalid Country }*
```json
{
    "status": "fail",
    "message": "The selected country does not exist.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this governorate.

---

#### Create New Region

**What this does:** Creates a new region.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions` |
| **Route name** | `admin.regions.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `governorate_id`
- **Description:** Governorate
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|exists:governorates,id`
- `required` — must be provided.
- `exists:governorates,id` — must reference an existing record (governorates, id).
### `shipping_zone_id`
- **Description:** Shipping Zone
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|exists:shipping_zones,id`
- `nullable` — may be null.
- `exists:shipping_zones,id` — must reference an existing record (shipping_zones, id).
### `lat`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-90,90`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `lng`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-180,180`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "governorate_id": 1,
    "shipping_zone_id": 1,
    "lat": 30.06,
    "lng": 31.33,
    "is_active": true,
    "sort_order": 2,
    "en": {
        "name": "Nasr City",
        "slug": "nasr-city"
    },
    "ar": {
        "name": "مدينة نصر",
        "slug": "nasr-city-ar"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "governorate_id": 7,
        "shipping_zone_id": 1,
        "lat": 30.06,
        "lng": 31.33,
        "is_active": true,
        "sort_order": 2,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Nasr City",
        "slug": "nasr-city"
    }
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Invalid Governorate }*
```json
{
    "status": "fail",
    "message": "The selected governorate does not exist.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this region.

---

#### Create New Shipping zone

**What this does:** Creates a new shipping zone.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones` |
| **Route name** | `admin.shipping-zones.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `country_id`
- **Description:** Country
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|exists:countries,id`
- `required` — must be provided.
- `exists:countries,id` — must reference an existing record (countries, id).
### `shipping_cost`
- **Description:** Shipping Cost
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|min:0`
- `required` — must be provided.
- `numeric` — must be numeric.
- `min:0` — must be at least .
### `delivery_eta_min_days`
- **Description:** Min Delivery Days
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|integer|min:0`
- `required` — must be provided.
- `integer` — must be a whole number.
- `min:0` — must be at least .
### `delivery_eta_max_days`
- **Description:** Max Delivery Days
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|integer|min:0`
- `required` — must be provided.
- `integer` — must be a whole number.
- `min:0` — must be at least .
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "country_id": 1,
    "shipping_cost": 25,
    "delivery_eta_min_days": 2,
    "delivery_eta_max_days": 5,
    "is_active": true,
    "en": {
        "name": "Zone B",
        "slug": "zone-b"
    },
    "ar": {
        "name": "منطقة ب",
        "slug": "zone-b-ar"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "country_id": 9,
        "shipping_cost": "25.00",
        "delivery_eta_min_days": 2,
        "delivery_eta_max_days": 5,
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Zone B",
        "slug": "zone-b"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select a country.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Invalid Country }*
```json
{
    "status": "fail",
    "message": "The selected country does not exist.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this shipping zone.

---

#### Update Country

**What this does:** Updates an existing country.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries/:country` |
| **Route name** | `admin.countries.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `country` — Country ID — seeded example: `1`

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

**Field documentation:**
### `phone_code`
- **Description:** Phone Code
- **Type:** `string`
- **Required:** optional
- **Example:** `20`
- **Validation rules:** `sometimes|string|max:5|unique:countries,phone_code,NULL,id`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:5` — must not exceed 5.
- `unique:countries,phone_code,NULL,id` — must be unique (countries, phone_code, NULL, id).
### `phone_starting_number`
- **Description:** Phone Starting Number
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `sometimes|string|max:10`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:10` — must not exceed 10.
### `phone_length`
- **Description:** Phone Length
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|integer|between:5,15`
- `sometimes` — validated only when the field is present.
- `integer` — must be a whole number.
- `between:5,15` — must be between 5, 15.
### `short_name`
- **Description:** Short Name
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:10`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:10` — must not exceed 10.
### `continent`
- **Description:** Continent
- **Type:** `string`
- **Required:** optional
- **Example:** `africa`
- **Validation rules:** `sometimes|in:"africa","europe","asia","south_america","north_america","australia"`
- `sometimes` — validated only when the field is present.
- `in:"africa","europe","asia","south_america","north_america","australia"` — must be one of: "africa", "europe", "asia", "south_america", "north_america", "australia".
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `flag`
- **Description:** Flag
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.currency`
- **Description:** Currency (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `EGP`
- **Validation rules:** `sometimes|string|max:50`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:50` — must not exceed 50.
### `en.nationality`
- **Description:** Nationality (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Egyptian`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.currency`
- **Description:** Currency (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `EGP`
- **Validation rules:** `sometimes|string|max:50`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:50` — must not exceed 50.
### `ar.nationality`
- **Description:** Nationality (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Egyptian`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "en": {
        "name": "Saudi Arabia Updated",
        "currency": "SAR",
        "nationality": "Saudi"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "phone_code": "966",
        "phone_starting_number": 5,
        "phone_length": 9,
        "short_name": "SA",
        "continent": "asia",
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Saudi Arabia Updated",
        "slug": null,
        "currency": "SAR",
        "nationality": "Saudi"
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Governorate

**What this does:** Updates an existing governorate.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates/:governorate` |
| **Route name** | `admin.governorates.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `governorate` — Governorate ID — seeded example: `1`

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

**Field documentation:**
### `country_id`
- **Description:** Country
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|exists:countries,id`
- `sometimes` — validated only when the field is present.
- `exists:countries,id` — must reference an existing record (countries, id).
### `lat`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-90,90`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `lng`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-180,180`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "en": {
        "name": "Alexandria Updated",
        "slug": "alexandria"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "country_id": 7,
        "lat": 31.2,
        "lng": 29.9,
        "is_active": true,
        "sort_order": 2,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Alexandria Updated",
        "slug": "alexandria"
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Region

**What this does:** Updates an existing region.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions/:region` |
| **Route name** | `admin.regions.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `region` — Region ID — seeded example: `1`

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

**Field documentation:**
### `governorate_id`
- **Description:** Governorate
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|exists:governorates,id`
- `sometimes` — validated only when the field is present.
- `exists:governorates,id` — must reference an existing record (governorates, id).
### `shipping_zone_id`
- **Description:** Shipping Zone
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|exists:shipping_zones,id`
- `nullable` — may be null.
- `exists:shipping_zones,id` — must reference an existing record (shipping_zones, id).
### `lat`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-90,90`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `lng`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric|between:-180,180`
- `nullable` — may be null.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "en": {
        "name": "Nasr City Updated",
        "slug": "nasr-city"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "governorate_id": 7,
        "shipping_zone_id": 1,
        "lat": 30.06,
        "lng": 31.33,
        "is_active": true,
        "sort_order": 2,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Nasr City Updated",
        "slug": "nasr-city"
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Shipping zone

**What this does:** Updates an existing shipping zone.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones/:shipping_zone` |
| **Route name** | `admin.shipping-zones.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `shipping_zone` — Shipping_zone ID — seeded example: `1`

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

**Field documentation:**
### `country_id`
- **Description:** Country
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|exists:countries,id`
- `sometimes` — validated only when the field is present.
- `exists:countries,id` — must reference an existing record (countries, id).
### `shipping_cost`
- **Description:** Shipping Cost
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric|min:0`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
- `min:0` — must be at least .
### `delivery_eta_min_days`
- **Description:** Min Delivery Days
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|integer|min:0`
- `sometimes` — validated only when the field is present.
- `integer` — must be a whole number.
- `min:0` — must be at least .
### `delivery_eta_max_days`
- **Description:** Max Delivery Days
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|integer|min:0`
- `sometimes` — validated only when the field is present.
- `integer` — must be a whole number.
- `min:0` — must be at least .
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.slug`
- **Description:** Slug (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.slug`
- **Description:** Slug (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `example-slug`
- **Validation rules:** `nullable|string|max:255`
- `nullable` — may be null.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "en": {
        "name": "Zone B Updated",
        "slug": "zone-b"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "country_id": 9,
        "shipping_cost": "25.00",
        "delivery_eta_min_days": 2,
        "delivery_eta_max_days": 5,
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "name": "Zone B Updated",
        "slug": "zone-b"
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Countries Geo

**What this does:** Returns the public list of active countries for address/registration forms.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/countries?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[continent]=asia` |
| **Route name** | `client.geo.countries` |
| **Guard** | None — public endpoint; `geo.context` |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` |

**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)
- `filters[continent]` — Filter by continent (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "countries": [
            {
                "id": 31,
                "phone_code": null,
                "phone_starting_number": null,
                "phone_length": null,
                "short_name": null,
                "continent": "asia",
                "is_active": true,
                "created_at": "2026-08-20T10:52:55.000000Z",
                "updated_at": "2026-08-20T10:52:55.000000Z",
                "name": null,
                "slug": null,
                "currency": null,
                "nationality": null
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/client/countries?page=1",
            "last": "http://localhost/api/v1/client/countries?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/client/countries?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/client/countries",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

**What happens next:** Use a returned `id` as `country_id` on address or registration forms.

---

#### Governorates Geo

**What this does:** Returns governorates for a given `country_id`.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/governorates?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[country_id]=1` |
| **Route name** | `client.geo.governorates` |
| **Guard** | None — public endpoint; `geo.context` |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` |

**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 (optional)
- `filters[country_id]` — Filter by country ID (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "governorates": [
            {
                "id": 1,
                "country_id": 32,
                "lat": 30.04,
                "lng": 31.23,
                "is_active": true,
                "sort_order": 1,
                "created_at": "2026-08-20T10:52:55.000000Z",
                "updated_at": "2026-08-20T10:52:55.000000Z",
                "name": "القاهرة",
                "slug": "القاهرة"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/client/governorates?page=1",
            "last": "http://localhost/api/v1/client/governorates?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/client/governorates?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/client/governorates",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

**What happens next:** Use a returned `id` as `governorate_id`.

---

#### Regions Geo

**What this does:** Returns regions for a given `governorate_id`.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/regions?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1&filters[governorate_id]=1&filters[shipping_zone_id]=1` |
| **Route name** | `client.geo.regions` |
| **Guard** | None — public endpoint; `geo.context` |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` |

**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 (optional)
- `filters[governorate_id]` — Filter by governorate ID (optional)
- `filters[shipping_zone_id]` — Filter by shipping zone ID (optional)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "regions": [
            {
                "id": 1,
                "governorate_id": 2,
                "shipping_zone_id": 1,
                "lat": 30.05,
                "lng": 31.22,
                "is_active": true,
                "sort_order": 1,
                "created_at": "2026-08-20T10:52:55.000000Z",
                "updated_at": "2026-08-20T10:52:55.000000Z",
                "name": "مدينة نصر",
                "slug": "مدينة-نصر"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/client/regions?page=1",
            "last": "http://localhost/api/v1/client/regions?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/client/regions?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/client/regions",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

**What happens next:** Use a returned `id` as `region_id` on addresses.

---

#### Delete Country

**What this does:** Permanently deletes a country.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries/:country` |
| **Route name** | `admin.countries.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `country` — Country ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Governorate

**What this does:** Permanently deletes a governorate.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates/:governorate` |
| **Route name** | `admin.governorates.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `governorate` — Governorate ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Region

**What this does:** Permanently deletes a region.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions/:region` |
| **Route name** | `admin.regions.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `region` — Region ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Shipping zone

**What this does:** Permanently deletes a shipping zone.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones/:shipping_zone` |
| **Route name** | `admin.shipping-zones.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `shipping_zone` — Shipping_zone ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Bulk

**What this does:** Deletes multiple countries in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/countries/bulk` |
| **Route name** | `admin.countries.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `countries.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted countries no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple governorates in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/governorates/bulk` |
| **Route name** | `admin.governorates.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `governorates.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted governorates no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple regions in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/regions/bulk` |
| **Route name** | `admin.regions.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `regions.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted regions no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple shipping zones in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/shipping-zones/bulk` |
| **Route name** | `admin.shipping-zones.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `shipping-zones.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted shipping zones no longer appear.

---

## 05. Branches

#### List All Branches

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.branches.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **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 — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branches retrieved successfully.",
    "data": {
        "data": [
            {
                "id": 1,
                "company_id": 2,
                "company_name": "Keaton Schuppe",
                "name": "Downtown Branch",
                "phone": "0221234567",
                "address_text": "12 Tahrir Street, Cairo",
                "latitude": "30.044420",
                "longitude": "31.235712",
                "working_hours": [
                    {
                        "to": "17:00",
                        "day": "sunday",
                        "from": "09:00"
                    }
                ],
                "is_active": true,
                "created_at": "2026-08-20 12:09:42"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/branches?page=1",
            "last": "http://localhost/api/v1/admin/branches?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/admin/branches?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/branches",
            "per_page": 15,
            "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:** Use `show` on a specific id for full details.

---

#### List All Branches

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

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

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

**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 — authenticated, lists own branches }*
```json
{
    "status": "success",
    "message": "Branches retrieved",
    "data": {
        "data": [
            {
                "id": 1,
                "name": "Downtown Branch",
                "phone": "1122233344",
                "address_text": "123 Main St, Downtown",
                "latitude": 24.7136,
                "longitude": 46.6753,
                "allowed_slots": [
                    "morning",
                    "afternoon"
                ],
                "working_hours": [
                    {
                        "day": "sunday",
                        "day_label": "Sunday",
                        "from": "09:00",
                        "to": "17:00"
                    },
                    {
                        "day": "monday",
                        "day_label": "Monday",
                        "from": "09:00",
                        "to": "17:00"
                    }
                ],
                "is_active": true,
                "created_at": "2026-08-20 11:20:50"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/company/branches?page=1",
            "last": "http://localhost/api/v1/company/branches?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/company/branches?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/company/branches",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - no token }*
```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 Branch Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/:branch` |
| **Route name** | `admin.branches.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `branch` — Branch ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branch retrieved successfully.",
    "data": {
        "id": 1,
        "company_id": 2,
        "company_name": "Keaton Schuppe",
        "name": "Downtown Branch",
        "phone": "0221234567",
        "address_text": "12 Tahrir Street, Cairo",
        "latitude": "30.044420",
        "longitude": "31.235712",
        "working_hours": [
            {
                "to": "17:00",
                "day": "sunday",
                "from": "09:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 12:09:42",
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "contacts": [],
        "stats": {
            "total_orders": 0,
            "active_contracts": 0
        },
        "updated_at": "2026-08-20 12:09:42"
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Branch Details

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

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

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

**Path Parameters:**
- `branch` — Branch ID — chained from an earlier request in this flow ({{branch_id}})

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

**Actual Success Response(s):**

*{ 200 OK — own branch }*
```json
{
    "status": "success",
    "message": "Branch retrieved",
    "data": {
        "id": 1,
        "name": "Downtown Branch",
        "phone": "1122233344",
        "address_text": "123 Main St, Downtown",
        "latitude": 24.7136,
        "longitude": 46.6753,
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "working_hours": [
            {
                "day": "sunday",
                "day_label": "Sunday",
                "from": "09:00",
                "to": "17:00"
            },
            {
                "day": "monday",
                "day_label": "Monday",
                "from": "09:00",
                "to": "17:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 11:20:50"
    }
}
```

**Actual Error Response(s):**

*{ 404 Not Found — branch belongs to a different company }*
```json
{
    "status": "fail",
    "message": "Resource not found",
    "data": null
}
```

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

---

#### Create New Branch

**What this does:** Creates a new branch.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches` |
| **Route name** | `admin.branches.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `company_id`
- **Description:** Company
- **Type:** `file`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|integer|exists:company_profiles,user_id`
- `required` — must be provided.
- `integer` — must be a whole number.
- `exists:company_profiles,user_id` — must reference an existing record (company_profiles, user_id).
### `name`
- **Description:** Branch Name
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:150`
- `required` — must be provided.
- `string` — must be text.
- `max:150` — must not exceed 150.
### `phone`
- **Description:** Branch Phone
- **Type:** `string`
- **Required:** required
- **Example:** `1234567890`
- **Validation rules:** `required|string|max:20`
- `required` — must be provided.
- `string` — must be text.
- `max:20` — must not exceed 20.
### `address_text`
- **Description:** Address
- **Type:** `string`
- **Required:** required
- **Example:** `123 Main Street, Building A`
- **Validation rules:** `required|string|max:500`
- `required` — must be provided.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `latitude`
- **Description:** Latitude
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|between:-90,90`
- `required` — must be provided.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `longitude`
- **Description:** Longitude
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|between:-180,180`
- `required` — must be provided.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `working_hours`
- **Description:** Working Hours
- **Type:** `array`
- **Required:** optional
- **Example:** `[{"day":"sunday","from":"09:00","to":"09:00"}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `working_hours.*.day`
- **Description:** Day
- **Type:** `string`
- **Required:** required
- **Example:** `sunday`
- **Validation rules:** `required|distinct|in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"`
- `required` — must be provided.
- `distinct` — custom validation rule.
- `in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"` — must be one of: "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday".
### `working_hours.*.from`
- **Description:** From
- **Type:** `string`
- **Required:** required
- **Example:** `09:00`
- **Validation rules:** `required|date_format:H:i`
- `required` — must be provided.
- `date_format:H:i` — custom validation rule.
### `working_hours.*.to`
- **Description:** To
- **Type:** `string`
- **Required:** required
- **Example:** `09:00`
- **Validation rules:** `required|date_format:H:i`
- `required` — must be provided.
- `date_format:H:i` — custom validation rule.
### `allowed_slots`
- **Description:** Allowed Slots
- **Type:** `array`
- **Required:** optional
- **Example:** `["morning"]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `allowed_slots.*`
- **Description:** Allowed slots item
- **Type:** `string`
- **Required:** optional
- **Example:** `morning`
- **Validation rules:** `string|in:morning,afternoon,evening`
- `string` — must be text.
- `in:morning,afternoon,evening` — must be one of: morning, afternoon, evening.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.

**Example Request Body:**
```json
{
    "company_id": 10,
    "name": "Downtown Branch",
    "phone": "0221234567",
    "address_text": "12 Tahrir Street, Cairo",
    "latitude": 30.04442,
    "longitude": 31.235712,
    "working_hours": [
        {
            "day": "sunday",
            "from": "09:00",
            "to": "17:00"
        }
    ],
    "allowed_slots": [
        "morning",
        "afternoon"
    ],
    "is_active": true
}
```

**Actual Success Response(s):**

*{ 201 Created — 201 - OK }*
```json
{
    "status": "success",
    "message": "Branch created successfully.",
    "data": {
        "id": 1,
        "company_id": 2,
        "company_name": "Keaton Schuppe",
        "name": "Downtown Branch",
        "phone": "0221234567",
        "address_text": "12 Tahrir Street, Cairo",
        "latitude": "30.044420",
        "longitude": "31.235712",
        "working_hours": [
            {
                "day": "sunday",
                "from": "09:00",
                "to": "17:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 12:09:42",
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "contacts": [],
        "stats": {
            "total_orders": 0,
            "active_contracts": 0
        },
        "updated_at": "2026-08-20 12:09:42"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this branch.

---

#### Create New Branch

**What this does:** Creates a new branch.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/company/branches` |
| **Route name** | `company.branches.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `name`
- **Description:** Branch Name
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:150`
- `required` — must be provided.
- `string` — must be text.
- `max:150` — must not exceed 150.
### `phone`
- **Description:** Branch Phone
- **Type:** `string`
- **Required:** required
- **Example:** `1234567890`
- **Validation rules:** `required|string|max:20`
- `required` — must be provided.
- `string` — must be text.
- `max:20` — must not exceed 20.
### `address_text`
- **Description:** Address
- **Type:** `string`
- **Required:** required
- **Example:** `123 Main Street, Building A`
- **Validation rules:** `required|string|max:500`
- `required` — must be provided.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `latitude`
- **Description:** Latitude
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|between:-90,90`
- `required` — must be provided.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `longitude`
- **Description:** Longitude
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric|between:-180,180`
- `required` — must be provided.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `allowed_slots`
- **Description:** Available Visit Slots
- **Type:** `array`
- **Required:** optional
- **Example:** `["morning"]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `allowed_slots.*`
- **Description:** Allowed slots item
- **Type:** `string`
- **Required:** optional
- **Example:** `morning`
- **Validation rules:** `string|in:morning,afternoon,evening`
- `string` — must be text.
- `in:morning,afternoon,evening` — must be one of: morning, afternoon, evening.
### `working_hours`
- **Description:** Working Hours
- **Type:** `array`
- **Required:** optional
- **Example:** `[{"day":"sunday","from":"09:00","to":"09:00"}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `working_hours.*.day`
- **Description:** Day
- **Type:** `string`
- **Required:** required
- **Example:** `sunday`
- **Validation rules:** `required|distinct|in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"`
- `required` — must be provided.
- `distinct` — custom validation rule.
- `in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"` — must be one of: "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday".
### `working_hours.*.from`
- **Description:** From
- **Type:** `string`
- **Required:** required
- **Example:** `09:00`
- **Validation rules:** `required|date_format:H:i`
- `required` — must be provided.
- `date_format:H:i` — custom validation rule.
### `working_hours.*.to`
- **Description:** To
- **Type:** `string`
- **Required:** required
- **Example:** `09:00`
- **Validation rules:** `required|date_format:H:i`
- `required` — must be provided.
- `date_format:H:i` — custom validation rule.

**Example Request Body:**
```json
{
    "name": "Downtown Branch",
    "phone": "1122233344",
    "address_text": "123 Main St, Downtown",
    "latitude": 24.7136,
    "longitude": 46.6753,
    "allowed_slots": [
        "morning",
        "afternoon"
    ],
    "working_hours": [
        {
            "day": "sunday",
            "from": "09:00",
            "to": "17:00"
        },
        {
            "day": "monday",
            "from": "09:00",
            "to": "17:00"
        }
    ]
}
```

**Actual Success Response(s):**

*{ 201 Created — valid branch }*
```json
{
    "status": "success",
    "message": "Branch created",
    "data": {
        "id": 1,
        "name": "Downtown Branch",
        "phone": "1122233344",
        "address_text": "123 Main St, Downtown",
        "latitude": 24.7136,
        "longitude": 46.6753,
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "working_hours": [
            {
                "day": "sunday",
                "day_label": "Sunday",
                "from": "09:00",
                "to": "17:00"
            },
            {
                "day": "monday",
                "day_label": "Monday",
                "from": "09:00",
                "to": "17:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 11:20:50"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - missing required fields }*
```json
{
    "status": "fail",
    "message": "Please enter your phone number.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this branch.

---

#### Update Branch

**What this does:** Updates an existing branch.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/:branch` |
| **Route name** | `admin.branches.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `branch` — Branch ID — seeded example: `1`

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

**Field documentation:**
### `company_id`
- **Description:** Company
- **Type:** `file`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|integer|exists:company_profiles,user_id`
- `sometimes` — validated only when the field is present.
- `integer` — must be a whole number.
- `exists:company_profiles,user_id` — must reference an existing record (company_profiles, user_id).
### `name`
- **Description:** Branch 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.
### `phone`
- **Description:** Branch Phone
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `sometimes|string|max:20`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:20` — must not exceed 20.
### `address_text`
- **Description:** Address
- **Type:** `string`
- **Required:** optional
- **Example:** `123 Main Street, Building A`
- **Validation rules:** `sometimes|string|max:500`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `latitude`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric|between:-90,90`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `longitude`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric|between:-180,180`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `working_hours`
- **Description:** Working Hours
- **Type:** `array`
- **Required:** optional
- **Example:** `[{"day":"sunday","from":"09:00","to":"09:00"}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `working_hours.*.day`
- **Description:** Day
- **Type:** `string`
- **Required:** optional
- **Example:** `sunday`
- **Validation rules:** `sometimes|distinct|in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"`
- `sometimes` — validated only when the field is present.
- `distinct` — custom validation rule.
- `in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"` — must be one of: "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday".
### `working_hours.*.from`
- **Description:** From
- **Type:** `string`
- **Required:** optional
- **Example:** `09:00`
- **Validation rules:** `sometimes|date_format:H:i`
- `sometimes` — validated only when the field is present.
- `date_format:H:i` — custom validation rule.
### `working_hours.*.to`
- **Description:** To
- **Type:** `string`
- **Required:** optional
- **Example:** `09:00`
- **Validation rules:** `sometimes|date_format:H:i`
- `sometimes` — validated only when the field is present.
- `date_format:H:i` — custom validation rule.
### `allowed_slots`
- **Description:** Allowed Slots
- **Type:** `array`
- **Required:** optional
- **Example:** `["morning"]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `allowed_slots.*`
- **Description:** Allowed slots item
- **Type:** `string`
- **Required:** optional
- **Example:** `morning`
- **Validation rules:** `string|in:morning,afternoon,evening`
- `string` — must be text.
- `in:morning,afternoon,evening` — must be one of: morning, afternoon, evening.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.

**Example Request Body:**
```json
{
    "name": "Downtown Branch (Renamed)",
    "phone": "0221234599"
}
```

**Actual Success Response(s):**

*{ 200 OK — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branch updated successfully.",
    "data": {
        "id": 1,
        "company_id": 2,
        "company_name": "Keaton Schuppe",
        "name": "Downtown Branch (Renamed)",
        "phone": "0221234599",
        "address_text": "12 Tahrir Street, Cairo",
        "latitude": "30.044420",
        "longitude": "31.235712",
        "working_hours": [
            {
                "to": "17:00",
                "day": "sunday",
                "from": "09:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 12:09:42",
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "contacts": [],
        "stats": {
            "total_orders": 0,
            "active_contracts": 0
        },
        "updated_at": "2026-08-20 12:09:42"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Branch

**What this does:** Updates an existing branch.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/company/branches/:branch` |
| **Route name** | `company.branches.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `branch` — Branch ID — chained from an earlier request in this flow ({{branch_id}})

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

**Field documentation:**
### `name`
- **Description:** Branch 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.
### `phone`
- **Description:** Branch Phone
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `sometimes|string|max:20`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:20` — must not exceed 20.
### `address_text`
- **Description:** Address
- **Type:** `string`
- **Required:** optional
- **Example:** `123 Main Street, Building A`
- **Validation rules:** `sometimes|string|max:500`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `latitude`
- **Description:** Latitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric|between:-90,90`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
- `between:-90,90` — must be between -90, 90.
### `longitude`
- **Description:** Longitude
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric|between:-180,180`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
- `between:-180,180` — must be between -180, 180.
### `allowed_slots`
- **Description:** Available Visit Slots
- **Type:** `array`
- **Required:** optional
- **Example:** `["morning"]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `allowed_slots.*`
- **Description:** Allowed slots item
- **Type:** `string`
- **Required:** optional
- **Example:** `morning`
- **Validation rules:** `string|in:morning,afternoon,evening`
- `string` — must be text.
- `in:morning,afternoon,evening` — must be one of: morning, afternoon, evening.
### `working_hours`
- **Description:** Working Hours
- **Type:** `array`
- **Required:** optional
- **Example:** `[{"day":"sunday","from":"09:00","to":"09:00"}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null.
- `array` — must be an array.
### `working_hours.*.day`
- **Description:** Day
- **Type:** `string`
- **Required:** optional
- **Example:** `sunday`
- **Validation rules:** `sometimes|distinct|in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"`
- `sometimes` — validated only when the field is present.
- `distinct` — custom validation rule.
- `in:"sunday","monday","tuesday","wednesday","thursday","friday","saturday"` — must be one of: "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday".
### `working_hours.*.from`
- **Description:** From
- **Type:** `string`
- **Required:** optional
- **Example:** `09:00`
- **Validation rules:** `sometimes|date_format:H:i`
- `sometimes` — validated only when the field is present.
- `date_format:H:i` — custom validation rule.
### `working_hours.*.to`
- **Description:** To
- **Type:** `string`
- **Required:** optional
- **Example:** `09:00`
- **Validation rules:** `sometimes|date_format:H:i`
- `sometimes` — validated only when the field is present.
- `date_format:H:i` — custom validation rule.

**Example Request Body:**
```json
{
    "name": "Downtown Branch (Renamed)",
    "phone": "1122233355"
}
```

**Actual Success Response(s):**

*{ 200 OK — valid update }*
```json
{
    "status": "success",
    "message": "Branch updated",
    "data": {
        "id": 1,
        "name": "Downtown Branch (Renamed)",
        "phone": "1122233355",
        "address_text": "123 Main St, Downtown",
        "latitude": 24.7136,
        "longitude": 46.6753,
        "allowed_slots": [
            "morning",
            "afternoon"
        ],
        "working_hours": [
            {
                "day": "sunday",
                "day_label": "Sunday",
                "from": "09:00",
                "to": "17:00"
            },
            {
                "day": "monday",
                "day_label": "Monday",
                "from": "09:00",
                "to": "17:00"
            }
        ],
        "is_active": true,
        "created_at": "2026-08-20 11:20:50"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Fetch `show` to confirm the change.

---

#### Contacts Branch

**What this does:** Replaces a branch's contact-person list (name/phone/email/is_primary per contact).

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/:id/contacts` |
| **Route name** | `admin.branches.contacts` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.contacts` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

**Payload:**
Example payload is included in the request body. No FormRequest validation rules were detected.
### `contacts.0.name`
- **Description:** Contacts 0 name
- **Type:** `string`
- **Required:** optional
- **Example:** `Jane Manager`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.
### `contacts.0.phone`
- **Description:** Contacts 0 phone
- **Type:** `string`
- **Required:** optional
- **Example:** `1234567890`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.
### `contacts.0.email`
- **Description:** Contacts 0 email
- **Type:** `string`
- **Required:** optional
- **Example:** `branch-contact@example.com`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.
### `contacts.0.is_primary`
- **Description:** Contacts 0 is primary
- **Type:** `string`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `not specified`
- `not specified` — no validation rule was declared.

**Example Request Body:**
```json
{
    "contacts": [
        {
            "name": "John Manager",
            "phone": "0100000000",
            "email": "john@branch.test",
            "is_primary": true
        },
        {
            "name": "Jane Assistant",
            "phone": "0100000001",
            "email": "jane@branch.test",
            "is_primary": false
        }
    ]
}
```

**Actual Success Response(s):**

*{ 200 OK — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branch contacts updated successfully.",
    "data": [
        {
            "id": 1,
            "branch_id": 1,
            "name": "John Manager",
            "phone": "0100000000",
            "email": "john@branch.test",
            "is_primary": true,
            "created_at": "2026-08-20T12:09:42.000000Z",
            "updated_at": "2026-08-20T12:09:42.000000Z"
        },
        {
            "id": 2,
            "branch_id": 1,
            "name": "Jane Assistant",
            "phone": "0100000001",
            "email": "jane@branch.test",
            "is_primary": false,
            "created_at": "2026-08-20T12:09:42.000000Z",
            "updated_at": "2026-08-20T12:09:42.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

**What happens next:** Fetch `GET /admin/branches/{id}` to confirm the new contact list.

---

#### Toggle Active Branch

**What this does:** Activates/deactivates a branch.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/:id/toggle-active` |
| **Route name** | `admin.branches.toggle-active` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.toggle-active` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

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

**Actual Success Response(s):**

*{ 200 OK — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branch deactivated successfully.",
    "data": {
        "id": 1,
        "is_active": false
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** An inactive branch should not be selectable as `branch_id` on new company job orders/contracts going forward (verify against `Branch` model scopes if enforcing this client-side).

---

#### Delete Branch

**What this does:** Permanently deletes a branch.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/:branch` |
| **Route name** | `admin.branches.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `branch` — Branch ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — 200 - OK }*
```json
{
    "status": "success",
    "message": "Branch deleted successfully.",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Branch

**What this does:** Permanently deletes a branch.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/company/branches/:branch` |
| **Route name** | `company.branches.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **Status codes** | `200 OK`, `404 Not Found`, `422 Unprocessable Entity` |

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

**Path Parameters:**
- `branch` — Branch ID — chained from an earlier request in this flow ({{branch_id}})

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

**Actual Success Response(s):**

*{ 200 OK — deletable branch }*
```json
{
    "status": "success",
    "message": "Branch deleted",
    "data": null
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Error - branch has scheduled visits (BR-12) }*
```json
{
    "status": "fail",
    "message": "Branch cannot be deleted — it has scheduled visits.",
    "data": null
}
```

**What happens next:** It no longer appears in `index`.

---

#### Delete Bulk

**What this does:** Deletes multiple branches in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/branches/bulk` |
| **Route name** | `admin.branches.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `branches.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found`, `422 Unprocessable Entity` |

**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 — 200 - OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 - Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted branches no longer appear.

---

## 06. Categories & Services (Catalog)

#### List All Categories

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.categories.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "categories": [
            {
                "id": 2,
                "name": "Plumbing",
                "description": null,
                "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
                "parent_id": null,
                "is_active": true,
                "sort_order": 1,
                "parent": null,
                "has_children": false,
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/categories?page=1",
            "last": "http://localhost/api/v1/admin/categories?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/admin/categories?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/categories",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Services

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.services.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "categories": [
            {
                "id": 8,
                "name": "Pipe Fixing",
                "description": null,
                "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
                "parent_id": 7,
                "parent": {
                    "id": 7,
                    "name": "Plumbing"
                },
                "is_active": true,
                "sort_order": 1,
                "price": "50.00",
                "contract_price": "45.00",
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/services?page=1",
            "last": "http://localhost/api/v1/admin/services?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/admin/services?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/services",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Categories

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

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

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

**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 — authenticated, lists top-level categories }*
```json
{
    "status": "success",
    "message": "Categories retrieved successfully.",
    "data": [
        {
            "id": 30,
            "name": "Plumbing",
            "description": "Pipe and water repairs",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "visit_price": null,
            "sort_order": 1,
            "created_at": "2026-08-20T11:20:50.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - no token }*
```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 Category Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories/:category` |
| **Route name** | `admin.categories.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `category` — Category ID — seeded example: `2`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 3,
        "name": "Electrical",
        "description": null,
        "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
        "parent_id": null,
        "is_active": true,
        "sort_order": 2,
        "parent": null,
        "has_children": false,
        "created_at": "2026-08-20 12:09:43",
        "children": [],
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Electrical",
            "description": null
        },
        "ar": {
            "name": "كهرباء",
            "description": null
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Service Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services/:service` |
| **Route name** | `admin.services.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `service` — Service ID — seeded example: `2`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 9,
        "name": "Leak Detection",
        "description": null,
        "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
        "parent_id": 7,
        "parent": {
            "id": 7,
            "name": "Plumbing"
        },
        "is_active": true,
        "sort_order": 2,
        "price": "80.00",
        "contract_price": "70.00",
        "created_at": "2026-08-20 12:09:43",
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "name": "Leak Detection",
            "description": null
        },
        "ar": {
            "name": "كشف التسريبات",
            "description": null
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Create New Category

**What this does:** Creates a new category.

> **`price` / `contract_price` are not accepted here.** Top-level categories don't carry a price — only the services under them do (see **Create New Service** below, where both are `required`). Sending `price`/`contract_price` in this request is silently ignored (stripped in `CategoryService::beforeSave()` before the row is saved); use `visit_price`/`visit_contract_price` instead if you also want an auto-created "Visit" service (`with_visit: true`). The response still includes `price`/`contract_price` keys for schema stability — they'll simply always be `null`/`0` on a category row.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories` |
| **Route name** | `admin.categories.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `parent_id`
- **Description:** Parent Category
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `prohibited`
- `prohibited` — custom validation rule.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `with_visit`
- **Description:** With Visit
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `nullable|boolean`
- `nullable` — may be null.
- `boolean` — must be true, false, 1, or 0.
- **Not a stored column — it's create-only trigger logic.** `with_visit: true` on a **category create** (`POST`, ignored on update) auto-creates a **child** category named "Visit"/"زيارة" under the new category, with `is_visit: true`, priced from `visit_price`/`visit_contract_price` below. That `is_visit` flag on the child then enforces a cart rule elsewhere in the API: a service marked `is_visit` must be the only item in the customer's cart — it can't be combined with other services in the same order (see `CartCommandService`). This field itself is stripped before the parent category row is saved, so it never appears in category responses.
### `visit_price`
- **Description:** Visit Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric`
- `nullable` — may be null.
- `numeric` — must be numeric.
- Only used when `with_visit: true` is also sent — becomes the auto-created visit service's `price`. Ignored otherwise.
### `visit_contract_price`
- **Description:** Visit Contract Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric`
- `nullable` — may be null.
- `numeric` — must be numeric.
- Only used when `with_visit: true` is also sent — becomes the auto-created visit service's `contract_price`. Ignored otherwise.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "is_active": true,
    "sort_order": 2,
    "en": {
        "name": "Electrical"
    },
    "ar": {
        "name": "كهرباء"
    }
}
```

**Example Response** *(field values illustrative — `price`/`contract_price` reflect the 2026-08-26 fix that stops categories from accepting them; not re-captured from a live run)*:

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "parent_id": null,
        "parent_info": null,
        "image_path": null,
        "is_active": true,
        "is_visible": true,
        "sort_order": 2,
        "price": null,
        "contract_price": null,
        "is_visit": false,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "deleted_at": null,
        "name": "Electrical",
        "description": null
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "The Name (en) field is required.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this category.

---

#### Create New Service

**What this does:** Creates a new service.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services` |
| **Route name** | `admin.services.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `parent_id`
- **Description:** Category
- **Type:** `integer`
- **Required:** required
- **Example:** `1`
- **Validation rules:** `required|exists:categories,id,parent_id,"NULL"`
- `required` — must be provided.
- `exists:categories,id,parent_id,"NULL"` — must reference an existing record (categories, id, parent_id, "NULL").
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `price`
- **Description:** Price
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric`
- `required` — must be provided.
- `numeric` — must be numeric.
### `contract_price`
- **Description:** Contract Price
- **Type:** `number`
- **Required:** required
- **Example:** `10`
- **Validation rules:** `required|numeric`
- `required` — must be provided.
- `numeric` — must be numeric.
### `is_visit`
- **Description:** Is Visit
- **Type:** `boolean`
- **Required:** optional
- **Example:** `false`
- **Validation rules:** `nullable|boolean`
- `nullable` — may be null.
- `boolean` — must be true, false, 1, or 0.
- Marks this service as a **visit-only service** (e.g. a base diagnostic/assessment visit fee). A service with `is_visit: true` must be the sole item in a customer's cart — it cannot be added alongside other services, and no other service can be added once a visit service is already in the cart (enforced by `CartCommandService`, not by this endpoint). Normally set automatically when a category is created with `with_visit: true` (see Categories §Create New Category), but can also be set directly here on any service.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Name`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "parent_id": 7,
    "is_active": true,
    "sort_order": 2,
    "price": 80,
    "contract_price": 70,
    "is_visit": false,
    "en": {
        "name": "Leak Detection"
    },
    "ar": {
        "name": "كشف التسريبات"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 9,
        "parent_id": 7,
        "parent_info": null,
        "image_path": null,
        "is_active": true,
        "is_visible": true,
        "sort_order": 2,
        "price": "80.00",
        "contract_price": "70.00",
        "is_visit": false,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "deleted_at": null,
        "name": "Leak Detection",
        "description": null
    }
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Invalid Parent }*
```json
{
    "status": "fail",
    "message": "The selected parent must be a top-level category.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this service.

---

#### Update Category

**What this does:** Updates an existing category.

> **`price` / `contract_price` are not accepted here** — see the note on **Create New Category** above; the same restriction applies on update.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories/:category` |
| **Route name** | `admin.categories.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `category` — Category ID — seeded example: `2`

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

**Field documentation:**
### `parent_id`
- **Description:** Parent Category
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `prohibited`
- `prohibited` — custom validation rule.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `with_visit`
- **Description:** With Visit
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `nullable|boolean`
- `nullable` — may be null.
- `boolean` — must be true, false, 1, or 0.
- **Accepted but silently ignored on update** — the auto-create-a-visit-service mechanic (see Create New Category above) only runs on `POST`/create. Sending `with_visit`/`visit_price`/`visit_contract_price` to this `PUT` endpoint has no effect.
### `visit_price`
- **Description:** Visit Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric`
- `nullable` — may be null.
- `numeric` — must be numeric.
- Ignored on update — see `with_visit` above.
### `visit_contract_price`
- **Description:** Visit Contract Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `nullable|numeric`
- `nullable` — may be null.
- `numeric` — must be numeric.
- Ignored on update — see `with_visit` above.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "en": {
        "name": "Electrical Updated"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "parent_id": null,
        "parent_info": null,
        "image_path": null,
        "is_active": true,
        "is_visible": true,
        "sort_order": 2,
        "price": "100.00",
        "contract_price": "90.00",
        "is_visit": false,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "deleted_at": null,
        "name": "Electrical Updated",
        "description": null
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Service

**What this does:** Updates an existing service.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services/:service` |
| **Route name** | `admin.services.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `service` — Service ID — seeded example: `2`

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

**Field documentation:**
### `parent_id`
- **Description:** Category
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|exists:categories,id,parent_id,"NULL"`
- `sometimes` — validated only when the field is present.
- `exists:categories,id,parent_id,"NULL"` — must reference an existing record (categories, id, parent_id, "NULL").
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `price`
- **Description:** Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
### `contract_price`
- **Description:** Contract Price
- **Type:** `number`
- **Required:** optional
- **Example:** `10`
- **Validation rules:** `sometimes|numeric`
- `sometimes` — validated only when the field is present.
- `numeric` — must be numeric.
### `is_visit`
- **Description:** Is Visit
- **Type:** `boolean`
- **Required:** optional
- **Example:** `false`
- **Validation rules:** `nullable|boolean`
- `nullable` — may be null.
- `boolean` — must be true, false, 1, or 0.
- Marks this service as a **visit-only service** — see `is_visit` under Create New Service above for the single-item-cart rule it enforces.
### `en.name`
- **Description:** Name (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.name`
- **Description:** Name (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Name`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.

**Example Request Body:**
```json
{
    "price": 80,
    "contract_price": 70,
    "en": {
        "name": "Leak Detection Updated"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 9,
        "parent_id": 7,
        "parent_info": null,
        "image_path": null,
        "is_active": true,
        "is_visible": true,
        "sort_order": 2,
        "price": "80.00",
        "contract_price": "70.00",
        "is_visit": false,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "deleted_at": null,
        "name": "Leak Detection Updated",
        "description": null
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### List Service Categories

**What this does:** Returns top-level bookable service categories.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/categories` |
| **Route name** | `client.categories.categories` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **Status codes** | `200 OK` |

**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 — live capture }*
```json
{
    "status": "success",
    "message": "Services retrieved successfully",
    "data": [
        {
            "id": 1,
            "name": "\u0627\u0644\u0633\u0628\u0627\u0643\u0629",
            "description": "\u0625\u0635\u0644\u0627\u062d \u0627\u0644\u0645\u0648\u0627\u0633\u064a\u0631 \u0648\u0627\u0644\u0635\u0631\u0641 \u0648\u062a\u0631\u0643\u064a\u0628\u0627\u062a \u0627\u0644\u0645\u064a\u0627\u0647",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 1,
            "created_at": "2026-08-20T18:37:20.000000Z"
        },
        {
            "id": 6,
            "name": "\u0627\u0644\u0643\u0647\u0631\u0628\u0627\u0621",
            "description": "\u0627\u0644\u0623\u0633\u0644\u0627\u0643 \u0648\u0627\u0644\u0645\u0642\u0627\u0628\u0633 \u0648\u0627\u0644\u0625\u0635\u0644\u0627\u062d\u0627\u062a \u0627\u0644\u0643\u0647\u0631\u0628\u0627\u0626\u064a\u0629",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 2,
            "created_at": "2026-08-20T18:37:20.000000Z"
        },
        {
            "id": 11,
            "name": "\u0627\u0644\u062a\u0643\u064a\u064a\u0641 \u0648\u0627\u0644\u062a\u0628\u0631\u064a\u062f",
            "description": "\u062a\u0631\u0643\u064a\u0628 \u0648\u0635\u064a\u0627\u0646\u0629 \u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u062a\u0643\u064a\u064a\u0641",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 3,
            "created_at": "2026-08-20T18:37:21.000000Z"
        },
        {
            "id": 16,
            "name": "\u0625\u0635\u0644\u0627\u062d \u0627\u0644\u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u0645\u0646\u0632\u0644\u064a\u0629",
            "description": "\u062e\u062f\u0645\u0627\u062a \u0625\u0635\u0644\u0627\u062d \u0627\u0644\u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u0645\u0646\u0632\u0644\u064a\u0629",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 4,
            "created_at": "2026-08-20T18:37:21.000000Z"
        },
        {
            "id": 20,
            "name": "\u0627\u0644\u062f\u0647\u0627\u0646\u0627\u062a \u0648\u0627\u0644\u0646\u062c\u0627\u0631\u0629",
            "description": "\u062e\u062f\u0645\u0627\u062a \u0627\u0644\u062f\u0647\u0627\u0646 \u0648\u0627\u0644\u0623\u0639\u0645\u0627\u0644 \u0627\u0644\u062e\u0634\u0628\u064a\u0629",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 5,
            "created_at": "2026-08-20T18:37:21.000000Z"
        }
    ]
}

```

**What happens next:** Drill into a category's bookable services with `client.categories.services`.

---

#### List Category Services

**What this does:** Returns bookable child services (with price) under one parent category.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/categories/:id/services` |
| **Route name** | `client.categories.services` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **Status codes** | `200 OK`, `401 Unauthorized`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — list active services under a category }*
```json
{
    "status": "success",
    "message": "Services retrieved successfully",
    "data": [
        {
            "id": 27,
            "parent_id": 26,
            "name": "Wiring Repair",
            "description": "Fix wiring",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 1,
            "parent": {
                "id": 26,
                "name": "Electrical"
            },
            "created_at": "2026-08-20T10:51:53.000000Z"
        },
        {
            "id": 28,
            "parent_id": 26,
            "name": "Socket Installation",
            "description": "Install sockets",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 2,
            "parent": {
                "id": 26,
                "name": "Electrical"
            },
            "created_at": "2026-08-20T10:51:53.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

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

**What happens next:** Add a service to the cart with `POST /client/cart/items` using its `id` as `category_id`.

---

#### List Category Services

**What this does:** Returns bookable child services under one parent category, for company job-order use.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/company/categories/:id/services` |
| **Route name** | `company.categories.services` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — lists services under category }*
```json
{
    "status": "success",
    "message": "Category services retrieved successfully.",
    "data": [
        {
            "id": 31,
            "parent_id": 30,
            "name": "Pipe Leak Repair",
            "description": "Fixing leaking pipes",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": "150.00",
            "sort_order": 1,
            "created_at": "2026-08-20T11:20:50.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

**What happens next:** Add to cart (`POST /company/cart/items`) or reference in a contract's `service_ids`.

---

#### Delete Category

**What this does:** Permanently deletes a category.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories/:category` |
| **Route name** | `admin.categories.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `category` — Category ID — seeded example: `2`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Service

**What this does:** Permanently deletes a service.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services/:service` |
| **Route name** | `admin.services.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `service` — Service ID — seeded example: `2`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Bulk

**What this does:** Deletes multiple categories in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/categories/bulk` |
| **Route name** | `admin.categories.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `categories.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted categories no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple services in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/services/bulk` |
| **Route name** | `admin.services.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `services.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted services no longer appear.

---

## 07. General Settings & Feature Flags

#### List All Feature flags

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.feature-flags.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "feature_flags": [
            {
                "id": 1,
                "key": "new_checkout_flow",
                "is_enabled": false,
                "targeting_rules": [
                    {"type": "country", "values": ["SA", "AE"]}
                ],
                "description": "Rolls out the redesigned checkout flow.",
                "created_at": "2026-08-20 12:10:31"
            },
            {
                "id": 2,
                "key": "wallet_top_up",
                "is_enabled": true,
                "targeting_rules": null,
                "description": "Enables wallet top-up via gateway.",
                "created_at": "2026-08-20 12:10:31"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/feature-flags?page=1",
            "last": "http://localhost/api/v1/admin/feature-flags?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/admin/feature-flags?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/feature-flags",
            "per_page": 15,
            "to": 2,
            "total": 2
        }
    }
}
```

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

---

#### List All Settings

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/settings?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.settings.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `settings.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**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 settings }*
```json
{
    "status": "success",
    "message": "Settings retrieved",
    "data": {
        "app": [
            {
                "id": 1,
                "key": "app_name",
                "type": "string",
                "value": "My App",
                "group": "app",
                "metadata": null,
                "created_at": "2026-08-20T12:09:45.000000Z",
                "updated_at": "2026-08-20T12:09:45.000000Z"
            }
        ],
        "contact": [
            {
                "id": 2,
                "key": "support_email",
                "type": "string",
                "value": "support@example.com",
                "group": "contact",
                "metadata": null,
                "created_at": "2026-08-20T12:09:45.000000Z",
                "updated_at": "2026-08-20T12:09:45.000000Z"
            }
        ]
    }
}
```

*{ 200 OK — list settings after update }*
```json
{
    "status": "success",
    "message": "Settings retrieved",
    "data": {
        "app": [
            {
                "id": 1,
                "key": "app_name",
                "type": "string",
                "value": "Maintenance Platform",
                "group": "app",
                "metadata": null,
                "created_at": "2026-08-20T12:09:45.000000Z",
                "updated_at": "2026-08-20T12:09:45.000000Z"
            }
        ],
        "contact": [
            {
                "id": 2,
                "key": "support_email",
                "type": "string",
                "value": "support@maintenance-platform.example.com",
                "group": "contact",
                "metadata": null,
                "created_at": "2026-08-20T12:09:45.000000Z",
                "updated_at": "2026-08-20T12:09:45.000000Z"
            }
        ]
    }
}
```

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

---

#### Create New Feature flag

**What this does:** Creates a new feature flag (`key`, `is_enabled`, optional `targeting_rules`/`description`).

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags` |
| **Route name** | `admin.feature-flags.create` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** required
- **Example:** `new_checkout_flow`
- **Validation rules:** `required|string|max:100|unique:feature_flags,key,NULL,id`
- `required` — must be provided.
- `string` — must be text.
- `max:100` — must not exceed 100.
- `unique:feature_flags,key,NULL,id` — must be unique (feature_flags, key, NULL, id).
### `is_enabled`
- **Description:** Is Enabled
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `targeting_rules`
- **Description:** Targeting Rules
- **Type:** `array` of rule objects
- **Required:** optional
- **Example:** `[{"type": "country", "values": ["EG", "SA"]}, {"type": "percentage", "value": 50, "seed": "checkout_v2"}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null (flag then always evaluates to `is_enabled` with no targeting).
- `array` — must be an array of rule objects, each shaped by its `type` (see `FlagTargetingEvaluator`):
  - `{"type": "country", "values": ["EG", "SA"]}` — passes if the resolved country (from the `X-Country` header, falling back to the user's `country_code`) is in `values`.
  - `{"type": "version", "min_version": "2.0.0"}` — passes if the `X-App-Version` header is `>=` `min_version`.
  - `{"type": "segment", "values": ["beta_testers"]}` — passes if the authenticated user's `segment` is in `values`.
  - `{"type": "percentage", "value": 50, "seed": "checkout_v2"}` — deterministic rollout: hashes `{user_id}:{seed}` into a 0–99 bucket and passes if it's below `value` (a `value: 50` rule always passes for the same ~50% of users, not a per-request coin flip). Omitting `seed` uses `"default"`, so unrelated flags reusing the default seed will bucket the same users together.
  - An unrecognized `type` (or a missing `type` key) passes automatically — it does not fail closed.
  - **All rules must pass (AND logic)** — the flag is enabled for a given request only if every rule in the array passes; one failing rule disables it regardless of the others.
### `description`
- **Description:** Description
- **Type:** `string`
- **Required:** optional
- **Example:** `Example content text`
- **Validation rules:** `nullable|string`
- `nullable` — may be null.
- `string` — must be text.

**Example Request Body:**
```json
{
    "key": "sms_notifications",
    "is_enabled": true,
    "targeting_rules": [
        {"type": "percentage", "value": 50, "seed": "sms_notifications"}
    ],
    "description": "Enables SMS notifications for order updates."
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 3,
        "key": "sms_notifications",
        "is_enabled": true,
        "targeting_rules": [
            {"type": "percentage", "value": 50, "seed": "sms_notifications"}
        ],
        "description": "Enables SMS notifications for order updates.",
        "created_at": "2026-08-20T12:10:31.000000Z",
        "updated_at": "2026-08-20T12:10:31.000000Z"
    }
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Validation Error - duplicate key }*
```json
{
    "status": "fail",
    "message": "The Key has already been taken.",
    "data": null
}
```

**What happens next:** Check it anywhere in the app via the `feature('key')` helper — flags gate optional behavior, not core flows documented here.

---

#### Update Feature flag

**What this does:** Updates an existing feature flag.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags/:id` |
| **Route name** | `admin.feature-flags.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found`, `422 Unprocessable Entity` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Field documentation:**
### `key`
- **Description:** Key
- **Type:** `string`
- **Required:** optional
- **Example:** `new_checkout_flow`
- **Validation rules:** `sometimes|string|max:100|unique:feature_flags,key,NULL,id`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:100` — must not exceed 100.
- `unique:feature_flags,key,NULL,id` — must be unique (feature_flags, key, NULL, id).
### `is_enabled`
- **Description:** Is Enabled
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `targeting_rules`
- **Description:** Targeting Rules
- **Type:** `array` of rule objects
- **Required:** optional
- **Example:** `[{"type": "country", "values": ["SA", "AE"]}]`
- **Validation rules:** `nullable|array`
- `nullable` — may be null (flag then always evaluates to `is_enabled` with no targeting).
- `array` — same rule-object schema as Create New Feature Flag above (`type: country|version|segment|percentage`) — see that section for the full per-type shape and evaluation semantics.

**Example Request Body:**
```json
{
    "description": "Rolls out the redesigned checkout flow (v2).",
    "is_enabled": true,
    "targeting_rules": [
        {"type": "country", "values": ["SA", "AE"]}
    ]
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 1,
        "key": "new_checkout_flow",
        "is_enabled": true,
        "targeting_rules": [
            {"type": "country", "values": ["SA", "AE"]}
        ],
        "description": "Rolls out the redesigned checkout flow (v2).",
        "created_at": "2026-08-20T12:10:31.000000Z",
        "updated_at": "2026-08-20T12:10:31.000000Z"
    }
}
```

**Actual Error Response(s):**

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

*{ 422 Unprocessable Entity — 422 Validation Error - duplicate key }*
```json
{
    "status": "fail",
    "message": "The Key has already been taken.",
    "data": null
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Setting

**What this does:** Updates an existing setting.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/settings` |
| **Route name** | `admin.settings.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `settings.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity`, `500 Internal Server Error` |

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

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

**Field documentation:**
### `settings`
- **Description:** Settings
- **Type:** `array`
- **Required:** optional
- **Example:** `[{"key":"value","value":"value"}]`
- **Validation rules:** `sometimes|array`
- `sometimes` — validated only when the field is present.
- `array` — must be an array.
### `settings.*.key`
- **Description:** Setting Key
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `sometimes|string|max:100`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:100` — must not exceed 100.
### `settings.*.value`
- **Description:** Setting Value
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string`
- `nullable` — may be null.
- `string` — must be text.

**Example Request Body:**
```json
{
    "settings": [
        {
            "key": "app_name",
            "value": "Maintenance Platform"
        },
        {
            "key": "support_email",
            "value": "support@maintenance-platform.example.com"
        }
    ]
}
```

**Actual Success Response(s):**

*{ 200 OK — bulk update existing settings }*
```json
{
    "status": "success",
    "message": "Settings updated successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error - missing setting key }*
```json
{
    "status": "fail",
    "message": "Each setting must have a key.",
    "data": null
}
```

*{ 422 Unprocessable Entity — 422 Validation Error - settings not an array }*
```json
{
    "status": "fail",
    "message": "Settings must be provided as an array.",
    "data": null
}
```

*{ 500 Internal Server Error — BUG: new key insert fails (group column has no default) }*
```json
{
    "status": "error",
    "message": "SQLSTATE[HY000]: General error: 1364 Field 'group' doesn't have a default value (Connection: mysql, Host: 127.0.0.1, Port: 3306, Database: maintenance_testing_ad_a, SQL: insert into `app_configs` (`key`, `value`, `updated_at`, `created_at`) values (brand_new_setting_key, some value, 2026-08-20 12:09:45, 2026-08-20 12:09:45))",
    "data": null
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Delete Feature flag

**What this does:** Permanently deletes a feature flag.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags/:id` |
| **Route name** | `admin.feature-flags.delete` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** Any `feature('key')` check for it now returns `false`.

---

#### Toggle Feature flag Status

**What this does:** Flips a feature flag on/off without touching its targeting rules.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags/:id/toggle` |
| **Route name** | `admin.feature-flags.toggle` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.toggle` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `id` — Id ID — seeded example: `1`

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

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Feature flag toggled",
    "data": {
        "id": 2,
        "key": "wallet_top_up",
        "is_enabled": false,
        "targeting_rules": null,
        "description": "Enables wallet top-up via gateway.",
        "created_at": "2026-08-20T12:10:31.000000Z",
        "updated_at": "2026-08-20T12:10:31.000000Z"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Takes effect immediately for any code path guarded by `feature('key')`.

---

#### Delete Bulk

**What this does:** Deletes multiple feature flags in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/feature-flags/bulk` |
| **Route name** | `admin.feature-flags.bulk.delete` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `feature-flags.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

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

**What happens next:** Re-fetch the index to confirm — deleted feature flags no longer appear.

---

## 08. Search

#### Search Services

**What this does:** Case-insensitive substring search over active, bookable services (`Category` rows under the `service()` scope, i.e. leaf categories with a price — not top-level categories) by translated `name` or `description`. Not a generic multi-entity search and not paginated — `SearchService::search()` runs an unpaginated `->get()` and reads only `search` (or `query` as a fallback param name); it does not accept `page`/`per_page`/`sort`/`paginate`/`filters[...]` even though those appear on other `index` endpoints in this API.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/search?search=pipe` |
| **Route name** | `client.search.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **Status codes** | `200 OK`, `401 Unauthorized` |

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

**Payload:**
No request body.

**Query parameters:**
- `search` — Search term, matched against translated service `name` and `description` (optional — `query` also works as a fallback param name, but `search` is the primary one)

**Actual Success Response(s):**

*{ 200 OK — search services by name/description }*
```json
{
    "status": "success",
    "message": "Search results retrieved successfully",
    "data": [
        {
            "id": 25,
            "parent_id": 24,
            "name": "Pipe Repair Plumbing",
            "description": "Fixing leaking pipes",
            "image": "https://ui-avatars.com/api/?name=C&background=fef3c7&color=d97706&size=200",
            "price": null,
            "sort_order": 1,
            "parent": {
                "id": 24,
                "name": "Plumbing Category"
            },
            "created_at": "2026-08-20T10:51:46.000000Z"
        }
    ]
}
```

*{ 200 OK — no matches found }*
```json
{
    "status": "success",
    "message": "Search results retrieved successfully",
    "data": []
}
```

**Actual Error Response(s):**

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

**What happens next:** Use the returned category `id` with `GET /client/categories/:id/services` (§06 Categories & Services) to view the full service, or add it straight to the cart via `POST /client/cart/items`.

---

## 09. Health & Status

#### Health Check

**What this does:** Infrastructure health probe — checks database, cache, queue, and storage connectivity/latency and reports an overall `healthy`/`degraded` status. Not a resource endpoint: no pagination, filtering, or search; response shape doesn't follow the standard `{status, message, data}` envelope (see below) since it predates/bypasses the `json()` helper (`HealthController::index()` calls `response()->json()` directly). Intended for load balancers, uptime monitors, and container orchestrators, not app clients.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/health` |
| **Route name** | `shared.health.index` |
| **Guard** | None — public endpoint |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` (healthy), `503 Service Unavailable` (any component check reports `down`) |

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

**Payload:**
No request body, no query parameters.

**Actual Success Response(s):**

*{ 200 OK — Example Response }*
```json
{
    "status": "healthy",
    "checks": {
        "database": {
            "status": "up",
            "latency_ms": 0.38
        },
        "cache": {
            "status": "up",
            "driver": "database",
            "latency_ms": 4.35
        },
        "queue": {
            "status": "up",
            "driver": "database"
        },
        "storage": {
            "status": "up",
            "driver": "local"
        }
    },
    "version": "1.0.0",
    "timestamp": "2026-08-19T21:35:04.385732Z"
}
```

**What happens next:** Nothing app-facing — this is a monitoring/probe endpoint, not part of any user-driven flow. A `degraded` response (any check `down`) is a signal for ops to investigate, not something a client app reacts to.

---

#### Bootstrap Config

**What this does:** Returns app-wide bootstrap configuration (feature flags, app settings needed before login) for client startup.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/config/bootstrap` |
| **Route name** | `shared.config.bootstrap` |
| **Guard** | None — public endpoint |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` |

**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 — Example Response }*
```json
{
    "status": "success",
    "message": "Bootstrap configuration retrieved",
    "data": {
        "app": {
            "min_version": "1.0.0",
            "force_update": false,
            "maintenance": {
                "enabled": false,
                "message": "Service temporarily unavailable",
                "platforms": []
            }
        },
        "features": {
            "enable_notifications": false,
            "enable_wallet": true,
            "enable_cod": true,
            "enable_reviews": false,
            "maintenance_mode": false,
            "new_checkout_flow": true
        },
        "integrations": [],
        "ui": {
            "primary_color": "#1d4ed8",
            "home_sections_order": "sliders,categories,featured,new_arrivals"
        },
        "limits": {
            "min_order_amount": "50",
            "max_cart_items": "20",
            "free_shipping_threshold": "500"
        },
        "urls": []
    }
}
```

**What happens next:** Call once at app launch, before or alongside authentication.

---

## 10. Content & Reference (FAQs, Pages, Sliders)

#### List All Faqs

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.faqs.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "f_a_q_s": [
            {
                "id": 1,
                "type": "General",
                "question": "What is this?",
                "answer": "A maintenance platform.",
                "is_active": true,
                "created_at": "2026-08-20 12:09:42"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/faqs?page=1",
            "last": "http://localhost/api/v1/admin/faqs?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/admin/faqs?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/faqs",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Pages

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.pages.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "static_pages": [
            {
                "id": 1,
                "type": "About Us",
                "title": "About Us",
                "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
                "is_active": true,
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/pages?page=1",
            "last": "http://localhost/api/v1/admin/pages?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/admin/pages?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/pages",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Sliders

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `admin.sliders.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.index` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

**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 — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "sliders": [
            {
                "id": 1,
                "title": "Sale",
                "subtitle": "Big discounts",
                "link": "https://example.com",
                "image": "https://ui-avatars.com/api/?name=S&background=e0e7ff&color=4f46e5&size=400",
                "sort_order": 1,
                "is_active": true,
                "created_at": "2026-08-20 12:09:43"
            }
        ],
        "links": {
            "first": "http://localhost/api/v1/admin/sliders?page=1",
            "last": "http://localhost/api/v1/admin/sliders?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/admin/sliders?page=1",
                    "label": "1",
                    "page": 1,
                    "active": true
                },
                {
                    "url": null,
                    "label": "Next &raquo;",
                    "page": null,
                    "active": false
                }
            ],
            "path": "http://localhost/api/v1/admin/sliders",
            "per_page": 15,
            "to": 1,
            "total": 1
        }
    }
}
```

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

---

#### List All Faqs

**What this does:** Returns the published FAQ list.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/faqs?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `client.faqs.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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 — OK }*
```json
{
    "status": "success",
    "message": "fetch FAQ successfully",
    "data": [
        {
            "id": 2,
            "type": "General",
            "question": "كيف أنشئ حساب؟",
            "answer": "انقر على التسجيل واملأ بياناتك.",
            "is_active": true,
            "created_at": "2026-08-20T10:52:55.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational — no follow-up call implied.

---

#### List All Faqs

**What this does:** Returns the published FAQ list.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/company/faqs?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `company.faqs.index` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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 — OK }*
```json
{
    "status": "success",
    "message": "fetch FAQ successfully",
    "data": [
        {
            "id": 3,
            "type": "General",
            "question": "How do I add a branch?",
            "answer": "Use the Branches endpoint to add a new branch to your company account.",
            "is_active": true,
            "created_at": "2026-08-20T11:43:10.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational — no follow-up call implied.

---

#### List All Faqs

**What this does:** Returns the published FAQ list.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/faqs?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false&filters[is_active]=1` |
| **Route name** | `technician.faqs.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 — OK }*
```json
{
    "status": "success",
    "message": "fetch FAQ successfully",
    "data": [
        {
            "id": 3,
            "type": "General",
            "question": "How do I get paid?",
            "answer": "Payouts are processed weekly to your wallet.",
            "is_active": true,
            "created_at": "2026-08-20T11:20:53.000000Z"
        }
    ]
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational — no follow-up call implied.

---

#### Show Faq Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs/:faq` |
| **Route name** | `admin.faqs.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `faq` — Faq ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 2,
        "type": "Orders And Payment",
        "question": "How do I pay?",
        "answer": "Via wallet or gateway.",
        "is_active": true,
        "created_at": "2026-08-20 12:09:42",
        "updated_at": "2026-08-20 12:09:42",
        "en": {
            "question": "How do I pay?",
            "answer": "Via wallet or gateway."
        },
        "ar": {
            "question": "كيف ادفع؟",
            "answer": "عبر المحفظة أو البوابة."
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Page Details

**What this does:** Returns full details for one static page.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages/:page` |
| **Route name** | `admin.pages.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `page` — Page ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 2,
        "type": "Return Policy",
        "title": "Terms",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20 12:09:43",
        "content": "Terms content.",
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "title": "Terms",
            "content": "Terms content."
        },
        "ar": {
            "title": "الشروط",
            "content": "محتوى الشروط."
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Show Slider Details

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

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders/:slider` |
| **Route name** | `admin.sliders.show` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.view` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `slider` — Slider ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "id": 2,
        "title": "Promo",
        "subtitle": "Limited time",
        "link": "https://example.com/promo",
        "image": "https://ui-avatars.com/api/?name=S&background=e0e7ff&color=4f46e5&size=400",
        "sort_order": 2,
        "is_active": true,
        "created_at": "2026-08-20 12:09:43",
        "updated_at": "2026-08-20 12:09:43",
        "en": {
            "title": "Promo",
            "subtitle": "Limited time"
        },
        "ar": {
            "title": "عرض",
            "subtitle": "وقت محدود"
        }
    }
}
```

**Actual Error Response(s):**

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

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

---

#### Create New Faq

**What this does:** Creates a new FAQ.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs` |
| **Route name** | `admin.faqs.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `type`
- **Description:** Type
- **Type:** `string`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|in:"1","2","3"`
- `sometimes` — validated only when the field is present.
- `in:"1","2","3"` — must be one of: "1", "2", "3".
### `en.question`
- **Description:** Question (en)
- **Type:** `string`
- **Required:** required
- **Example:** `What is this?`
- **Validation rules:** `required|string|max:500`
- `required` — must be provided.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `en.answer`
- **Description:** Answer (en)
- **Type:** `string`
- **Required:** required
- **Example:** `This is the answer.`
- **Validation rules:** `required|string|max:5000`
- `required` — must be provided.
- `string` — must be text.
- `max:5000` — must not exceed 5000.
### `ar.question`
- **Description:** Question (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `What is this?`
- **Validation rules:** `required|string|max:500`
- `required` — must be provided.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `ar.answer`
- **Description:** Answer (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `This is the answer.`
- **Validation rules:** `required|string|max:5000`
- `required` — must be provided.
- `string` — must be text.
- `max:5000` — must not exceed 5000.

**Example Request Body:**
```json
{
    "is_active": true,
    "type": 2,
    "en": {
        "question": "How do I pay?",
        "answer": "Via wallet or gateway."
    },
    "ar": {
        "question": "كيف ادفع؟",
        "answer": "عبر المحفظة أو البوابة."
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "is_active": true,
        "type": 2,
        "created_at": "2026-08-20T12:09:42.000000Z",
        "updated_at": "2026-08-20T12:09:42.000000Z",
        "question": "How do I pay?",
        "answer": "Via wallet or gateway."
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "The Question (en) field is required.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this FAQ.

---

#### Create New Page

**What this does:** Creates a new static page.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages` |
| **Route name** | `admin.pages.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `type`
- **Description:** Type
- **Type:** `string`
- **Required:** required
- **Example:** `about_us`
- **Validation rules:** `required|in:about_us,privacy_policy,warranty_policy,return_policy,shipping_policy`
- `required` — must be provided.
- `in:about_us,privacy_policy,warranty_policy,return_policy,shipping_policy` — must be one of: about_us, privacy_policy, warranty_policy, return_policy, shipping_policy (`StaticPageType` enum). The `StaticPage` model casts `type` to this enum on read, so a value outside it would throw an error when the page is loaded — this is enforced, not just labeling.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.title`
- **Description:** Title (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Title`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.content`
- **Description:** Content (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example content text`
- **Validation rules:** `required|string`
- `required` — must be provided.
- `string` — must be text.
### `ar.title`
- **Description:** Title (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Title`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.content`
- **Description:** Content (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example content text`
- **Validation rules:** `required|string`
- `required` — must be provided.
- `string` — must be text.

**Example Request Body:**
```json
{
    "type": "return_policy",
    "is_active": true,
    "en": {
        "title": "Terms",
        "content": "Terms content."
    },
    "ar": {
        "title": "الشروط",
        "content": "محتوى الشروط."
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "is_active": true,
        "type": "return_policy",
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "title": "Terms",
        "content": "Terms content."
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "Please select a type.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this static page.

---

#### Create New Slider

**What this does:** Creates a new slider.

| | |
|---|---|
| **Method** | `POST` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders` |
| **Route name** | `admin.sliders.store` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.create` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `201 Created`, `422 Unprocessable Entity` |

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

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

**Field documentation:**
### `link`
- **Description:** Link
- **Type:** `string`
- **Required:** optional
- **Example:** `https://example.com`
- **Validation rules:** `nullable|url|max:500`
- `nullable` — may be null.
- `url` — must be a valid URL.
- `max:500` — must not exceed 500.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.title`
- **Description:** Title (en)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Title`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.subtitle`
- **Description:** Subtitle (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `nullable|string|max:500`
- `nullable` — may be null.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `ar.title`
- **Description:** Title (ar)
- **Type:** `string`
- **Required:** required
- **Example:** `Example Title`
- **Validation rules:** `required|string|max:255`
- `required` — must be provided.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.subtitle`
- **Description:** Subtitle (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `nullable|string|max:500`
- `nullable` — may be null.
- `string` — must be text.
- `max:500` — must not exceed 500.

**Example Request Body:**
```json
{
    "link": "https://example.com/promo",
    "sort_order": 2,
    "is_active": true,
    "en": {
        "title": "Promo",
        "subtitle": "Limited time"
    },
    "ar": {
        "title": "عرض",
        "subtitle": "وقت محدود"
    }
}
```

**Actual Success Response(s):**

*{ 201 Created — Created }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "link": "https://example.com/promo",
        "sort_order": 2,
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "title": "Promo",
        "subtitle": "Limited time"
    }
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Validation Error }*
```json
{
    "status": "fail",
    "message": "The Link must be a valid URL.",
    "data": null
}
```

**What happens next:** Use the returned id with `show`/`update`/`destroy` for this slider.

---

#### Update Faq

**What this does:** Updates an existing FAQ.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs/:faq` |
| **Route name** | `admin.faqs.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `faq` — Faq ID — seeded example: `1`

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

**Field documentation:**
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `type`
- **Description:** Type
- **Type:** `string`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `sometimes|in:"1","2","3"`
- `sometimes` — validated only when the field is present.
- `in:"1","2","3"` — must be one of: "1", "2", "3".
### `en.question`
- **Description:** Question (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `What is this?`
- **Validation rules:** `sometimes|string|max:500`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `en.answer`
- **Description:** Answer (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `This is the answer.`
- **Validation rules:** `sometimes|string|max:5000`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:5000` — must not exceed 5000.
### `ar.question`
- **Description:** Question (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `What is this?`
- **Validation rules:** `sometimes|string|max:500`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `ar.answer`
- **Description:** Answer (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `This is the answer.`
- **Validation rules:** `sometimes|string|max:5000`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:5000` — must not exceed 5000.

**Example Request Body:**
```json
{
    "en": {
        "question": "Updated question?",
        "answer": "Updated answer."
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "is_active": true,
        "type": 2,
        "created_at": "2026-08-20T12:09:42.000000Z",
        "updated_at": "2026-08-20T12:09:42.000000Z",
        "question": "Updated question?",
        "answer": "Updated answer."
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Page

**What this does:** Updates an existing static page.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages/:page` |
| **Route name** | `admin.pages.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `page` — Page ID — seeded example: `1`

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

**Field documentation:**
### `type`
- **Description:** Type
- **Type:** `string`
- **Required:** optional
- **Example:** `about_us`
- **Validation rules:** `sometimes|in:about_us,privacy_policy,warranty_policy,return_policy,shipping_policy`
- `sometimes` — validated only when the field is present.
- `in:about_us,privacy_policy,warranty_policy,return_policy,shipping_policy` — must be one of: about_us, privacy_policy, warranty_policy, return_policy, shipping_policy (`StaticPageType` enum) — see Create New Page above.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.title`
- **Description:** Title (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.content`
- **Description:** Content (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example content text`
- **Validation rules:** `sometimes|string`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
### `ar.title`
- **Description:** Title (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.content`
- **Description:** Content (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example content text`
- **Validation rules:** `sometimes|string`
- `sometimes` — validated only when the field is present.
- `string` — must be text.

**Example Request Body:**
```json
{
    "en": {
        "title": "Terms Updated",
        "content": "Updated content."
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "is_active": true,
        "type": "return_policy",
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "title": "Terms Updated",
        "content": "Updated content."
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Update Slider

**What this does:** Updates an existing slider.

| | |
|---|---|
| **Method** | `PUT` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders/:slider` |
| **Route name** | `admin.sliders.update` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.update` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK` |

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

**Path Parameters:**
- `slider` — Slider ID — seeded example: `1`

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

**Field documentation:**
### `link`
- **Description:** Link
- **Type:** `string`
- **Required:** optional
- **Example:** `https://example.com`
- **Validation rules:** `nullable|url|max:500`
- `nullable` — may be null.
- `url` — must be a valid URL.
- `max:500` — must not exceed 500.
### `sort_order`
- **Description:** Sort Order
- **Type:** `integer`
- **Required:** optional
- **Example:** `1`
- **Validation rules:** `nullable|integer`
- `nullable` — may be null.
- `integer` — must be a whole number.
### `is_active`
- **Description:** Is Active
- **Type:** `boolean`
- **Required:** optional
- **Example:** `true`
- **Validation rules:** `sometimes|boolean`
- `sometimes` — validated only when the field is present.
- `boolean` — must be true, false, 1, or 0.
### `image`
- **Description:** Image
- **Type:** `string`
- **Required:** optional
- **Example:** `value`
- **Validation rules:** `nullable|string|custom:MediaExists`
- `nullable` — may be null.
- `string` — must be text.
- `custom:MediaExists` — custom validation rule.
### `en.title`
- **Description:** Title (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `en.subtitle`
- **Description:** Subtitle (en)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `nullable|string|max:500`
- `nullable` — may be null.
- `string` — must be text.
- `max:500` — must not exceed 500.
### `ar.title`
- **Description:** Title (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `sometimes|string|max:255`
- `sometimes` — validated only when the field is present.
- `string` — must be text.
- `max:255` — must not exceed 255.
### `ar.subtitle`
- **Description:** Subtitle (ar)
- **Type:** `string`
- **Required:** optional
- **Example:** `Example Title`
- **Validation rules:** `nullable|string|max:500`
- `nullable` — may be null.
- `string` — must be text.
- `max:500` — must not exceed 500.

**Example Request Body:**
```json
{
    "en": {
        "title": "Promo Updated",
        "subtitle": "Still limited"
    }
}
```

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data saved successfully",
    "data": {
        "id": 2,
        "link": "https://example.com/promo",
        "sort_order": 2,
        "is_active": true,
        "created_at": "2026-08-20T12:09:43.000000Z",
        "updated_at": "2026-08-20T12:09:43.000000Z",
        "title": "Promo Updated",
        "subtitle": "Still limited"
    }
}
```

**What happens next:** Fetch `show` to confirm the change.

---

#### Page Content

**What this does:** Returns one static page by `:type` (e.g. `warranty_policy`, `return_policy`, `shipping_policy` — the public, unauthenticated equivalent of the role-scoped `static_pages` endpoints above for types beyond about-us/privacy).

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/pages/:type` |
| **Route name** | `client.content.page` |
| **Guard** | None — public endpoint; `geo.context` |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `type` — Static page type/slug — see ContentController for the allowed values. — seeded example: `shipping_policy`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Page retrieved successfully",
    "data": {
        "id": 1,
        "is_active": true,
        "type": "shipping_policy",
        "created_at": "2026-08-20T10:52:55.000000Z",
        "updated_at": "2026-08-20T10:52:55.000000Z",
        "title": "سياسة الشحن",
        "content": "التوصيل العادي يستغرق 3-5 أيام عمل."
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational.

---

#### Sliders Content

**What this does:** Returns active homepage promotional sliders for public/unauthenticated display.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/sliders?page=1&per_page=15&search=&sort[created_at]=desc&paginate=false` |
| **Route name** | `client.content.sliders` |
| **Guard** | None — public endpoint; `geo.context` |
| **Allowed roles** | Public — no authentication required |
| **Status codes** | `200 OK` |

**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)

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Sliders retrieved successfully",
    "data": [
        {
            "id": 1,
            "link": "https://example.com/summer",
            "sort_order": 1,
            "is_active": true,
            "created_at": "2026-08-20T10:52:55.000000Z",
            "updated_at": "2026-08-20T10:52:55.000000Z",
            "title": "تخفيضات الصيف",
            "subtitle": "خصم يصل الى 50%"
        }
    ]
}
```

**What happens next:** Purely informational.

---

#### About Us Static page

**What this does:** Returns the published "About Us" static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/static_pages/about_us` |
| **Route name** | `client.static_pages.about_us` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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 — (auth required — see route-placement note) }*
```json
{
    "status": "success",
    "message": "fetch About Us Successfully",
    "data": {
        "id": 2,
        "type": "About Us",
        "title": "من نحن",
        "content": "نقدم خدمة عالية الجودة.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T10:52:56.000000Z"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational — no follow-up call implied.

---

#### Privacy Policies Static page

**What this does:** Returns the published Privacy Policy static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/client/static_pages/privacy_policies` |
| **Route name** | `client.static_pages.privacy_policies` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; `geo.context` |
| **Allowed roles** | Individual client (`user_type: individual`) |
| **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 — (auth required — see route-placement note) }*
```json
{
    "status": "success",
    "message": "fetch Privacy Policies Successfully",
    "data": {
        "id": 3,
        "type": "Privacy Policy",
        "title": "سياسة الخصوصية",
        "content": "خصوصيتك مهمة لنا.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T10:52:56.000000Z"
    }
}
```

**Actual Error Response(s):**

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

**What happens next:** Purely informational — no follow-up call implied.

---

#### About Us Static page

**What this does:** Returns the published "About Us" static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/company/static_pages/about_us` |
| **Route name** | `company.static_pages.about_us` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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 — (auth required — see route-placement bug report) }*
```json
{
    "status": "success",
    "message": "fetch About Us Successfully",
    "data": {
        "id": 4,
        "type": "About Us",
        "title": "About Us",
        "content": "We connect maintenance companies with skilled technicians and clients.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T11:43:10.000000Z"
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - confirms route-placement bug (see report) }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Purely informational — no follow-up call implied.

---

#### Privacy Policies Static page

**What this does:** Returns the published Privacy Policy static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/company/static_pages/privacy_policies` |
| **Route name** | `company.static_pages.privacy_policies` |
| **Guard** | JWT bearer (`auth:api`); account must be `active` |
| **Allowed roles** | Company (`user_type: company`) |
| **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 — (auth required — see route-placement bug report) }*
```json
{
    "status": "success",
    "message": "fetch Privacy Policies Successfully",
    "data": {
        "id": 5,
        "type": "Privacy Policy",
        "title": "Privacy Policy",
        "content": "Your company data is handled according to our privacy policy.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T11:43:10.000000Z"
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - confirms route-placement bug (see report) }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Purely informational — no follow-up call implied.

---

#### About Us Static page

**What this does:** Returns the published "About Us" static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/static_pages/about_us` |
| **Route name** | `technician.static_pages.about_us` |
| **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 — (auth required — see route-placement bug report) }*
```json
{
    "status": "success",
    "message": "fetch About Us Successfully",
    "data": {
        "id": 4,
        "type": "About Us",
        "title": "About Us",
        "content": "We connect skilled technicians with customers.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T11:20:53.000000Z"
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - confirms route-placement bug (see report) }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Purely informational — no follow-up call implied.

---

#### Privacy Policies Static page

**What this does:** Returns the published Privacy Policy static page content.

| | |
|---|---|
| **Method** | `GET` |
| **Endpoint** | `{{base_url}}/api/v1/technician/static_pages/privacy_policies` |
| **Route name** | `technician.static_pages.privacy_policies` |
| **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 — (auth required — see route-placement bug report) }*
```json
{
    "status": "success",
    "message": "fetch Privacy Policies Successfully",
    "data": {
        "id": 5,
        "type": "Privacy Policy",
        "title": "Privacy Policy",
        "content": "Your privacy is important to us.",
        "image": "https://ui-avatars.com/api/?name=P&background=e0e7ff&color=4f46e5&size=200",
        "is_active": true,
        "created_at": "2026-08-20T11:20:53.000000Z"
    }
}
```

**Actual Error Response(s):**

*{ 401 Unauthorized — 401 Unauthenticated - confirms route-placement bug (see report) }*
```json
{
    "status": "fail",
    "message": "Unauthenticated, you have to login first",
    "data": null
}
```

**What happens next:** Purely informational — no follow-up call implied.

---

#### Delete Faq

**What this does:** Permanently deletes a FAQ.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs/:faq` |
| **Route name** | `admin.faqs.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `faq` — Faq ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Page

**What this does:** Permanently deletes a static page.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages/:page` |
| **Route name** | `admin.pages.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `page` — Page ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Slider

**What this does:** Permanently deletes a slider.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders/:slider` |
| **Route name** | `admin.sliders.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found` |

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

**Path Parameters:**
- `slider` — Slider ID — seeded example: `1`

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

**Actual Success Response(s):**

*{ 200 OK — OK }*
```json
{
    "status": "success",
    "message": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

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

**What happens next:** It no longer appears in `index`.

---

#### Delete Bulk

**What this does:** Deletes multiple FAQs in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/faqs/bulk` |
| **Route name** | `admin.faqs.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `faqs.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `404 Not Found`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

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

**What happens next:** Re-fetch the index to confirm — deleted FAQs no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple static pages in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/pages/bulk` |
| **Route name** | `admin.pages.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `pages.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted static pages no longer appear.

---

#### Delete Bulk

**What this does:** Deletes multiple sliders in one call by an array of ids.

| | |
|---|---|
| **Method** | `DELETE` |
| **Endpoint** | `{{base_url}}/api/v1/admin/sliders/bulk` |
| **Route name** | `admin.sliders.bulk.destroy` |
| **Guard** | JWT bearer (`auth:api`); account must be `active`; RBAC permission `sliders.delete` required (`rbac.auto` — bypassed in `local`/`testing` environments, see [AUTH.md](../AUTH.md)) |
| **Allowed roles** | Admin / Super Admin (`user_type: admin` or `super_admin`) |
| **Status codes** | `200 OK`, `422 Unprocessable Entity` |

**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": "Data removed successfully",
    "data": null
}
```

**Actual Error Response(s):**

*{ 422 Unprocessable Entity — 422 Empty ids }*
```json
{
    "status": "fail",
    "message": "Please select at least one item.",
    "data": null
}
```

**What happens next:** Re-fetch the index to confirm — deleted sliders no longer appear.

---
