KalHR stores one attendance record per employee per day. Clocking in writes Present or Late; when HR runs the
absence report for a day, everyone without a record gets Absent, Leave, Holiday or Excused. So a day's
records are complete once its report has finished, which the
attendance.absence_report.completed event tells you.
Records, reports and holidays need Attendance: View. Excused days (attendance exceptions) need the View Attendance Exceptions permission, which Attendance: View includes.
Attendance statuses
status | Number | Meaning |
|---|---|---|
Present | 0 | Clocked in on time |
Late | 1 | Clocked in after the policy's late cut-off |
Absent | 2 | Expected, and no clock-in or excuse |
Leave | 3 | On approved leave |
Holiday | 4 | A public or company holiday |
Excused | 5 | Covered by an approved attendance exception |
To filter, send the name or the number: Status=Absent or Status=2.
List attendance records
GET/v1/attendance/listKey needs Attendance: ViewReturns your organisation's attendance records, a page at a time, most recent day first.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
PageNumber | integer | 1 | The page to return |
PageSize | integer | 10 | Records per page |
StartDate, EndDate | date | Only days from and to these dates, inclusive, as YYYY-MM-DD | |
EmployeeId | uuid | Only this employee's records | |
CompanyId | uuid | Only this company. Ids come from Companies. | |
Department | uuid | Only this department | |
Unit | uuid | Only this unit | |
Status | name or integer | Only this status | |
WorkMode | name or integer | Remote (0), Onsite (1) or Hybrid (2) | |
ApplicableDay | name | Only this weekday, such as Monday | |
NeedsReview | boolean | true for clock-ins on fully excused days, which HR should look at | |
SearchTerm | string | Part of the employee's first or last name |
curl "https://api.kalhr.com/v1/attendance/list?StartDate=2026-10-01&EndDate=2026-10-31&Status=Absent&PageSize=100" \
-H "Authorization: Bearer $KALHR_API_KEY"Response
{
"isSuccessful": true,
"code": "00",
"message": "Success",
"data": {
"data": [
{
"id": "5c4b3a29-1807-4f6e-9d5c-4b3a29180706",
"employeeId": "4b9d2c1e-7a3f-4e8b-9c21-5d6e7f8a9b0c",
"firstName": "Ada",
"lastName": "Okafor",
"email": "ada.okafor@acme.ng",
"department": "Finance",
"role": "Payroll Officer",
"company": { "id": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b", "name": "Acme Nigeria Ltd" },
"attendanceDate": "2026-10-19",
"applicableDay": "Monday",
"trackingMethod": "ClockInClockOut",
"clockInTime": "08:52:10",
"clockOutTime": "17:31:44",
"status": "Late",
"totalWorkedHours": 8.66,
"workmode": "Hybrid",
"location": "12 Admiralty Way, Lekki, Lagos",
"needsReview": false
}
],
"pageNumber": 1,
"pageSize": 100,
"totalRecords": 1,
"totalPages": 1
}
}
| Field | Type | Description |
|---|---|---|
id | uuid | The attendance record |
employeeId, firstName, lastName, email | uuid, string | The employee |
department, role | string | Their first department and job role |
company | object | id, name |
attendanceDate | date | The day, as YYYY-MM-DD |
applicableDay | string | Its weekday |
trackingMethod | string | Their attendance policy's method: ClockInClockOut, or AttendanceOnly (presence only, no clock-out) |
clockInTime, clockOutTime | time | HH:mm:ss, in the company's local time. Missing when they didn't clock in or out. |
status | string | See statuses |
totalWorkedHours | number | Hours between clock-in and clock-out |
workmode | string | Remote, Onsite or Hybrid, on that day |
location | string | Where they clocked in, when recorded |
attendanceExceptionId | uuid | The exception excusing the day, if any |
excuseType | string | That exception's type, such as RemoteDay |
excusedHalfDay | string | FirstHalf or SecondHalf when only half the day is excused |
needsReview | boolean | true when they clocked in on a day that was fully excused |
Get a day's absence report
GET/v1/attendance/report/{id}Key needs Attendance: ViewThe totals for one day, once HR has run its absence report. The reportId in the
attendance.absence_report.completed event is the id here.
curl "https://api.kalhr.com/v1/attendance/report/9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a" \
-H "Authorization: Bearer $KALHR_API_KEY"Response
{
"isSuccessful": true,
"code": "00",
"message": "Success",
"data": {
"reportId": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
"reportDate": "2026-10-19",
"status": "Completed",
"isWorkDay": true,
"completedAt": "2026-10-19T18:05:12Z",
"totalEmployeesProcessed": 134,
"presentCount": 112,
"lateCount": 9,
"absentCount": 4,
"leaveCount": 6,
"holidayCount": 0,
"excusedCount": 3,
"excusedAbsenceCount": 9,
"unexcusedAbsenceCount": 4
}
}
| Field | Description |
|---|---|
reportDate | The day the report covers |
status | Completed when the counts are final |
isWorkDay | false when the day wasn't a working day |
totalEmployeesProcessed | Employees the report looked at |
presentCount, lateCount, absentCount, leaveCount, holidayCount, excusedCount | Employees with each status |
excusedAbsenceCount | Not expected in: leave, holiday and excused together |
unexcusedAbsenceCount | The same as absentCount |
List attendance exceptions
GET/v1/attendance/exceptionsKey needs Attendance: ViewAttendance exceptions are days HR (or a line manager, with approval) has excused: an excused absence, a day off, a remote day, or a closure. They change what a day counts as, so an excused employee is never Absent or Late.
Good to know
What an exception is for stays in KalHR. Keys don't receive the employee's reason, HR's remarks, the attachment or
the change history, and Search matches names only.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
PageNumber | integer | 1 | The page to return |
PageSize | integer | 20 | Exceptions per page, up to 100 |
From, To | date | Only exceptions covering any day in this range, as YYYY-MM-DD | |
EmployeeId | uuid | Only this employee's | |
CompanyId | uuid | Only this company's | |
DepartmentId | uuid | Only employees in this department | |
Status | name or integer | Pending (1), Approved (2), Rejected (3) or Cancelled (4) | |
Type | name or integer | ExcusedAbsence (1), DayOffGranted (2), RemoteDay (3) or CompanyClosure (4) | |
BatchId | uuid | Only exceptions created together in one bulk action | |
Search | string | Part of the employee's name |
curl "https://api.kalhr.com/v1/attendance/exceptions?Status=Approved&From=2026-10-01&To=2026-10-31" \
-H "Authorization: Bearer $KALHR_API_KEY"Response
{
"isSuccessful": true,
"code": "00",
"message": "Success",
"data": {
"data": [
{
"id": "e1d2c3b4-a596-4877-8695-a4b3c2d1e0f9",
"employeeId": "4b9d2c1e-7a3f-4e8b-9c21-5d6e7f8a9b0c",
"employeeName": "Ada Okafor",
"companyId": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b",
"startDate": "2026-10-22",
"endDate": "2026-10-23",
"days": 2,
"type": "RemoteDay",
"isPaid": true,
"status": "Approved",
"requiresApproval": false,
"createdByName": "Tunde Bello",
"created": "2026-10-18T09:14:03",
"approvedByName": "Tunde Bello",
"approvedAt": "2026-10-18T09:14:03Z",
"canCancel": false
}
],
"pageNumber": 1,
"pageSize": 20,
"totalRecords": 1,
"totalPages": 1
}
}
| Field | Type | Description |
|---|---|---|
id | uuid | The exception |
employeeId, employeeName, companyId | uuid, string, uuid | Who it excuses |
startDate, endDate | date | First and last day covered |
days | integer | Calendar days covered; a half day counts as one |
halfDay | string | FirstHalf or SecondHalf for a half day; missing for whole days |
type | string | See Type above |
isPaid | boolean | Whether the days are paid |
status | string | See Status above. Only Approved exceptions change attendance. |
requiresApproval | boolean | Raised by a line manager and sent for approval |
batchId | uuid | Set when created in bulk |
createdByName, created | string, datetime | Who raised it and when (UTC) |
approvedByName, approvedAt, rejectedAt, cancelledAt | string, datetime | Its decision |
canCancel | boolean | Always false for a key |
Get an attendance exception
GET/v1/attendance/exceptions/{id}Key needs Attendance: ViewThe same fields as the list, for one exception. An id outside your organisation answers isSuccessful: false.
curl "https://api.kalhr.com/v1/attendance/exceptions/e1d2c3b4-a596-4877-8695-a4b3c2d1e0f9" \
-H "Authorization: Bearer $KALHR_API_KEY"List company holidays
GET/v1/attendance/holidaysKey needs Attendance: ViewThe holidays and closures your organisation has added itself, for one year, in date order. A recurring holiday is listed on the date it falls that year. Public holidays are a separate list (below).
| Parameter | Type | Default | Description |
|---|---|---|---|
Year | integer | This year | The year to list |
CompanyId | uuid | This company's holidays plus the organisation-wide ones. Leave it out for all. |
curl "https://api.kalhr.com/v1/attendance/holidays?Year=2026" \
-H "Authorization: Bearer $KALHR_API_KEY"Response
{
"isSuccessful": true,
"code": "00",
"message": "Success",
"data": [
{
"id": "0f1e2d3c-4b5a-4968-8778-695a4b3c2d1e",
"name": "Company anniversary",
"date": "2019-06-14",
"occursOn": "2026-06-14",
"isRecurring": true
},
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Lagos office closed for refurbishment",
"date": "2026-11-27",
"occursOn": "2026-11-27",
"isRecurring": false,
"companyId": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b",
"companyName": "Acme Nigeria Ltd"
}
]
}
| Field | Type | Description |
|---|---|---|
id, name | uuid, string | The holiday |
occursOn | date | The day it falls on in the year you asked for |
date | date | The date it was added for; for a recurring holiday, its first year |
isRecurring | boolean | Repeats on the same day every year |
companyId, companyName | uuid, string | The one company it applies to; missing when it's organisation-wide |
This is a plain list, not paged.
List public holidays
GET/v1/attendance/holidays/publicKey needs Attendance: ViewThe public holidays of your organisation's countries (its own, its companies' and their offices'), or one company's, for a year. KalHR imports them from public calendars; they can't be edited.
| Parameter | Type | Default | Description |
|---|---|---|---|
Year | integer | This year | The year to list |
CompanyId | uuid | Only the countries this company and its offices are in |
curl "https://api.kalhr.com/v1/attendance/holidays/public?Year=2026" \
-H "Authorization: Bearer $KALHR_API_KEY"Response
{
"isSuccessful": true,
"code": "00",
"message": "Success",
"data": [
{ "name": "Independence Day", "startDate": "2026-10-01", "endDate": "2026-10-01", "countryId": 160, "countryName": "Nigeria" },
{ "name": "Christmas Day", "startDate": "2026-12-25", "endDate": "2026-12-25", "countryId": 160, "countryName": "Nigeria" }
]
}
| Field | Type | Description |
|---|---|---|
name | string | The holiday |
startDate, endDate | date | First and last day, inclusive |
countryId, countryName | integer, string | The country it belongs to |
A day that is both a public holiday and a company holiday counts once.
Related
- Leave: leave requests and balances
- Event catalogue:
attendance.exception.approved,attendance.absence_report.completed