# UKG Pro — HRIS API: gotchas

> Canonical URL: https://developers.apideck.com/apis/hris/ukg-pro/gotchas

## GET /hris/employees

UKG Pro serves person data and employment data from two different endpoints, so the employment fields on "List Employees" (`employment_status`, `employment_role`, `title`, `department`, `department_id`, `employment_start_date`, `employment_end_date`, `manager`) are enriched from a second call made on your behalf. If that secondary call fails, the request still succeeds with a 200 and those fields are simply absent from the affected records — the person-scope fields are unaffected. Treat their absence as "unavailable for this request", not as "empty at the source". On a list filtered by `employment_status` the roles are reversed: the second call supplies the person fields, so if it fails the records keep their employment fields while the person fields — including `id` — may be absent.
UKG returns every employee ever employed, including terminated ones. `employment_status` resolves from the connector's employment status value; use `filter[employment_status]` to scope the list server-side. The filter accepts `active`, `inactive` and `terminated` — `other` is rejected because this connector has no status it could match. The filter can be combined with `filter[company_id]`, but not with `filter[email]`, `filter[first_name]` or `filter[last_name]` — those combinations are rejected with a 400 rather than returning silently unfiltered results.
A filtered list returns one entry per matching employment record, so an employee with more than one matching record appears more than once — dedupe on `id` if you need unique employees. The unfiltered list keeps its one-entry-per-employee shape and reports each employee's primary employment record, so the same employee can appear under `filter[employment_status]=terminated` (an older terminated record matched) while the unfiltered list reports them as `active`.
`department` and `department_id` both resolve from the connector's organisation-level hierarchy, and `division` is not currently mapped. `jobs`, `compensations`, `custom_fields` and `bank_accounts` are only available on "Get Employee".

## GET /hris/employees/{id}

`employment_role` is sourced from the UKG `employment-details` endpoint and is returned only by
Get Employee (the detail read), not by List Employees.

`employment_role.type` is derived from UKG's `employeeTypeCode`, which is a company-defined code
table (configurable per tenant), not a fixed system enum. The codes we translate are: `CON` →
`contractor`, `INT` → `internship`, `REG` → `employee`, `TMP` → `temp`, `SUM` → `temp`, `STU` →
`other`, `TES` → `other`. Any other or unrecognized code returns `other` because employee types
are defined by each UKG customer and vary between tenants.

`employment_role.sub_type` is derived from `fullTimeOrPartTimeCode` (`F` → `full_time`, `P` →
`part_time`), falling back to `salaryOrHourly` (`H` → `hourly`), otherwise `not_specified`. When
both are present, `fullTimeOrPartTimeCode` takes precedence (sub_type holds a single value).

To map your own `employeeTypeCode` values, add a custom mapping scoped to the `getEmploymentDetails`
request with the JSONPath `$['employeeTypeCode']` (do not use the picker-suggested
`$[*]['employeeTypeCode']` — the `[*]` form does not populate). The value is returned under
`custom_mappings`.


[API reference](https://developers.apideck.com/apis/hris/reference.md) · [Coverage matrix](https://developers.apideck.com/apis/hris/coverage)
