Developer Guide

Mobile API Usage Guide

This section shows how a mobile client uses the currently implemented API. Replace https://jit.sa with the API base URL for the environment being used.

1. Set The API Base URL

API_BASE_URL = https://jit.sa/api/v1

All API requests should include:

Accept: application/json

Protected endpoints also require:

Authorization: Bearer {access_token}

Use HTTPS outside local development. Never log or store the access token in plain text.

2. Generate An Employee Token

Send the employee's login credentials to the public login endpoint:

POST /api/v1/auth/login
Content-Type: application/json
Accept: application/json
{
  "email": "employee@example.com",
  "password": "employee-password",
  "device_name": "iPhone 15"
}

Example with cURL:

curl -X POST "https://jit.sa/api/v1/auth/login" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "employee@example.com",
    "password": "employee-password",
    "device_name": "iPhone 15"
  }'

Successful response:

{
  "data": {
    "token": "1|sanctum-token-value",
    "token_type": "Bearer",
    "abilities": [
      "employee:read",
      "employee:write"
    ],
    "employee": {
      "id": 25,
      "employee_id": "EMP-0025",
      "full_name": "Ahmed Ali",
      "email": "employee@example.com",
      "status": "active",
      "tenant": {
        "id": 3,
        "name": "Example Company",
        "slug": "example-company"
      }
    }
  },
  "meta": [],
  "links": [],
  "message": null
}

Store data.token securely. The mobile application must not send the employee ID or tenant ID during login or later requests.

3. Add The Token To Protected Requests

Create one shared authenticated HTTP client in the mobile application:

Authorization: Bearer 1|sanctum-token-value
Accept: application/json

Example:

curl "https://jit.sa/api/v1/me" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value"

If the token is missing, expired, or revoked, the API returns 401 Unauthorized. The app should clear the local token and send the employee back to login.

4. Retrieve Employee Details

Use either endpoint below:

GET /api/v1/me
GET /api/v1/me/profile
curl "https://jit.sa/api/v1/me/profile" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value"

The response contains employee-safe information such as:

  • Employee number and names
  • Work email and phone
  • Profile photo URL
  • Employment status and hire date
  • Tenant
  • Branch
  • Department
  • Job title
  • Group
  • Shift

The response does not allow the mobile app to edit identity, tenant, status, manager, salary, or payroll configuration fields.

5. Retrieve The Employee Dashboard

GET /api/v1/me/dashboard
curl "https://jit.sa/api/v1/me/dashboard" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value"

The dashboard response contains:

{
  "data": {
    "employee": {},
    "today_attendance": null,
    "leave_balances": [],
    "pending_requests_count": 0,
    "pending_approvals_count": 0,
    "unread_notifications_count": 0,
    "recent_payslip": null
  },
  "meta": [],
  "links": [],
  "message": null
}

Use this endpoint for the first mobile screen instead of making many separate requests during app startup.

6. Update Employee Contact Information

Only the fields below are editable through the profile endpoint:

PATCH /api/v1/me/profile
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
{
  "phone": "+966500000000",
  "emergency_contact_name": "Family Contact",
  "emergency_contact_phone": "+966511111111"
}

Do not send employee_id, tenant_id, full_name, email, status, or employment fields. They are not employee-editable API fields.

7. Change The Password

PATCH /api/v1/me/password
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
{
  "current_password": "old-password",
  "password": "new-password",
  "password_confirmation": "new-password"
}

The current password, confirmation, and minimum password policy are validated by the server.

8. Retrieve Attendance History

GET /api/v1/me/attendance
GET /api/v1/me/attendance/summary
GET /api/v1/me/attendance/{attendanceId}

Supported query parameters for history and summary:

from=2026-08-01
to=2026-08-31
status=present
per_page=25

Example:

curl "https://jit.sa/api/v1/me/attendance?from=2026-08-01&to=2026-08-31&status=present" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value"

The app should use the returned attendance ID when calling check-out.

9. Submit A Mobile Check-In

A check-in requires a unique Idempotency-Key request header:

POST /api/v1/me/attendance/check-in
Idempotency-Key: check-in-device-20260803-0001
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
{
  "client_time": "2026-08-03T08:02:11+03:00",
  "gps_location": "24.7136,46.6753",
  "notes": "Arrived at the office"
}

Example:

curl -X POST "https://jit.sa/api/v1/me/attendance/check-in" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: check-in-device-20260803-0001" \
  -d '{
    "client_time": "2026-08-03T08:02:11+03:00",
    "gps_location": "24.7136,46.6753",
    "notes": "Arrived at the office"
  }'

The server records the authoritative punch time. client_time is retained only as device metadata. If the same idempotency key is sent again, the existing attendance result is returned instead of creating a duplicate punch.

10. Submit A Mobile Check-Out

Use the attendance ID from the active attendance record:

POST /api/v1/me/attendance/{attendanceId}/check-out
Idempotency-Key: check-out-device-20260803-0001
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
curl -X POST "https://jit.sa/api/v1/me/attendance/501/check-out" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|sanctum-token-value" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: check-out-device-20260803-0001" \
  -d '{
    "client_time": "2026-08-03T17:01:45+03:00",
    "gps_location": "24.7136,46.6753",
    "notes": "Leaving the office"
  }'

Possible attendance conflict codes include:

  • ATTENDANCE_ALREADY_STARTED
  • CHECK_IN_REQUIRED
  • CHECK_OUT_BEFORE_CHECK_IN
  • ATTENDANCE_NOT_ACTIVE
  • IDEMPOTENCY_KEY_REUSED

These return HTTP 409 with the standard API error envelope.

11. Retrieve Leave Balances And History

Balances:

GET /api/v1/me/leave-balances

History:

GET /api/v1/me/leaves?status=approved&leave_type=annual&from=2026-01-01&to=2026-12-31
GET /api/v1/me/leaves/{leaveId}

Leave records are read-only. To request a new leave, retrieve the appropriate dynamic request type and submit an employee request instead.

12. Retrieve Dynamic Request Types

GET /api/v1/request-types
GET /api/v1/request-types/{requestTypeId}

The response includes:

  • Request type ID
  • Localized name and description
  • schema_version
  • Dynamic fields
  • Field types
  • Required flags
  • Select options
  • Conditional field information
  • Attachment requirements

The mobile app should cache the schema using its schema_version, but it must send the current version when submitting the request.

13. Submit A Dynamic Employee Request

First retrieve a request type. Then submit normalized field keys from its schema:

POST /api/v1/me/requests
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
{
  "request_type_id": 12,
  "schema_version": 3,
  "subject": "Annual leave request",
  "description": "Family vacation",
  "form_data": {
    "leave_type": "annual",
    "date_from": "2026-08-10",
    "date_to": "2026-08-12",
    "notes": "Family vacation"
  }
}

The server supplies employee_id, tenant_id, status, submitted time, approval state, and side-effect state. Do not send those fields from the app.

If the schema has changed, the API returns 409 SCHEMA_VERSION_MISMATCH. The app should retrieve the request type again and rebuild the form before retrying.

14. Upload A Request Attachment

Only the request owner can upload an attachment, and only while the request is pending:

POST /api/v1/me/requests/{requestId}/attachments
Content-Type: multipart/form-data
Accept: application/json
Authorization: Bearer {access_token}

Form field:

attachment: PDF, JPG, JPEG, PNG, DOC, or DOCX file, maximum 5 MB

15. Review And Decide On Approvals

List pending approvals:

GET /api/v1/me/approvals

Approve:

POST /api/v1/me/approvals/{requestId}/approve
Content-Type: application/json
{
  "comment": "Approved."
}

Reject:

POST /api/v1/me/approvals/{requestId}/reject
Content-Type: application/json
{
  "comment": "Please provide the required supporting document."
}

The rejection comment is required. Approval requests are restricted to the current approval level and repeated mobile decisions are idempotent.

16. Read Notifications

List notifications:

GET /api/v1/notifications?unread=true&per_page=20

Unread count:

GET /api/v1/notifications/unread-count

Mark one as read:

POST /api/v1/notifications/{notificationId}/read

Mark all as read:

POST /api/v1/notifications/read-all

Notifications are scoped to the authenticated user and tenant. The mobile app should use the notification UUID returned by the list endpoint.

17. Register A Mobile Push Device

POST /api/v1/me/devices
Content-Type: application/json
Accept: application/json
Authorization: Bearer {access_token}
{
  "device_id": "ios-device-unique-id",
  "device_name": "iPhone 15",
  "platform": "ios",
  "push_token": "provider-push-token",
  "app_version": "1.0.0"
}

The push token is encrypted at rest and is not returned in API responses. Registering the same device_id updates its current push token and last-seen time.

List registered mobile devices:

GET /api/v1/me/devices

Remove a device:

DELETE /api/v1/me/devices/{deviceId}

18. Manage Bearer Sessions

List the authenticated user's Sanctum sessions:

GET /api/v1/auth/devices

Revoke one session:

DELETE /api/v1/auth/devices/{tokenId}

The API only permits revoking tokens that belong to the authenticated user.

19. Handle API Errors

Validation errors use HTTP 422:

{
  "message": "The given data was invalid.",
  "errors": {
    "email": ["The email field is required."]
  },
  "code": "VALIDATION_ERROR"
}

Authentication errors use HTTP 401. Ownership and approval errors use HTTP 403 or 404. Business conflicts such as duplicate punches, stale schemas, overlapping work tasks, and invalid approval state use HTTP 409:

{
  "message": "A check-in is required before checking out.",
  "errors": [],
  "code": "CHECK_IN_REQUIRED"
}

The mobile client should use code for programmatic handling and message for a localized user-facing fallback.

20. Logout

POST /api/v1/auth/logout
Accept: application/json
Authorization: Bearer {access_token}

After a successful logout, delete the locally stored access token and stop using the associated session. The mobile push-device registration may remain available for future login, but the revoked bearer token cannot be reused.

21. OpenAPI Documentation

The current API documentation is generated by Scramble:

Interactive documentation: /docs/api
OpenAPI JSON: /docs/api.json

The OpenAPI document includes the current implemented routes only. Planned endpoints such as payroll, private documents, password reset, offline synchronization, and push delivery will appear after their routes are implemented.