API Documentation
The Octal HR REST API gives third-party systems programmatic access to all HR data. Integrate ERPs, accounting platforms, payroll exporters, and custom dashboards using API Key + Secret authentication. All responses are JSON.
Overview
Third-party integrations authenticate as an API User — a dedicated credential set created and managed by an Octal HR administrator. API Users carry a scoped role that determines exactly which modules and operations are accessible.
token_type: api_user
Post your api_key and api_secret to /auth/token to receive a
short-lived JWT. Include that JWT as Authorization: Bearer <token> on every subsequent
request. Tokens expire after 1 hour; use /auth/refresh to renew without re-authenticating.
Keep secrets server-side. Never embed your API Key, API Secret, or any token in client-side JavaScript, mobile source code, or public repositories. Always use environment variables or a secrets manager.
Base URL
All v1 API requests are prefixed with:
https://api.octalhr.com/v1/
All endpoints accept and return application/json. Include the following headers on every authenticated request:
Content-Type: application/json Accept: application/json Authorization: Bearer <access_token>
All list endpoints support pagination via ?page=1&per_page=25 query parameters. Responses include a meta object with total, page, per_page, and last_page.
Authentication
Every protected endpoint requires a valid JWT access token in the
Authorization: Bearer header. Tokens are valid for 1 hour.
Use the refresh endpoint to obtain a new pair without re-posting credentials.
On success, every auth endpoint returns the same token envelope:
{
"success": true,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "a3f9d2e1c8b...",
"token_type": "Bearer",
"expires_in": 3600
}
}
Auth Endpoints
POST
/api/v1/auth/token
Exchanges an API Key + API Secret for a JWT pair. The API User must be created and activated by an admin — see Creating API Users.
Request Body
{
"api_key": "hrms_4a7f2e9d1c3b8a6e...", // required
"api_secret": "9d3f1a7c2e8b4d6a..." // required — shown once at creation
}
Success Response 200
{
"success": true,
"data": {
"access_token": "eyJ...",
"refresh_token": "c9e2...",
"token_type": "Bearer",
"expires_in": 3600,
"api_user": { "id": 1, "name": "ERP Connector" },
"tenant": { "id": 4 }
}
}
IP Whitelisting: API Users can optionally have an IP whitelist configured. Requests from non-whitelisted IPs receive 403 Forbidden.
POST
/api/v1/auth/refresh
Exchanges a valid refresh token for a new access + refresh token pair. The old refresh token is immediately revoked (token rotation). Refresh tokens expire after 30 days of inactivity.
{
"refresh_token": "a3f9d2e1c8b..." // required
}
Returns the same token envelope as /auth/token.
POST
/api/v1/auth/logout
🔒 Auth required
Revokes the provided refresh token. The current access token remains valid until its natural expiry (max 1 hour). Call this when your integration session ends.
{
"refresh_token": "a3f9d2e1c8b..."
}
Returns 200 with "message": "Logged out successfully." even if the token was already revoked.
hr.employees.*
Full CRUD for employee records including termination, reinstatement, and sub-resources like documents, bank accounts, and emergency contacts.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/employees | List employees (paginated). Filter by ?status=active&department_id=3 | hr.employees.view |
| POST | /api/v1/hr/employees | Create a new employee record | hr.employees.add |
| GET | /api/v1/hr/employees/{id} | Get single employee with full profile | hr.employees.view |
| PATCH | /api/v1/hr/employees/{id} | Update employee fields (partial) | hr.employees.edit |
| DELETE | /api/v1/hr/employees/{id} | Archive employee record | hr.employees.delete |
| POST | /api/v1/hr/employees/{id}/terminate | Terminate employee — requires termination_date, reason | hr.employees.terminate |
| POST | /api/v1/hr/employees/{id}/reinstate | Reinstate a terminated employee | hr.employees.edit |
| GET | /api/v1/hr/employees/{id}/documents | List documents attached to employee | hr.employees.view |
| GET | /api/v1/hr/employees/{id}/bank-accounts | Employee bank accounts for salary disbursement | hr.employees.view |
| GET | /api/v1/hr/employees/{id}/emergency-contacts | Emergency contacts on file | hr.employees.view |
Example — List active employees
GET /v1/hr/employees?status=active&per_page=50 Authorization: Bearer eyJ...
{
"success": true,
"data": [
{
"id": 42,
"employee_code": "EMP-0042",
"full_name": "Sara Khan",
"email": "sara.khan@company.com",
"department": "Finance",
"designation": "Senior Accountant",
"hire_date": "2022-03-01",
"status": "active"
}
],
"meta": { "total": 84, "page": 1, "per_page": 50, "last_page": 2 }
}
hr.employees.profile.*
CRUD for structured profile sections: work history, education, skills, dependants, certifications, and distinctions. All routes are nested under /employees/{id}/.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/employees/{id}/work-history | List work history entries | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/work-history | Add work history entry | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/work-history/{rid} | Update work history entry | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/work-history/{rid} | Delete work history entry | hr.employees.profile.delete |
| GET | /api/v1/employees/{id}/education | List education records | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/education | Add education record | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/education/{rid} | Update education record | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/education/{rid} | Delete education record | hr.employees.profile.delete |
| GET | /api/v1/employees/{id}/skills | List skills | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/skills | Add skill | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/skills/{rid} | Update skill | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/skills/{rid} | Delete skill | hr.employees.profile.delete |
| GET | /api/v1/employees/{id}/dependants | List dependants | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/dependants | Add dependant | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/dependants/{rid} | Update dependant | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/dependants/{rid} | Delete dependant | hr.employees.profile.delete |
| GET | /api/v1/employees/{id}/certifications | List certifications | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/certifications | Add certification | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/certifications/{rid} | Update certification | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/certifications/{rid} | Delete certification | hr.employees.profile.delete |
| GET | /api/v1/employees/{id}/distinctions | List distinctions / awards | hr.employees.profile.view |
| POST | /api/v1/employees/{id}/distinctions | Add distinction | hr.employees.profile.add |
| PATCH | /api/v1/employees/{id}/distinctions/{rid} | Update distinction | hr.employees.profile.edit |
| DELETE | /api/v1/employees/{id}/distinctions/{rid} | Delete distinction | hr.employees.profile.delete |
Key fields — work-history POST body
{
"company_name": "Acme Corp", // required
"designation": "Team Lead",
"department": "Engineering",
"from_date": "2019-01-01", // required
"to_date": "2022-12-31",
"responsibilities": "Led a team of 6...",
"reason_for_leaving": "Better opportunity",
"last_salary": 120000,
"reference_name": "Jane Doe",
"reference_contact": "+1-555-000-1234"
}
hr.departments.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/departments | List all departments | hr.departments.view |
| POST | /api/v1/hr/departments | Create department | hr.departments.add |
| GET | /api/v1/hr/departments/{id} | Get department detail | hr.departments.view |
| PATCH | /api/v1/hr/departments/{id} | Update department | hr.departments.edit |
| DELETE | /api/v1/hr/departments/{id} | Delete department | hr.departments.delete |
hr.designations.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/designations | List all designations | hr.designations.view |
| POST | /api/v1/hr/designations | Create designation | hr.designations.add |
| GET | /api/v1/hr/designations/{id} | Get designation detail | hr.designations.view |
| PATCH | /api/v1/hr/designations/{id} | Update designation | hr.designations.edit |
| DELETE | /api/v1/hr/designations/{id} | Delete designation | hr.designations.delete |
hr.shifts.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/shifts | List all shifts | hr.shifts.view |
| POST | /api/v1/hr/shifts | Create shift | hr.shifts.add |
| GET | /api/v1/hr/shifts/{id} | Get shift detail | hr.shifts.view |
| PATCH | /api/v1/hr/shifts/{id} | Update shift | hr.shifts.edit |
| DELETE | /api/v1/hr/shifts/{id} | Delete shift | hr.shifts.delete |
hr.holidays.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/holidays | List holidays. Filter by ?year=2026 | hr.holidays.view |
| POST | /api/v1/hr/holidays | Create holiday | hr.holidays.add |
| GET | /api/v1/hr/holidays/{id} | Get holiday detail | hr.holidays.view |
| PATCH | /api/v1/hr/holidays/{id} | Update holiday | hr.holidays.edit |
| DELETE | /api/v1/hr/holidays/{id} | Delete holiday | hr.holidays.delete |
hr.orgchart.view
Read-only org chart endpoints. Returns the reporting hierarchy as a nested tree, or a flat node for a single employee.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/hr/org-chart | Full org tree (nested JSON) | hr.orgchart.view |
| GET | /api/v1/hr/org-chart/employee/{id} | Single employee node with direct reports | hr.orgchart.view |
| GET | /api/v1/hr/org-chart/search | Search employees in tree by ?q=sara | hr.orgchart.view |
attendance.*
Attendance records track daily check-in/check-out events per employee. Filter by ?employee_id=42&from=2026-05-01&to=2026-05-31.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/attendance | List attendance records (paginated) | attendance.view |
| POST | /api/v1/attendance | Post a new attendance record | attendance.add |
| GET | /api/v1/attendance/{id} | Get single attendance record | attendance.view |
| PATCH | /api/v1/attendance/{id} | Edit attendance record (regularization) | attendance.edit |
| DELETE | /api/v1/attendance/{id} | Delete attendance record | attendance.delete |
attendance.overtime.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/attendance/overtime | List overtime entries | attendance.overtime.view |
| POST | /api/v1/attendance/overtime | Create overtime entry | attendance.overtime.add |
| PATCH | /api/v1/attendance/overtime/{id} | Update overtime entry | attendance.overtime.edit |
| DELETE | /api/v1/attendance/overtime/{id} | Delete overtime entry | attendance.overtime.delete |
attendance.lop.*
LOP records are deducted from the employee's payslip for the applicable period.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/attendance/lop | List LOP entries | attendance.lop.view |
| POST | /api/v1/attendance/lop | Create LOP entry | attendance.lop.add |
| PATCH | /api/v1/attendance/lop/{id} | Update LOP entry | attendance.lop.edit |
| DELETE | /api/v1/attendance/lop/{id} | Delete LOP entry | attendance.lop.delete |
leave.types.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/leave/types | List all leave types | leave.types.view |
| POST | /api/v1/leave/types | Create leave type | leave.types.add |
| GET | /api/v1/leave/types/{id} | Get leave type detail | leave.types.view |
| PATCH | /api/v1/leave/types/{id} | Update leave type | leave.types.edit |
| DELETE | /api/v1/leave/types/{id} | Delete leave type | leave.types.delete |
leave.allocations.*
Allocate leave entitlements to employees or groups for a specific leave type and period.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/leave/allocations | List allocations. Filter by ?employee_id=42 | leave.allocations.view |
| POST | /api/v1/leave/allocations | Create allocation | leave.allocations.add |
| GET | /api/v1/leave/allocations/{id} | Get allocation detail | leave.allocations.view |
| PATCH | /api/v1/leave/allocations/{id} | Update allocation | leave.allocations.edit |
| DELETE | /api/v1/leave/allocations/{id} | Delete allocation | leave.allocations.delete |
leave.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/leave/requests | List leave requests. Filter by ?status=pending | leave.view |
| POST | /api/v1/leave/requests | Submit a leave request | leave.add |
| GET | /api/v1/leave/requests/{id} | Get leave request detail | leave.view |
| POST | /api/v1/leave/requests/{id}/approve | Approve pending leave request | leave.approve |
| POST | /api/v1/leave/requests/{id}/reject | Reject pending leave request — requires reason | leave.approve |
| DELETE | /api/v1/leave/requests/{id} | Cancel / delete leave request | leave.delete |
leave.view
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/leave/balances | All employee balances for current year | leave.view |
| GET | /api/v1/leave/balances/{employee_id} | Balances for a specific employee | leave.view |
Example response — employee balances
{
"success": true,
"data": [
{ "leave_type": "Annual", "allocated": 21, "used": 5, "balance": 16 },
{ "leave_type": "Sick", "allocated": 10, "used": 2, "balance": 8 },
{ "leave_type": "Casual", "allocated": 7, "used": 0, "balance": 7 }
]
}
payroll.structures.*
Salary structures define the components (basic, HRA, allowances, deductions) that make up an employee's pay package.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/structures | List salary structures | payroll.structures.view |
| POST | /api/v1/payroll/structures | Create salary structure | payroll.structures.add |
| GET | /api/v1/payroll/structures/{id} | Get structure with component breakdown | payroll.structures.view |
| PATCH | /api/v1/payroll/structures/{id} | Update structure | payroll.structures.edit |
| DELETE | /api/v1/payroll/structures/{id} | Delete structure | payroll.structures.delete |
payroll.runs.*
A payroll run processes salaries for a given period. Runs go through draft → processed → approved → finalized states.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/runs | List payroll runs | payroll.runs.view |
| POST | /api/v1/payroll/runs | Create a new payroll run draft | payroll.runs.add |
| GET | /api/v1/payroll/runs/{id} | Get run detail with summary totals | payroll.runs.view |
| POST | /api/v1/payroll/runs/{id}/process | Process payroll (calculate payslips) | payroll.runs.edit |
| POST | /api/v1/payroll/runs/{id}/approve | Approve a processed run | payroll.runs.approve |
| POST | /api/v1/payroll/runs/{id}/reject | Reject run — requires reason | payroll.runs.approve |
| DELETE | /api/v1/payroll/runs/{id} | Delete draft run | payroll.runs.delete |
payroll.payslips.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/payslips | List payslips. Filter by ?employee_id=42&run_id=7 | payroll.payslips.view |
| GET | /api/v1/payroll/payslips/{id} | Get payslip with earnings/deductions breakdown | payroll.payslips.view |
| GET | /api/v1/payroll/payslips/{id}/pdf | Download payslip as PDF (binary response) | payroll.payslips.view |
payroll.adjustments.*
One-off additions or deductions applied to a specific payroll run (bonuses, penalties, reimbursements).
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/adjustments | List adjustments. Filter by ?run_id=7 | payroll.adjustments.view |
| POST | /api/v1/payroll/adjustments | Create adjustment — requires employee_id, run_id, type (addition|deduction), amount | payroll.adjustments.add |
| PATCH | /api/v1/payroll/adjustments/{id} | Update adjustment | payroll.adjustments.edit |
| DELETE | /api/v1/payroll/adjustments/{id} | Delete adjustment | payroll.adjustments.delete |
payroll.payments.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/payments | List payment vouchers | payroll.payments.view |
| POST | /api/v1/payroll/payments | Create payment voucher for a finalized run | payroll.payments.add |
| GET | /api/v1/payroll/payments/{id} | Get voucher detail | payroll.payments.view |
payroll.wage.*
Tenant-level wage settings — minimum wage, overtime multipliers, and currency configuration used in payroll calculations.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/wage-config | Get current wage configuration | payroll.wage.view |
| PATCH | /api/v1/payroll/wage-config | Update wage configuration | payroll.wage.edit |
payroll.advances.*
Short-term salary advances paid to employees ahead of their pay date. Recovered automatically from the next payslip.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/advances | List advances. Filter by ?employee_id=42&status=pending | payroll.advances.view |
| POST | /api/v1/payroll/advances | Create advance — requires employee_id, amount, advance_date | payroll.advances.add |
| POST | /api/v1/payroll/advances/{id}/cancel | Cancel a pending advance | payroll.advances.delete |
| DELETE | /api/v1/payroll/advances/{id} | Delete advance record | payroll.advances.delete |
payroll.arrears.*
Arrears are supplementary amounts added to a payroll run to cover under-payments from previous periods.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payroll/runs/{runId}/arrears | List arrears for a payroll run | payroll.arrears.view |
| POST | /api/v1/payroll/runs/{runId}/arrears | Add arrear entry to run | payroll.arrears.add |
| DELETE | /api/v1/payroll/runs/{runId}/arrears/{id} | Remove arrear entry | payroll.arrears.delete |
payroll.approval.*
Configure multi-level approval chains for payroll runs. Each level defines a role that must approve before the run advances.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/approval-chain | Get current approval chain levels | payroll.approval.view |
| POST | /api/v1/settings/approval-chain | Save approval chain (replaces all levels) | payroll.approval.edit |
loans.*
Employee loan management with EMI, bullet, or manual repayment schedules. Loans integrate with payroll for automatic EMI deductions.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/loans | List loans. Filter by ?employee_id=42&status=active | loans.view |
| POST | /api/v1/loans | Create loan application | loans.add |
| GET | /api/v1/loans/{id} | Get loan detail with repayment schedule | loans.view |
| PATCH | /api/v1/loans/{id} | Edit loan details (draft only) | loans.edit |
| DELETE | /api/v1/loans/{id} | Delete loan (draft only) | loans.delete |
| POST | /api/v1/loans/{id}/approve | Approve loan application | loans.approve |
| POST | /api/v1/loans/{id}/disburse | Mark loan as disbursed | loans.disburse |
| POST | /api/v1/loans/{id}/restructure | Restructure active loan schedule | loans.restructure |
| GET | /api/v1/loans/reports | Loan summary report (outstanding, EMI, etc.) | loans.reports.view |
Loan — POST body
{
"employee_id": 42, // required
"loan_type_id": 2,
"amount": 500000, // required
"repayment_type": "emi", // emi | bullet | manual
"emi_months": 12,
"interest_rate": 0, // percent per annum
"start_date": "2026-06-01"
}
tax.regimes.*
Tax regimes define the slab structure applied during payslip tax computation. Multiple regimes can exist simultaneously (e.g., different fiscal years).
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/tax/regimes | List tax regimes | tax.regimes.view |
| POST | /api/v1/tax/regimes | Create tax regime | tax.regimes.add |
| GET | /api/v1/tax/regimes/{id} | Get regime with slab detail | tax.regimes.view |
| PATCH | /api/v1/tax/regimes/{id} | Update regime | tax.regimes.edit |
| DELETE | /api/v1/tax/regimes/{id} | Delete regime | tax.regimes.delete |
tax.profiles.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/tax/profiles | List employee tax profiles | tax.profiles.view |
| POST | /api/v1/tax/profiles | Create tax profile for employee | tax.profiles.add |
| GET | /api/v1/tax/profiles/{id} | Get profile detail | tax.profiles.view |
| PATCH | /api/v1/tax/profiles/{id} | Update tax profile | tax.profiles.edit |
tax.reports.view
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/tax/reports | Tax deduction summary report. Filter by ?year=2026 | tax.reports.view |
payments.banks.*
Company bank accounts used for salary disbursement and payment vouchers.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/payments/bank-accounts | List bank accounts | payments.banks.view |
| POST | /api/v1/payments/bank-accounts | Add bank account | payments.banks.add |
| GET | /api/v1/payments/bank-accounts/{id} | Get account detail | payments.banks.view |
| PATCH | /api/v1/payments/bank-accounts/{id} | Update bank account | payments.banks.edit |
| DELETE | /api/v1/payments/bank-accounts/{id} | Remove bank account | payments.banks.delete |
tasks.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/tasks | List tasks. Filter by ?assigned_to=42&status=open | tasks.view |
| POST | /api/v1/tasks | Create task — requires title, assigned_to | tasks.add |
| GET | /api/v1/tasks/{id} | Get task detail | tasks.view |
| PATCH | /api/v1/tasks/{id} | Update task | tasks.edit |
| DELETE | /api/v1/tasks/{id} | Delete task | tasks.delete |
| POST | /api/v1/tasks/{id}/complete | Mark task as complete | tasks.edit |
surveys.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/surveys | List surveys | surveys.view |
| POST | /api/v1/surveys | Create survey with questions | surveys.add |
| GET | /api/v1/surveys/{id} | Get survey with questions | surveys.view |
| PATCH | /api/v1/surveys/{id} | Update survey | surveys.edit |
| DELETE | /api/v1/surveys/{id} | Delete survey | surveys.delete |
| GET | /api/v1/surveys/{id}/results | Aggregated response results | surveys.reports.view |
| POST | /api/v1/surveys/{id}/respond | Submit survey response on behalf of an employee | surveys.add |
visitors.*
Visitor management — log walk-in visits, manage a blacklist, and issue visitor badges.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/visitors | List visitor records. Filter by ?date=2026-05-13 | visitors.view |
| POST | /api/v1/visitors | Log a new visitor | visitors.add |
| GET | /api/v1/visitors/{id} | Get visitor detail | visitors.view |
| PATCH | /api/v1/visitors/{id} | Update visitor record (check-out time, etc.) | visitors.edit |
| DELETE | /api/v1/visitors/{id} | Delete visitor record | visitors.delete |
| GET | /api/v1/visitors/blacklist | List blacklisted visitors | visitors.blacklist.view |
| POST | /api/v1/visitors/blacklist | Add visitor to blacklist | visitors.blacklist.add |
| DELETE | /api/v1/visitors/blacklist/{id} | Remove from blacklist | visitors.blacklist.delete |
| GET | /api/v1/visitors/badges | List visitor badge configurations | visitors.badges.view |
| POST | /api/v1/visitors/badges | Issue visitor badge | visitors.badges.add |
| DELETE | /api/v1/visitors/badges/{id} | Revoke visitor badge | visitors.badges.delete |
settings.roles.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/roles | List all roles with permission slugs | settings.roles.view |
| POST | /api/v1/settings/roles | Create role with permissions array | settings.roles.add |
| GET | /api/v1/settings/roles/{id} | Get role detail | settings.roles.view |
| PATCH | /api/v1/settings/roles/{id} | Update role and its permissions | settings.roles.edit |
| DELETE | /api/v1/settings/roles/{id} | Delete role (only if unassigned) | settings.roles.delete |
settings.users.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/users | List admin users | settings.users.view |
| POST | /api/v1/settings/users | Create admin user | settings.users.add |
| GET | /api/v1/settings/users/{id} | Get user detail | settings.users.view |
| PATCH | /api/v1/settings/users/{id} | Update user | settings.users.edit |
| DELETE | /api/v1/settings/users/{id} | Delete user | settings.users.delete |
settings.general.view / edit
Tenant-level settings: company name, locale, currency, logo, payroll period, etc.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/general | Get all tenant settings | settings.general.view |
| PATCH | /api/v1/settings/general | Update tenant settings (partial) | settings.general.edit |
settings.lop.*
Loss-of-pay deduction rules per absence type, applied automatically in payroll calculation.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/lop-rules | List LOP rules | settings.lop.view |
| POST | /api/v1/settings/lop-rules | Create LOP rule | settings.lop.edit |
| GET | /api/v1/settings/lop-rules/{id} | Get rule detail | settings.lop.view |
| PATCH | /api/v1/settings/lop-rules/{id} | Update LOP rule | settings.lop.edit |
| DELETE | /api/v1/settings/lop-rules/{id} | Delete LOP rule | settings.lop.edit |
settings.overtime.*
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/settings/overtime-rules | List overtime rules | settings.overtime.view |
| POST | /api/v1/settings/overtime-rules | Create overtime rule | settings.overtime.edit |
| GET | /api/v1/settings/overtime-rules/{id} | Get rule detail | settings.overtime.view |
| PATCH | /api/v1/settings/overtime-rules/{id} | Update overtime rule | settings.overtime.edit |
| DELETE | /api/v1/settings/overtime-rules/{id} | Delete overtime rule | settings.overtime.edit |
logs.audit.view
Read-only audit trail of all create/update/delete actions across the system. Filter by ?user_id=5&module=payroll&from=2026-05-01.
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/logs/audit | Paginated audit log entries | logs.audit.view |
logs.email.view
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/logs/email | List sent emails with delivery status | logs.email.view |
logs.notifications.view
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/v1/notifications | List in-app notifications for the API User | logs.notifications.view |
| PATCH | /api/v1/notifications/{id}/read | Mark notification as read | logs.notifications.view |
Permission Slugs
Every endpoint is gated by a permission slug. Assign slugs to a role in Settings → Roles & Permissions,
then assign that role to an API User. The table below lists all slugs available for API Users
(marked is_api = 1 in the system).
HR
Attendance
Leave
Payroll
Loans
Tax
Other Modules
Settings & Logs
To see all permissions in the admin UI, go to Settings → Roles & Permissions → All Permissions.
Error Codes
All errors follow a consistent envelope:
{
"success": false,
"errors": [
{
"field": "email", // null for non-field errors
"message": "Email is required."
}
]
}
| Status | Meaning |
|---|---|
| 400 | Validation error — request body failed validation. The errors array contains field-level messages. |
| 401 | Unauthorized — missing, invalid, or expired JWT. Re-authenticate with /auth/token or refresh with /auth/refresh. |
| 403 | Forbidden — token is valid but the API User's role lacks the required permission slug, or the request IP is not whitelisted. |
| 404 | Not found — the requested resource does not exist or belongs to a different tenant. |
| 422 | Business rule violation — e.g., approving an already-approved leave, processing an already-processed payroll run, or disbursing an unapproved loan. |
| 429 | Rate limited — too many requests in a short window. Slow down and retry after the Retry-After header value. |
| 500 | Server error — unexpected internal failure. Contact support with the timestamp and request ID from the response. |
Creating API Users
Third-party systems authenticate as an API User — a dedicated credential set with its own scoped role. API Users are created by an administrator in the Octal HR panel. The API Secret is shown exactly once at creation — store it immediately.
POST /api/v1/auth/token to obtain a JWT on each session.Secret stored as a one-way hash. Octal HR stores only a bcrypt hash of the API Secret. If you lose it, use the Regenerate Secret button — this immediately revokes all existing tokens for that API User and issues new credentials.
Typical Integration Pattern
# 1. Authenticate once per session (token valid for 1 hour) import requests, os resp = requests.post("https://api.octalhr.com/v1/auth/token", json={ "api_key": os.environ["OCTALHR_API_KEY"], "api_secret": os.environ["OCTALHR_API_SECRET"], }) token = resp.json()["data"]["access_token"] headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"} # 2. Use the token on subsequent requests employees = requests.get( "https://api.octalhr.com/v1/hr/employees?status=active", headers=headers ).json() # 3. Refresh before expiry (or handle 401 → re-auth) refresh_resp = requests.post( "https://api.octalhr.com/v1/auth/refresh", json={"refresh_token": resp.json()["data"]["refresh_token"]} ) token = refresh_resp.json()["data"]["access_token"]
Contact us at info@octalhr.com or visit the Help Center for guides and tutorials.