API Reference
The Easy Manage HR REST API lets you build integrations, mobile apps, and automations on top of your HR data. All responses are JSON. The API is versioned — current version is v1.
{subdomain} with your company's
subdomain, e.g. acme.hr.techbloomsolutions.com.
Authentication
The API uses Laravel Sanctum Bearer tokens. Obtain a token via POST
/auth/login,
then include it in every subsequent request as an Authorization header.
Authorization: Bearer your_token_here
Content-Type: application/json
Accept: application/json
POST /auth/logout to revoke a token, or
POST /auth/refresh to rotate it. Store tokens securely — never expose them in
client-side code.
Error Codes
All errors follow a consistent envelope. The message field is
human-readable; use status for programmatic handling.
{
"success": false,
"message": "Unauthenticated.",
"errors": null,
"timestamp": "2024-06-07T10:30:00.000000Z"
}
| Status | Meaning | Common Cause |
|---|---|---|
| 200 | OK | Request succeeded. |
| 201 | Created | Resource created successfully. |
| 400 | Bad Request | Invalid or missing parameters. |
| 401 | Unauthenticated | Missing or invalid Bearer token. |
| 403 | Forbidden | Authenticated but not authorised for this action. |
| 404 | Not Found | Resource does not exist or is outside your tenant. |
| 422 | Unprocessable | Validation failed — check the errors object for field-level messages. |
| 500 | Server Error | Unexpected server error. Contact support if persistent. |
Pagination
List endpoints return paginated responses. Pass per_page (default 15,
max 100) and page query parameters.
{
"success": true,
"data": {
"data": [ /* array of items */ ],
"current_page": 1,
"last_page": 4,
"per_page": 15,
"total": 58,
"from": 1,
"to": 15
},
"message": "Employees retrieved successfully.",
"timestamp": "2024-06-07T10:30:00.000000Z"
}
Auth
Parameters
| Name | Type | Description | |
|---|---|---|---|
| string | required | User email address. | |
| password | string | required | User password. |
| device | string | optional | Device name for token labelling (e.g. "iPhone 15"). |
{
"email": "admin@acme.co.ke",
"password": "secret1234",
"device": "My App"
}
{
"success": true,
"data": {
"token": "1|abc123xyz...",
"user": {
"id": 1,
"name": "Alice Mwangi",
"email": "admin@acme.co.ke",
"roles": ["hr_manager"]
}
},
"message": "Login successful.",
"timestamp": "2024-06-07T10:30:00.000000Z"
}
Authorization: Bearer <token> header.
{
"success": true,
"message": "Logged out successfully.",
"timestamp": "2024-06-07T10:31:00.000000Z"
}
Authorization: Bearer <token> header.
{
"success": true,
"data": { "token": "2|newtoken..." },
"message": "Token refreshed.",
"timestamp": "2024-06-07T10:32:00.000000Z"
}
Employees
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| search | string | optional | Full-text search on name, email, or employee number. |
| department_id | integer | optional | Filter by department ID. |
| status | string | optional | active | inactive | terminated | on_leave |
| per_page | integer | optional | Results per page (default 15, max 100). |
| page | integer | optional | Page number (default 1). |
{
"success": true,
"data": {
"data": [{
"id": 42,
"employee_number": "EMP-0042",
"first_name": "James",
"last_name": "Otieno",
"email": "james.otieno@acme.co.ke",
"phone": "+254712345678",
"job_title": "Software Engineer",
"department_id": 3,
"status": "active",
"hire_date": "2022-03-01",
"department": { "id": 3, "name": "Engineering" }
}],
"total": 58, "per_page": 15, "current_page": 1
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| first_name | string | required | First name. |
| last_name | string | required | Last name. |
| string | required | Work email address (unique per tenant). | |
| phone | string | optional | Phone number (E.164 preferred). |
| job_title | string | optional | Job title / position. |
| department_id | integer | optional | Department ID. |
| hire_date | date | required | Hire date in YYYY-MM-DD format. |
| status | string | optional | active (default) | inactive | terminated | on_leave |
| national_id | string | optional | National ID number. |
| kra_pin | string | optional | KRA PIN for payroll tax computation. |
| nssf_number | string | optional | NSSF member number. |
| nhif_number | string | optional | NHIF / SHA member number. |
| bank_name | string | optional | Bank name for payroll disbursement. |
| bank_account | string | optional | Bank account number. |
| basic_salary | numeric | optional | Gross basic salary (KES). |
{
"first_name": "Jane",
"last_name": "Kamau",
"email": "jane.kamau@acme.co.ke",
"department_id": 3,
"hire_date": "2024-07-01",
"basic_salary": 85000,
"kra_pin": "A001234567B"
}
{
"success": true,
"data": { /* employee object */ },
"message": "Employee created successfully."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| id | integer | required | Employee ID. |
{
"success": true,
"data": { /* full employee object with department, user, etc. */ }
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| id | integer | required | Employee ID (path param). |
| first_name | string | optional | First name. |
| last_name | string | optional | Last name. |
| string | optional | Work email. | |
| department_id | integer | optional | Department ID. |
| status | string | optional | active | inactive | terminated | on_leave |
| basic_salary | numeric | optional | Updated gross salary. |
{
"status": "on_leave",
"basic_salary": 90000
}
{
"success": true,
"data": { /* updated employee object */ },
"message": "Employee updated successfully."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| id | integer | required | Employee ID. |
{
"success": true,
"message": "Employee deleted successfully."
}
Departments
Authorization: Bearer <token> header.
{
"success": true,
"data": [{
"id": 1, "name": "Headquarters",
"children": [{
"id": 2, "name": "Engineering",
"children": []
}]
}]
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| name | string | optional | Filter by name (partial match). |
| parent_id | integer | optional | Filter by parent department ID. |
| per_page | integer | optional | Results per page. |
{
"success": true,
"data": {
"data": [{
"id": 2, "name": "Engineering",
"parent_id": 1,
"employees_count": 14
}],
"total": 8
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| name | string | required | Department name (unique per tenant). |
| parent_id | integer | optional | Parent department ID for nested structures. |
| description | string | optional | Optional description. |
| manager_id | integer | optional | Employee ID of the department manager. |
{
"name": "Product Design",
"parent_id": 2,
"manager_id": 42
}
{
"success": true,
"data": { /* department object */ },
"message": "Department created successfully."
}
Attendance
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| start_date | date | optional | Filter from this date (YYYY-MM-DD). |
| end_date | date | optional | Filter to this date (YYYY-MM-DD). |
| department_id | integer | optional | Filter by department. |
| status | string | optional | present | absent | late | half_day |
| per_page | integer | optional | Results per page (default 15). |
{
"success": true,
"data": {
"data": [{
"id": 1001,
"employee_id": 42,
"date": "2024-06-07",
"clock_in": "08:02:00",
"clock_out": "17:01:00",
"status": "present",
"hours_worked": 8.98
}]
}
}
Authorization: Bearer <token> header.
{
"success": true,
"data": {
"date": "2024-06-07",
"total_employees": 58,
"present": 52,
"absent": 4,
"late": 2,
"on_leave": 3,
"records": [ /* today's attendance records */ ]
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| start_date | date | required | Report start date (YYYY-MM-DD). |
| end_date | date | required | Report end date (YYYY-MM-DD). |
| department_id | integer | optional | Limit to one department. |
| group_by | string | optional | day | week | month | employee | department |
{
"success": true,
"data": {
"period": { "start": "2024-06-01", "end": "2024-06-30" },
"summary": {
"total_days": 30,
"working_days": 21,
"attendance_rate": "94.3%"
},
"rows": [ /* grouped rows */ ]
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| employee_id | integer | required | Employee ID clocking in. |
| latitude | numeric | optional | GPS latitude (required if geofencing is active). |
| longitude | numeric | optional | GPS longitude (required if geofencing is active). |
| note | string | optional | Optional clock-in note. |
{
"employee_id": 42,
"latitude": -1.286389,
"longitude": 36.817223
}
{
"success": true,
"data": {
"id": 1002,
"employee_id": 42,
"date": "2024-06-07",
"clock_in": "08:05:12",
"status": "present"
},
"message": "Clock-in recorded."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| employee_id | integer | required | Employee ID clocking out. |
| latitude | numeric | optional | GPS latitude. |
| longitude | numeric | optional | GPS longitude. |
| note | string | optional | Optional clock-out note. |
{
"employee_id": 42
}
{
"success": true,
"data": {
"id": 1002,
"clock_out": "17:03:44",
"hours_worked": 8.97
},
"message": "Clock-out recorded."
}
Leave
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| employee_id | integer | optional | Filter by employee. |
| status | string | optional | pending | approved | rejected | cancelled |
| start_date | date | optional | From date (YYYY-MM-DD). |
| end_date | date | optional | To date (YYYY-MM-DD). |
| per_page | integer | optional | Results per page. |
{
"success": true,
"data": {
"data": [{
"id": 77,
"employee_id": 42,
"leave_type": { "id": 1, "name": "Annual Leave" },
"start_date": "2024-07-01",
"end_date": "2024-07-05",
"days": 5,
"status": "approved",
"reason": "Family holiday"
}]
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| employee_id | integer | required | Employee submitting the request. |
| leave_type_id | integer | required | Leave type ID (from /leaves/types). |
| start_date | date | required | First day of leave (YYYY-MM-DD). |
| end_date | date | required | Last day of leave (YYYY-MM-DD). |
| reason | string | optional | Reason for the leave. |
| notes | string | optional | Additional notes for approver. |
{
"employee_id": 42,
"leave_type_id": 1,
"start_date": "2024-07-01",
"end_date": "2024-07-05",
"reason": "Annual family holiday"
}
{
"success": true,
"data": { /* leave request object */ },
"message": "Leave request submitted successfully."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| id | integer | required | Leave request ID (path param). |
| comment | string | optional | Approver comment. |
{
"comment": "Approved. Enjoy your holiday!"
}
{
"success": true,
"message": "Leave request approved."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| employee_id | integer | required | Employee ID (path param). |
| year | integer | optional | Year (defaults to current year). |
{
"success": true,
"data": [{
"leave_type": "Annual Leave",
"entitled": 21,
"used": 5,
"pending": 0,
"remaining": 16
}]
}
Authorization: Bearer <token> header.
{
"success": true,
"data": [{
"id": 1,
"name": "Annual Leave",
"days_per_year": 21,
"is_paid": true,
"requires_approval": true
}]
}
Payroll
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| status | string | optional | draft | processing | completed | paid |
| year | integer | optional | Filter by year. |
| per_page | integer | optional | Results per page. |
{
"success": true,
"data": {
"data": [{
"id": 12,
"name": "June 2024",
"start_date": "2024-06-01",
"end_date": "2024-06-30",
"status": "completed",
"total_gross": 4935000,
"total_net": 3902000,
"employees_count": 58
}]
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| period_id | integer | required | Period ID (path param). |
| employee_ids | array | optional | Subset of employee IDs to process. Omit to process all. |
{
// omit employee_ids to process everyone in the period
"employee_ids": [42, 43, 44]
}
{
"success": true,
"data": {
"processed": 58,
"total_gross": 4935000,
"total_net": 3902000,
"total_paye": 890000,
"total_nssf": 52200,
"total_nhif": 90800
},
"message": "Payroll generated for 58 employees."
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| per_page | integer | optional | Results per page. |
{
"success": true,
"data": {
"data": [{
"id": 234,
"period_name": "June 2024",
"basic_salary": 85000,
"gross_pay": 95000,
"net_pay": 74250,
"paye": 15200,
"nssf": 900,
"nhif": 1570,
"download_url": "/api/v1/payroll/payslips/234/download"
}]
}
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| period_id | integer | optional | Specific period ID. |
| start_date | date | optional | Report range start. |
| end_date | date | optional | Report range end. |
| department_id | integer | optional | Filter by department. |
| format | string | optional | json (default) | csv |
{
"success": true,
"data": {
"periods": [{
"name": "June 2024",
"total_gross": 4935000,
"total_net": 3902000,
"total_paye": 890000
}]
}
}
Geofencing
Geofencing restricts clock-in/clock-out to approved physical locations. If geofencing is required for your organisation, clock-in requests must include valid coordinates.
Authorization: Bearer <token> header.
{
"success": true,
"data": [{
"id": 1,
"name": "Nairobi HQ",
"latitude": -1.286389,
"longitude": 36.817223,
"radius_m": 200,
"is_active": true
}]
}
Authorization: Bearer <token> header.
Parameters
| Name | Type | Description | |
|---|---|---|---|
| latitude | numeric | required | Device GPS latitude. |
| longitude | numeric | required | Device GPS longitude. |
{
"latitude": -1.286389,
"longitude": 36.817223
}
{
"success": true,
"data": {
"valid": true,
"geofence_id": 1,
"geofence_name": "Nairobi HQ",
"distance_m": 45.2
},
"message": "Location is within the allowed geofence."
}