MASKDocs

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 work stretches of shifts that are no longer in progress: complete ones, and incomplete ones, 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 (absenceDays of 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#

GET/integration/reports/institutionScope: reports:read

One 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

  • institutionIdqueryintegerrequired

    One of the key's institutions (403 institution_not_allowed otherwise)

  • fromquerydaterequired

    First business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31

  • toquerydaterequired

    Last business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31; from..to spans at most 366 days, both included

Responses

200OKInstitutionReport
Fields of InstitutionReport
  • fromdate
  • institutionIdinteger
  • rowsarray of InstitutionReportRow
    Fields of InstitutionReportRow
    • absenceDaysnumber

      Approved absence days in the period; a half day counts 0.5

    • daysWorkedinteger

      Business dates with closed work in the period

    • employeeIdinteger
    • firstNamestring
    • lastNamestring
    • workSecondsinteger

      Closed shifts' work in the period

  • todate
  • totalWorkSecondsinteger
  • workingDaysinteger

    Business dates on which anyone worked there

Errors this route can answer
  • 400Bad Requestvalidation_failedidempotency_requiredevent_too_old
  • 401Unauthorizedinvalid_api_keykey_expiredkey_revoked
  • 403Forbiddenip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee
  • 404Not Foundnot_found
  • 429Too Many Requestsrate_limited
  • 500Internal Server Errorinternal_error
  • 503Service Unavailableunavailable

Each error has the same JSON body; see Errors for what every code means.

Get payroll report#

GET/integration/reports/payrollScope: reports:read

One 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

  • institutionIdqueryintegerrequired

    One of the key's institutions (403 institution_not_allowed otherwise)

  • yearqueryintegerrequired

    2000-2037: every day of the month must lie from 2000-01-01 to 2037-12-31

  • monthqueryintegerrequired

    1-12

Responses

200OKPayrollReport
Fields of PayrollReport
  • institutionIdinteger
  • monthinteger
  • rowsarray of PayrollReportRow
    Fields of PayrollReportRow
    • emailstring

      Absent when the person has none

    • employeeIdinteger
    • firstNamestring
    • lastNamestring
    • workSecondsinteger

      The month's closed work, no lunch deducted

    • workingDaysinteger

      Business dates with work in the month

  • totalWorkSecondsinteger
  • yearinteger
Errors this route can answer
  • 400Bad Requestvalidation_failedidempotency_requiredevent_too_old
  • 401Unauthorizedinvalid_api_keykey_expiredkey_revoked
  • 403Forbiddenip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee
  • 404Not Foundnot_found
  • 429Too Many Requestsrate_limited
  • 500Internal Server Errorinternal_error
  • 503Service Unavailableunavailable

Each error has the same JSON body; see Errors for what every code means.

Get employee report#

GET/integration/reports/employeeScope: reports:read

One 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

  • employeeIdqueryintegerrequired

    A person the key can see (404 otherwise)

  • fromquerydaterequired

    First business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31

  • toquerydaterequired

    Last business date, ISO yyyy-MM-dd, from 2000-01-01 to 2037-12-31; from..to spans at most 366 days, both included

  • institutionIdqueryinteger

    One of the key's institutions; omitted = all of them

Responses

200OKEmployeeReport
Fields of EmployeeReport
  • employeeIdinteger
  • fromdate
  • rowsarray of EmployeeReportRow
    Fields of EmployeeReportRow
    • businessDatedate
    • endedAtinteger

      Absent while the shift is open or when the clock-out was forgotten

    • institutionIdinteger
    • sessionIdinteger
    • startedAtinteger

      Epoch seconds, UTC

    • statusstring

      in_progress, complete or incomplete

    • workSecondsinteger

      The shift's closed work; absent while it is in progress

  • todate
  • totalWorkSecondsinteger

    The closed shifts' work; a shift still in progress adds nothing yet

Errors this route can answer
  • 400Bad Requestvalidation_failedidempotency_requiredevent_too_old
  • 401Unauthorizedinvalid_api_keykey_expiredkey_revoked
  • 403Forbiddenip_not_allowedscope_missinginstitution_not_allowednot_api_eventadmin_linked_employee
  • 404Not Foundnot_found
  • 429Too Many Requestsrate_limited
  • 500Internal Server Errorinternal_error
  • 503Service Unavailableunavailable

Each error has the same JSON body; see Errors for what every code means.