SS API Docs
Admin API

Frontend API Reference

Complete reference for frontend developers — authentication, endpoints, models, enums, and integration patterns.

REST + JWT JSON responses Role-based access

Shipping System — Frontend API Reference

Detailed reference for frontend developers integrating with the Admin API.

Scope: Only the Admin API is exposed over HTTP today (/api/admin/*). Shipment, route, and delivery domain models exist in the database and are documented below for context, but those endpoints are not implemented yet.


Table of Contents

  1. Quick Start
  2. Global Conventions
  3. Authentication
  4. Profile
  5. Admins
  6. Roles & Permissions
  7. Drivers
  8. Vehicles
  9. Driver Unavailabilities
  10. Driver–Vehicle Assignments
  11. Users (Customers)
  12. Domain Models Reference
  13. Enums Reference
  14. Permissions Reference
  15. Error Handling
  16. Frontend Integration Checklist

Quick Start

HTTP
POST /api/admin/login
Content-Type: application/json
Accept-Language: en

{
  "email": "mousa@example.com",
  "password": "password"
}

Store the returned JWT and send it on every subsequent request:

HTTP
GET /api/admin/drivers
Authorization: Bearer {token}
Accept: application/json
Accept-Language: en

Base URL: {APP_URL}/api — e.g. http://localhost:8000/api when running php artisan serve.


Global Conventions

Request Headers

Header Required Description
Authorization Yes (except login) Bearer {jwt_token}
Accept Recommended application/json
Accept-Language Optional ar (default) or en — controls translated message text
Content-Type Varies application/json for JSON bodies; multipart/form-data when uploading images

Response Envelope

Every API response uses this structure:

JSON
{
  "status": "Success",
  "message": "Data fetched successfully",
  "data": {},
  "statusCode": 200
}
Field Type Description
status "Success" | "Error" Outcome indicator
message string Human-readable, localized message
data object | array | null Payload (null on delete/logout)
statusCode integer HTTP status code echoed in body

On login success, the JWT is also returned in the Authorization response header as Bearer {token}.

HTTP Methods

Updates use POST, not PUT or PATCH.

All update endpoints follow the pattern POST /api/admin/{resource}/{id}.

Content Types

Scenario Content-Type
JSON CRUD (no file) application/json
Create/update with image (admin, driver, profile) multipart/form-data
Assign/unassign vehicle No body required

When using multipart/form-data:

  • Send scalar fields as form fields.
  • Send working_days as repeated fields or JSON string (array).
  • Send roles / permissions as repeated fields or JSON array string.
  • Send image as a file field (max 5 MB, must be an image).

Listing, Filtering & Sorting

Most GET list endpoints accept query string parameters:

Parameter Type Description
search string Full-text search across model-specific columns
sort_by string Column name (must be in model's sortable list)
sort_direction "asc" | "desc" Sort direction (default varies by model)
page integer Page number (paginated endpoints only)

Query string values "null" and "" are automatically converted to null by middleware.

Pagination

Paginated endpoints (GET .../paginated) wrap results like this:

JSON
{
  "drivers": [],
  "total": 50,
  "count": 10,
  "per_page": 10,
  "current_page": 1,
  "total_pages": 5,
  "links": {
    "first": "http://localhost:8000/api/admin/drivers/paginated?page=1",
    "last": "http://localhost:8000/api/admin/drivers/paginated?page=5",
    "prev": null,
    "next": "http://localhost:8000/api/admin/drivers/paginated?page=2"
  }
}

The collection key matches the resource name:

Endpoint prefix Collection key
/admins/paginated admins
/roles/paginated roles
/drivers/paginated drivers
/vehicles/paginated vehicles
/users/paginated users
/driver-unavailabilities/paginated driver_unavailabilities

Page size is fixed at 10 per page.


Authentication

All routes under /api/admin/* except POST /login require a valid JWT in the Authorization header.

  • Guard: admin
  • Library: JWT Auth (tymon/jwt-auth)
  • Token TTL: Returned as expires_in (seconds) in login response. Configured via JWT_TTL env (minutes); if unset, token may not expire.

POST /api/admin/login

Authenticate an admin and receive a JWT.

Auth required: No

Request body:

Field Type Required Validation
email string Yes Valid email
password string Yes Non-empty string
fcm_token string No Push notification device token; stored on admin record

Example request body:

JSON
{
  "email": "mousa@example.com",
  "password": "SecurePass123!",
  "fcm_token": "device-fcm-token-abc123"
}

Success response 200:

JSON
{
  "status": "Success",
  "message": "User successfully signed in",
  "data": {
    "admin": {
      "id": 1,
      "name": "mousa",
      "email": "mousa@example.com",
      "roles": ["operations_manager"],
      "permission_groups": [
        {
          "group": "Driver",
          "group_label": "Drivers",
          "permissions": [
            {
              "id": 1,
              "name": "ViewAny:Driver",
              "display_name": "View Any",
              "group": "Driver"
            }
          ]
        }
      ]
    },
    "token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
    "token_type": "bearer",
    "expires_in": 3600
  },
  "statusCode": 200
}

Error responses:

Status When
401 Invalid email/password (credentialsError)
422 Validation failed (missing email/password)
500 Token generation failed (couldNotCreateToken)

POST /api/admin/logout

Invalidate the current JWT session.

Auth required: Yes

Request body: None

Success response 200:

JSON
{
  "status": "Success",
  "message": "User successfully signed out",
  "data": null,
  "statusCode": 200
}

Error responses:

Status When
403 No token provided (Unauthenticated)
500 Logout failed (couldNotLogout)

Profile

Manage the currently authenticated admin's own profile. Does not require admin-management permissions.


GET /api/admin/profile

Permission required: None (authentication only)

Success response 200: Returns an AdminResource (see Admins) with:

  • is_current_admin: true
  • permission_groups included
  • roles included

POST /api/admin/profile

Update the authenticated admin's profile.

Permission required: None (authentication only)

Request body (JSON or multipart):

Field Type Required Validation
name string No Max 255 chars
email string No Valid email, unique among admins
password string No Laravel password defaults
image file No Image, max 5 MB

Example request body:

JSON
{
  "name": "mousa",
  "email": "mousa.updated@example.com",
  "password": "NewSecurePass123!"
}

Success response 200: Updated AdminResource with message profileUpdatedSuccessfully.


Admins

Back-office user management.

Permission prefix: Admin (e.g. ViewAny:Admin, Create:Admin)

Hidden admin accounts (configured via HIDDEN_ADMIN env) are excluded from listings.

Endpoints

Method Path Description Permission
GET /api/admin/admins List all admins ViewAny:Admin
GET /api/admin/admins/paginated List paginated ViewAny:Admin
GET /api/admin/admins/{id} Get single admin View:Admin
POST /api/admin/admins Create admin Create:Admin
POST /api/admin/admins/{id} Update admin Update:Admin
DELETE /api/admin/admins/{id} Delete admin Delete:Admin

List Query Parameters

Parameter Description
search Searches name, email
role Filter by role name (exact match)
sort_by name, email, created_at
sort_direction asc or desc (default: asc by name)

Create Request Body

Field Type Required Validation
name string Yes Max 255
email string Yes Valid email, unique
password string Yes Laravel password defaults
image file No Image, max 5 MB
roles string[] No Array of existing role names

Example request body:

JSON
{
  "name": "mousa",
  "email": "mousa@example.com",
  "password": "SecurePass123!",
  "roles": ["operations_manager"]
}

Update Request Body

Same fields as create, all optional (sometimes rules apply). Omit password to keep current password. Send roles: [] to remove all roles.

Example request body:

JSON
{
  "name": "mousa",
  "email": "mousa.admin@example.com",
  "roles": ["super_admin"]
}

Admin Resource Shape

JSON
{
  "id": 1,
  "name": "mousa",
  "email": "mousa@example.com",
  "image": "http://localhost:8000/storage/1/admin_image.jpg",
  "roles": ["super_admin"],
  "permission_groups": [],
  "is_current_admin": false,
  "created_at": "2026-01-15T10:00:00.000000Z",
  "updated_at": "2026-01-15T10:00:00.000000Z"
}
Field Notes
image Full URL to profile image, or empty string if none
roles Array of role name strings
permission_groups Only included on GET /profile and GET /admins/{id}
is_current_admin true if this admin is the one making the request

Delete Constraints

  • Cannot delete your own account → 403 with message cannotDeleteCurrentAdmin

Roles & Permissions

Role-based access control using Spatie Permission (guard: admin).

Permission prefixes: Role, Permission

Endpoints

Method Path Description Permission
GET /api/admin/permissions All permissions grouped ViewAny:Permission
GET /api/admin/roles List all roles ViewAny:Role
GET /api/admin/roles/paginated List paginated ViewAny:Role
GET /api/admin/roles/{id} Get single role View:Role
POST /api/admin/roles Create role Create:Role
POST /api/admin/roles/{id} Update role Update:Role
POST /api/admin/roles/{id}/permissions Sync permissions only Update:Role
DELETE /api/admin/roles/{id} Delete role Delete:Role

List Query Parameters (roles)

Parameter Description
search Searches role name
sort_by name, created_at
sort_direction asc or desc (default: asc by name)

Create Role Request Body

Field Type Required Validation
name string Yes Unique per guard, max 255
permissions string[] No Array of permission name strings

Example request body:

JSON
{
  "name": "warehouse_manager",
  "permissions": ["ViewAny:Driver", "Create:Driver", "ViewAny:Vehicle"]
}

Update Role Request Body

Field Type Required Validation
name string No Unique per guard, max 255
permissions string[] No Replaces all permissions when provided

Example request body:

JSON
{
  "name": "warehouse_manager",
  "permissions": ["ViewAny:Driver", "Update:Driver"]
}

Sync Permissions Request Body

POST /api/admin/roles/{id}/permissions

Field Type Required Validation
permissions string[] Yes Array of permission name strings

Example request body:

JSON
{
  "permissions": ["ViewAny:Driver", "Create:Driver", "ViewAny:Vehicle", "Create:Vehicle"]
}

Success message: permissionsAssignedSuccessfully

Role Resource Shape

JSON
{
  "id": 2,
  "name": "warehouse_manager",
  "guard_name": "admin",
  "permission_groups": [
    {
      "group": "Driver",
      "group_label": "Drivers",
      "permissions": [
        {
          "id": 1,
          "name": "ViewAny:Driver",
          "display_name": "View Any",
          "group": "Driver"
        }
      ]
    }
  ],
  "created_at": "2026-01-15T10:00:00.000000Z",
  "updated_at": "2026-01-15T10:00:00.000000Z"
}

Permission Group Shape

Used in login, profile, roles, and the permissions list:

JSON
{
  "group": "Driver",
  "group_label": "Drivers",
  "permissions": [
    {
      "id": 1,
      "name": "ViewAny:Driver",
      "display_name": "View Any",
      "group": "Driver"
    }
  ]
}

Constraints

  • Super admin role cannot be modified or deleted → 403 (cannotModifySuperAdmin)

Drivers

Delivery driver management.

Permission prefix: Driver

Endpoints

Method Path Description Permission
GET /api/admin/drivers List all drivers ViewAny:Driver
GET /api/admin/drivers/paginated List paginated ViewAny:Driver
GET /api/admin/drivers/{id} Get single driver View:Driver
POST /api/admin/drivers Create driver Create:Driver
POST /api/admin/drivers/{id} Update driver Update:Driver
DELETE /api/admin/drivers/{id} Delete driver Delete:Driver
GET /api/admin/drivers/{id}/vehicle-assignments Vehicle assignments ViewAny:DriverVehicleAssignment
GET /api/admin/drivers/{id}/unavailabilities Unavailability periods ViewAny:DriverUnavailability
POST /api/admin/drivers/{driverId}/vehicles/{vehicleId}/assign Assign vehicle Create:DriverVehicleAssignment
POST /api/admin/drivers/{driverId}/vehicles/{vehicleId}/unassign Unassign vehicle Update:DriverVehicleAssignment

List Query Parameters

Parameter Type Description
search string Searches name, phone, email, driver_number, license_number
is_active boolean Filter by active status
lat float Latitude for nearby search (requires lng)
lng float Longitude for nearby search (requires lat)
radius float Search radius in km (default: 10)
work_start_time time Filter drivers whose shift overlaps this start time
work_end_time time Filter drivers whose shift overlaps this end time
work_start_time_from time Minimum shift start time
work_start_time_to time Maximum shift start time
work_end_time_from time Minimum shift end time
work_end_time_to time Maximum shift end time
sort_by string See sortable columns below
sort_direction string asc or desc

Sortable columns: name, phone, driver_number, work_start_time, work_end_time, created_at, distance

When lat/lng are provided, results include a distance field (km) and default-sort by nearest first unless sort_by is explicitly set.

Time format: HH:MM:SS or HH:MM (auto-normalized to HH:MM:SS).

Create Request Body

Field Type Required Validation
name string Yes Max 255
phone string Yes Max 50
phone_country string Yes Max 10 (e.g. +963)
email string No Valid email, unique
password string Yes Laravel password defaults
address string Yes
license_number string No Max 255
working_days string[] Yes Min 1 item; see Working Days
work_start_time time Yes Must differ from work_end_time
work_end_time time Yes Must differ from work_start_time
lat float No -90 to 90
lng float No -180 to 180
fcm_token string No Push notification token
is_active boolean No Default: true
image file No Image, max 5 MB
driver_number string No Unique; auto-generated 6-digit number if omitted

Example request body:

JSON
{
  "name": "mousa",
  "phone": "944123456",
  "phone_country": "+963",
  "email": "mousa.driver@example.com",
  "password": "SecurePass123!",
  "address": "Damascus, Syria",
  "license_number": "SY-12345",
  "working_days": ["sun", "mon", "tue", "wed", "thu"],
  "work_start_time": "08:00:00",
  "work_end_time": "17:00:00",
  "lat": 33.5138,
  "lng": 36.2765,
  "is_active": true
}

Update Request Body

Same fields as create. All fields optional except where sometimes + required applies. Password optional (omit to keep current).

Example request body:

JSON
{
  "name": "mousa",
  "phone": "944654321",
  "phone_country": "+963",
  "work_start_time": "09:00:00",
  "work_end_time": "18:00:00",
  "is_active": true
}

Driver Resource Shape

JSON
{
  "id": 1,
  "driver_number": "482910",
  "name": "mousa",
  "phone": "944123456",
  "phone_country": "+963",
  "email": "mousa.driver@example.com",
  "image": "http://localhost:8000/storage/2/driver_image.jpg",
  "address": "Damascus, Syria",
  "license_number": "DL-12345",
  "working_days": ["sun", "mon", "tue", "wed", "thu"],
  "work_start_time": "08:00:00",
  "work_end_time": "17:00:00",
  "lat": 24.7136,
  "lng": 46.6753,
  "is_active": true,
  "created_at": "2026-01-15T10:00:00.000000Z",
  "updated_at": "2026-01-15T10:00:00.000000Z",
  "distance": 3.2
}
Field Notes
distance Only present when nearby filter (lat/lng) is used
working_days JSON array of day codes
image Full URL or empty string

Vehicles

Fleet vehicle management.

Permission prefix: Vehicle

Endpoints

Method Path Description Permission
GET /api/admin/vehicles List all vehicles ViewAny:Vehicle
GET /api/admin/vehicles/paginated List paginated ViewAny:Vehicle
GET /api/admin/vehicles/{id} Get single vehicle View:Vehicle
POST /api/admin/vehicles Create vehicle Create:Vehicle
POST /api/admin/vehicles/{id} Update vehicle Update:Vehicle
DELETE /api/admin/vehicles/{id} Delete vehicle Delete:Vehicle

List Query Parameters

Parameter Description
search Searches plate_number
owner_type Exact match: company or driver
is_active Boolean filter
sort_by plate_number, owner_type, max_weight, max_volume, created_at
sort_direction asc or desc (default: asc by plate_number)

Create Request Body

Field Type Required Validation
plate_number string Yes Unique, max 255
owner_type string Yes company or driver — see OwnerTypes
owner_id integer Conditional Required when owner_type is driver; must be a valid driver ID. Must not be sent when owner_type is company
max_weight number Yes Min 0 (kg)
max_volume number Yes Min 0 (m³)

Example request body:

JSON
{
  "plate_number": "DMS-1234",
  "owner_type": "company",
  "max_weight": 1500,
  "max_volume": 12.5
}

Example request body (driver-owned):

JSON
{
  "plate_number": "DMS-5678",
  "owner_type": "driver",
  "owner_id": 1,
  "max_weight": 2000,
  "max_volume": 15
}

Update Request Body

Field Type Required Validation
plate_number string No Unique, max 255
owner_type string No company or driver
owner_id integer Conditional Same rules as create
max_weight number No Min 0
max_volume number No Min 0
is_active boolean No

Example request body:

JSON
{
  "plate_number": "DMS-9999",
  "max_weight": 1800,
  "max_volume": 14,
  "is_active": false
}

Vehicle Resource Shape

JSON
{
  "id": 1,
  "plate_number": "ABC-1234",
  "owner_type": "company",
  "owner_type_label": "الشركة",
  "owner_id": null,
  "max_weight": 1500,
  "max_volume": 12.5,
  "is_active": true,
  "created_at": "2026-01-15T10:00:00.000000Z",
  "updated_at": "2026-01-15T10:00:00.000000Z"
}
Field Notes
owner_type_label Localized Arabic label (API always returns Arabic label regardless of Accept-Language)
owner_id null for company-owned vehicles; driver ID for driver-owned

Driver Unavailabilities

Time-off / unavailability blocks for drivers.

Permission prefix: DriverUnavailability

Endpoints

Method Path Description Permission
GET /api/admin/driver-unavailabilities List all ViewAny:DriverUnavailability
GET /api/admin/driver-unavailabilities/paginated List paginated ViewAny:DriverUnavailability
GET /api/admin/driver-unavailabilities/{id} Get single View:DriverUnavailability
POST /api/admin/driver-unavailabilities Create Create:DriverUnavailability
POST /api/admin/driver-unavailabilities/{id} Update Update:DriverUnavailability
DELETE /api/admin/driver-unavailabilities/{id} Delete Delete:DriverUnavailability

Also accessible via GET /api/admin/drivers/{id}/unavailabilities.

List Query Parameters

Parameter Description
driver_id Filter by driver ID (exact)
sort_by start_date, end_date, created_at
sort_direction Default: desc by start_date

Create Request Body

Field Type Required Validation
driver_id integer Yes Must exist in drivers table
start_date date Yes ISO date or datetime
end_date date Yes Must be after start_date
reason string No Free text

Example request body:

JSON
{
  "driver_id": 1,
  "start_date": "2026-06-01",
  "end_date": "2026-06-05",
  "reason": "Annual leave"
}

Update Request Body

Same fields, all optional. If both dates are provided on update, end_date must still be after start_date.

Example request body:

JSON
{
  "start_date": "2026-06-01",
  "end_date": "2026-06-10",
  "reason": "Extended leave"
}

Driver Unavailability Resource Shape

JSON
{
  "id": 1,
  "driver_id": 5,
  "start_date": "2026-06-01T00:00:00.000000Z",
  "end_date": "2026-06-05T00:00:00.000000Z",
  "reason": "Annual leave",
  "created_at": "2026-05-20T10:00:00.000000Z",
  "updated_at": "2026-05-20T10:00:00.000000Z"
}

Driver–Vehicle Assignments

Links drivers to vehicles for operational use. A vehicle can only have one active assignment at a time (released_at === null).

Permission prefix: DriverVehicleAssignment

Endpoints

Method Path Description Permission
GET /api/admin/drivers/{id}/vehicle-assignments List assignments for driver ViewAny:DriverVehicleAssignment
POST /api/admin/drivers/{driverId}/vehicles/{vehicleId}/assign Assign vehicle to driver Create:DriverVehicleAssignment
POST /api/admin/drivers/{driverId}/vehicles/{vehicleId}/unassign Release assignment Update:DriverVehicleAssignment

Assign

No request body. Creates a record with:

  • assigned_at = current timestamp
  • released_at = null

Error: 422 if vehicle already has an active assignment (vehicleAlreadyAssigned)

Unassign

No request body. Sets released_at to current timestamp on the active assignment.

Error: 404 if no active assignment exists (vehicleAssignmentNotFound)

Driver Vehicle Assignment Resource Shape

JSON
{
  "id": 1,
  "driver_id": 3,
  "vehicle_id": 7,
  "assigned_at": "2026-06-01T08:00:00.000000Z",
  "released_at": null,
  "vehicle": {
    "id": 7,
    "plate_number": "XYZ-5678",
    "owner_type": "company",
    "owner_type_label": "الشركة",
    "owner_id": null,
    "max_weight": 2000,
    "max_volume": 15,
    "is_active": true,
    "created_at": "...",
    "updated_at": "..."
  },
  "driver": {
    "id": 3,
    "driver_number": "482910",
    "name": "mousa"
  },
  "created_at": "2026-06-01T08:00:00.000000Z",
  "updated_at": "2026-06-01T08:00:00.000000Z"
}
Field Notes
vehicle Nested VehicleResource when relation is loaded
driver Nested DriverResource when relation is loaded
released_at null = currently assigned

Users (Customers)

End customers who place shipments. These are not admin accounts.

Permission prefix: User

Endpoints

Method Path Description Permission
GET /api/admin/users List all users ViewAny:User
GET /api/admin/users/paginated List paginated ViewAny:User
GET /api/admin/users/{id} Get single user View:User
POST /api/admin/users Create user Create:User
POST /api/admin/users/{id} Update user Update:User
DELETE /api/admin/users/{id} Delete user Delete:User

List Query Parameters

Parameter Description
search Searches name, phone, email, company_name
sort_by name, phone, email, company_name, created_at
sort_direction asc or desc (default: asc by name)

Create Request Body

Field Type Required Validation
name string Yes Max 255
phone string Yes Max 50; unique per phone_country
phone_country string Yes Max 10
email string No Valid email, unique
company_name string No

Example request body:

JSON
{
  "name": "mousa",
  "phone": "944123456",
  "phone_country": "+963",
  "email": "mousa@example.com",
  "company_name": "Mousa Trading Co."
}

Update Request Body

Same fields, all optional. Phone uniqueness is re-validated when phone or phone_country changes.

Example request body:

JSON
{
  "name": "mousa",
  "phone": "944654321",
  "phone_country": "+963",
  "company_name": "Mousa Logistics"
}

User Resource Shape

JSON
{
  "id": 1,
  "name": "mousa",
  "phone": "944123456",
  "phone_country": "+963",
  "email": "mousa@example.com",
  "company_name": "Mousa Trading Co.",
  "created_at": "2026-01-15T10:00:00.000000Z",
  "updated_at": "2026-01-15T10:00:00.000000Z"
}

Domain Models Reference

Currently Exposed via API

Model Table Description Key Fields
Admin admins Back-office users name, email, password, fcm_token
Role roles Spatie roles (guard: admin) name, guard_name
Permission permissions Spatie permissions name, guard_name
Driver drivers Delivery drivers driver_number, name, phone, working_days, work_start_time, work_end_time, lat, lng, is_active
Vehicle vehicles Fleet vehicles plate_number, owner_type, owner_id, max_weight, max_volume, is_active
DriverVehicleAssignment driver_vehicle_assignments Driver ↔ vehicle link driver_id, vehicle_id, assigned_at, released_at
DriverUnavailability driver_unavailabilities Driver time-off driver_id, start_date, end_date, reason
User users Customers / shippers name, phone, phone_country, email, company_name

Planned Domain (Not Yet in API)

These models exist in the database and will power shipment/route features. No HTTP endpoints exist for them yet.

Shipment (shipments)

Core delivery order placed by a customer.

Field Type Enum / Notes
shipment_number string Unique identifier
user_id integer FK → users
user_name string Denormalized customer name
delivery_date datetime Target delivery date
status string ShipmentStatuses
type string ShipmentTypes
payment_type string PaymentTypes
cod_amount float Required when payment is COD
total_weight float kg
total_volume float
notes string Optional

Relationships:

  • user → User
  • shipment_items → ShipmentItem[]
  • shipment_stops → ShipmentStop[]
  • shipment_events → ShipmentEvent[]
  • proof_of_deliveries → ProofOfDelivery[]
  • failed_deliveries → FailedDelivery[]

ShipmentItem (shipment_items)

Individual items within a shipment.

Field Type Notes
shipment_id integer FK → shipments
name string Item name
weight float kg
length, width, height float Dimensions
quantity integer
description string Optional
declared_value float Optional

ShipmentStop (shipment_stops)

Pickup, delivery, or intermediate stops on a shipment route.

Field Type Enum / Notes
shipment_id integer FK → shipments
type string ShipmentStopTypes
contact_name string
phone, phone_country string
country, city, area string Address components
street, building, floor, apartment string
lat, lng float Coordinates
full_address string Formatted address
sequence integer Order within shipment

ShipmentEvent (shipment_events)

Audit log / timeline entries for a shipment.

Field Type Enum / Notes
shipment_id integer FK → shipments
event_type string ShipmentEvents
data string JSON payload (optional)
causer_type, causer_id string, int Polymorphic causer
causer_name string Denormalized causer name
notes string Optional

Route (routes)

Optimized daily route assigned to a driver and vehicle.

Field Type Enum / Notes
driver_id integer FK → drivers
vehicle_id integer FK → vehicles
route_date datetime Date of the route
status string RouteStatuses
total_stops integer
optimized_distance float km
duration float minutes

Relationships:

  • driver → Driver
  • vehicle → Vehicle
  • route_stops → RouteStop[]
  • route_assignments → RouteAssignment[]

RouteStop (route_stops)

An ordered stop on a driver's route, linked to a shipment stop.

Field Type Enum / Notes
route_id integer FK → routes
shipment_stop_id integer FK → shipment_stops
status string RouteStopStatuses
distance_from_previous_km float
estimated_minutes_from_previous float
arrived_at, completed_at datetime
sequence integer Order on route

RouteAssignment (route_assignments)

Tracks driver assignment history to a route.

Field Type Notes
route_id integer FK → routes
driver_id integer FK → drivers
assigned_at datetime
unassigned_at datetime Null while active

ProofOfDelivery (proof_of_deliveries)

Delivery confirmation record.

Field Type Notes
shipment_id integer FK → shipments
receiver_name string
receiver_phone, receiver_phone_country string
delivered_at datetime
notes string
created_by_type, created_by_id string, int Polymorphic creator
created_by_name string

FailedDelivery (failed_deliveries)

Record of a failed delivery attempt.

Field Type Notes
shipment_id integer FK → shipments
reason_code string Failure reason code
notes string
created_by_type, created_by_id string, int Polymorphic creator
created_by_name string

StaticContent (static_contents)

Key-value CMS / app configuration store (not yet in API).

Field Type Notes
key string Primary key; see StaticContentTypes
value string Plain text or JSON-encoded value

Entity Relationship Diagram (Overview)

TEXT
User ──< Shipment ──< ShipmentItem
                  ──< ShipmentStop ──< RouteStop >── Route >── Driver
                  ──< ShipmentEvent                  └── Vehicle
                  ──< ProofOfDelivery
                  ──< FailedDelivery

Driver ──< DriverVehicleAssignment >── Vehicle
       ──< DriverUnavailability
       ──< RouteAssignment >── Route

Admin ──< Role ──< Permission

Enums Reference

All enum values are snake_case strings. Store them as string constants in the frontend.

OwnerTypes

Used in API today — vehicle ownership.

Value Arabic Label Rules
company الشركة owner_id must be null
driver سائق owner_id must be a valid driver ID

API returns both owner_type (value) and owner_type_label (Arabic label).

Suggested TypeScript:

TYPESCRIPT
type OwnerType = 'company' | 'driver';

ShipmentStatuses

Planned — shipment lifecycle state.

Value Arabic Label Suggested UI Color
draft مسودة secondary
pending في الانتظار warning
assigned تم التخصيص لسائق primary
pickup_in_progress قيد التحصيل warning
picked_up تم التحصيل success
at_hub في المخزن info
out_for_delivery قيد التسليم primary
delivered تم التسليم success
delivery_failed فشل التسليم danger
returned مرتجع warning
cancelled تم الإلغاء danger

Typical flow:

TEXT
draft → pending → assigned → pickup_in_progress → picked_up → at_hub
  → out_for_delivery → delivered
                                    ↘ delivery_failed → returned
Any state → cancelled

ShipmentTypes

Value Arabic Label
domestic محلي
international دولية

ShipmentStopTypes

Value Arabic Label Description
pickup تحصيل Collect goods from sender
delivery تسليم Deliver to recipient
stop موقف Intermediate stop

PaymentTypes

Value Arabic Label Notes
cod عند الاستلام Cash on delivery; requires cod_amount
prepaid مقدما Paid upfront

RouteStatuses

Value Arabic Label
draft مسودة
assigned تم تخصيص السائق
active فعال
completed مكتمل
cancelled ملغي

RouteStopStatuses

Value Arabic Label
pending قيد الانتظار
arrived تم الوصول
completed مكتمل
failed فشل
skipped تخطي

ShipmentEvents

Timeline event types logged on shipments.

Value Arabic Label
shipment_created تم إنشاء الشحنة
driver_assigned تم تخصيص السائق
route_created تم إنشاء الرحلة
pickup_started تم بدء التحصيل
pickup_completed تم إنهاء التحصيل
delivery_failed فشل التسليم
pod_uploaded تم تحميل إثبات التوصيل

MediaTypes

Internal media collection names (used for image uploads).

Value Used For
admin_image Admin profile photos
driver_image Driver profile photos
user_image User profile photos (future)

StaticContentTypes

CMS / app configuration keys (not yet in API).

Value Arabic Label
privacy_policy سياسة الخصوصية
commission عمولة الإدارة
social_media المواقع الاجتماعية
android_app_version نسخة التطبيق للاندرويد
ios_app_version نسخة التطبيق لل IOS
android_provider_app_version نسخة التطبيق للاندرويد للمزود
ios_provider_app_version نسخة التطبيق لل IOS للمزود

NotificationTypes

Push notification categories (PHP backed enum).

Value
general
wallet_deposit
wallet_withdraw
new_offer
offer_accepted
offer_rejected
order_status_changed
new_order

Working Days (not a PHP enum)

Driver schedule days. Accepted as array values in create/update requests.

Value Day
sun Sunday
mon Monday
tue Tuesday
wed Wednesday
thu Thursday
fri Friday
sat Saturday

Suggested TypeScript:

TYPESCRIPT
type WorkingDay = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat';

Permissions Reference

Permissions control what each admin can do. Check them client-side to show/hide UI elements.

Naming Convention

TEXT
{Action}:{Model}
Action Description
ViewAny List/index access
View View single record
Create Create new record
Update Edit existing record
Delete Delete record
Reorder Reorder records (if applicable)

Examples:

  • ViewAny:Driver — can list drivers
  • Create:Vehicle — can create vehicles
  • Update:Admin — can edit admins
  • Delete:Role — can delete roles

Custom Permissions

These do not map to a CRUD model:

Permission Description
Export:Reports Export reports
Send:Notifications Send push notifications
Manage:AppSettings Manage app settings
Assign:Routes Assign routes to drivers
Track:Deliveries Track live deliveries

How to Check Permissions on Frontend

After login, inspect data.admin.permission_groups:

TYPESCRIPT
function hasPermission(
  permissionGroups: PermissionGroup[],
  permissionName: string
): boolean {
  return permissionGroups.some(group =>
    group.permissions.some(p => p.name === permissionName)
  );
}

// Usage
if (hasPermission(admin.permission_groups, 'Create:Driver')) {
  // Show "Add Driver" button
}

Super admin role has all permissions implicitly.


Error Handling

HTTP Status Codes

Code Meaning When
200 Success Normal operation
401 Unauthorized Missing/invalid permission, wrong credentials
403 Forbidden Not authenticated (no/invalid token)
404 Not Found Route or resource not found
422 Validation Error Invalid request body/query
500 Server Error Unexpected failure

Common Error Messages

Message Key English Text Status
credentialsError Wrong Credentials 401
Unauthenticated Please login first 403
Unauthorized You do not have permissions to perform this action 401
cannotDeleteCurrentAdmin You cannot delete your own admin account 403
cannotModifySuperAdmin Super admin role cannot be modified 403
vehicleAlreadyAssigned This vehicle is already assigned to a driver 422
vehicleAssignmentNotFound No active vehicle assignment found for this driver 404
couldNotCreateToken Could not create authentication token 500
couldNotLogout Could not log out 500

Validation errors return the first validation message as message.

Error Response Shape

JSON
{
  "status": "Error",
  "message": "Wrong Credentials",
  "data": null,
  "statusCode": 401
}

Frontend Integration Checklist

  • Store JWT from login response (data.token)
  • Send Authorization: Bearer {token} on every authenticated request
  • Set Accept-Language: en or ar for localized messages
  • Use POST for all updates (not PUT/PATCH)
  • Use multipart/form-data when uploading image fields
  • Gate UI by checking permission_groups from login/profile response
  • Handle the standard { status, message, data, statusCode } envelope
  • Use paginated endpoints with page query param for tables
  • Pass search, sort_by, sort_direction for filterable lists
  • For vehicles: enforce owner_id rules based on owner_type
  • For drivers: send working_days as array; times as HH:MM or HH:MM:SS
  • Shipment/route features: use enums from this doc but wait for backend API routes

Route Index (Quick Reference)

Method Path
POST /api/admin/login
POST /api/admin/logout
GET /api/admin/profile
POST /api/admin/profile
GET /api/admin/permissions
GET/POST/DELETE /api/admin/roles, /api/admin/roles/paginated, /api/admin/roles/{id}, /api/admin/roles/{id}/permissions
GET/POST/DELETE /api/admin/admins, /api/admin/admins/paginated, /api/admin/admins/{id}
GET/POST/DELETE /api/admin/drivers, /api/admin/drivers/paginated, /api/admin/drivers/{id}
GET/POST /api/admin/drivers/{id}/vehicle-assignments, .../assign, .../unassign
GET /api/admin/drivers/{id}/unavailabilities
GET/POST/DELETE /api/admin/vehicles, /api/admin/vehicles/paginated, /api/admin/vehicles/{id}
GET/POST/DELETE /api/admin/driver-unavailabilities, .../paginated, .../{id}
GET/POST/DELETE /api/admin/users, /api/admin/users/paginated, /api/admin/users/{id}

Generated from the shipping_system codebase. Last updated: June 2026.