Integration API API reference
Reports
Totals per institution over a period, per payroll month and per person. Needs reports:read.
Reports add up the shifts for payroll and HR. They read the current data, so a correction an administrator makes today changes last month's numbers when it touches last month.
What the reports count#
- Work is the sum of the
workstretches of shifts that are no longer in progress:completeones, andincompleteones, where the stretch left open counts nothing. Break, personal and business stretches are not work in the reports; the sessions report each kind separately. - Days are business dates. A day counts as worked when it has real working time; an accidental double tap does not make a day.
- Absences are the ones approved in MASK (
absenceDaysof the institution report). The API has no absence endpoints. - Payroll (
/reports/payroll) is exactly what the Abacus payroll export in the MASK web app contains for that institution and month. The official monthly form deducts 30 minutes of lunch a day; the API reports never do. - Re-pull a month you have already imported when you need it final (see Monthly payroll pull).
Get institution report#
/integration/reports/institutionScope: reports:readOne institution over a period: days worked, absence days and work per person. 403 institution_not_allowed for an institution outside the key.
The period is at most 366 days. workingDays counts the business dates on which anyone worked there; each person has daysWorked, absenceDays (a half day counts 0.5) and workSeconds, and totalWorkSeconds adds them up. The rows are the institution's active members plus anyone who worked there in the period, by first then last name.
curl -s "$MASK/integration/reports/institution?institutionId=4&from=2026-11-01&to=2026-11-30" -H "Authorization: Bearer $KEY"
{
"institutionId": 4,
"from": "2026-11-01",
"to": "2026-11-30",
"workingDays": 1,
"rows": [
{"employeeId": 24, "firstName": "Ana", "lastName": "Primjer-Kovač", "daysWorked": 1, "absenceDays": 0.0, "workSeconds": 7200}
],
"totalWorkSeconds": 7200
}
Parameters
institutionIdqueryintegerrequiredOne of the key's institutions (403 institution_not_allowed otherwise)
fromquerydaterequiredFirst business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31
toquerydaterequiredLast business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31; from..to spans at most 366 days, both included
Responses
Fields of InstitutionReport
fromdateinstitutionIdintegerrowsarray of InstitutionReportRowFields of InstitutionReportRow
absenceDaysnumberApproved absence days in the period; a half day counts 0.5
daysWorkedintegerBusiness dates with closed work in the period
employeeIdintegerfirstNamestringlastNamestringworkSecondsintegerClosed shifts' work in the period
todatetotalWorkSecondsintegerworkingDaysintegerBusiness dates on which anyone worked there
Errors this route can answer
- 400Bad Request
validation_failedidempotency_requiredevent_too_old - 401Unauthorized
invalid_api_keykey_expiredkey_revoked - 403Forbidden
ip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee - 404Not Found
not_found - 429Too Many Requests
rate_limited - 500Internal Server Error
internal_error - 503Service Unavailable
unavailable
Each error has the same JSON body; see Errors for what every code means.
Get payroll report#
/integration/reports/payrollScope: reports:readOne calendar month at one institution: working days and work per person (the Abacus payroll numbers, no lunch deducted). 403 institution_not_allowed for an institution outside the key.
Each row also has the person's email when they have one.
curl -s "$MASK/integration/reports/payroll?institutionId=4&year=2026&month=11" -H "Authorization: Bearer $KEY"
{
"institutionId": 4,
"year": 2026,
"month": 11,
"rows": [
{"employeeId": 24, "firstName": "Ana", "lastName": "Primjer-Kovač", "email": "ana.kovac@example.com", "workingDays": 1, "workSeconds": 7200}
],
"totalWorkSeconds": 7200
}
Parameters
institutionIdqueryintegerrequiredOne of the key's institutions (403 institution_not_allowed otherwise)
yearqueryintegerrequired2000-2037: every day of the month must lie from 2000-01-01 to 2037-12-31
monthqueryintegerrequired1-12
Responses
Fields of PayrollReport
institutionIdintegermonthintegerrowsarray of PayrollReportRowFields of PayrollReportRow
emailstringAbsent when the person has none
employeeIdintegerfirstNamestringlastNamestringworkSecondsintegerThe month's closed work, no lunch deducted
workingDaysintegerBusiness dates with work in the month
totalWorkSecondsintegeryearinteger
Errors this route can answer
- 400Bad Request
validation_failedidempotency_requiredevent_too_old - 401Unauthorized
invalid_api_keykey_expiredkey_revoked - 403Forbidden
ip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee - 404Not Found
not_found - 429Too Many Requests
rate_limited - 500Internal Server Error
internal_error - 503Service Unavailable
unavailable
Each error has the same JSON body; see Errors for what every code means.
Get employee report#
/integration/reports/employeeScope: reports:readOne person's shifts in the key's institutions (or one of them) over a period, with the work of each. 404 for a person outside the key, 403 institution_not_allowed for an institutionId outside it.
The period is at most 366 days, and the rows are ordered by business date and start. A shift still in_progress has no workSeconds and is not in totalWorkSeconds.
curl -s "$MASK/integration/reports/employee?employeeId=24&from=2026-11-03&to=2026-11-04&institutionId=1" -H "Authorization: Bearer $KEY"
{
"employeeId": 24,
"from": "2026-11-03",
"to": "2026-11-04",
"rows": [
{"sessionId": 4438, "businessDate": "2026-11-03", "institutionId": 1, "startedAt": 1793689200, "endedAt": 1793719800,
"status": "complete", "workSeconds": 30600},
{"sessionId": 4440, "businessDate": "2026-11-04", "institutionId": 1, "startedAt": 1793811246, "status": "in_progress"}
],
"totalWorkSeconds": 30600
}
Parameters
employeeIdqueryintegerrequiredA person the key can see (404 otherwise)
fromquerydaterequiredFirst business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31
toquerydaterequiredLast business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31; from..to spans at most 366 days, both included
institutionIdqueryintegerOne of the key's institutions; omitted = all of them
Responses
Fields of EmployeeReport
employeeIdintegerfromdaterowsarray of EmployeeReportRowFields of EmployeeReportRow
businessDatedateendedAtintegerAbsent while the shift is open or when the clock-out was forgotten
institutionIdintegersessionIdintegerstartedAtintegerEpoch seconds, UTC
statusstringin_progress, complete or incomplete
workSecondsintegerThe shift's closed work; absent while it is in progress
todatetotalWorkSecondsintegerThe closed shifts' work; a shift still in progress adds nothing yet
Errors this route can answer
- 400Bad Request
validation_failedidempotency_requiredevent_too_old - 401Unauthorized
invalid_api_keykey_expiredkey_revoked - 403Forbidden
ip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee - 404Not Found
not_found - 429Too Many Requests
rate_limited - 500Internal Server Error
internal_error - 503Service Unavailable
unavailable
Each error has the same JSON body; see Errors for what every code means.