Direction: EPC --> HRMS (Outbound)
Version: 1.7
Date: 21 Oct 2026
Status: Implemented
Changes in 1.7: ACTIVE_STATUS Active is read before LEAVING_REASON (Status Mapping). An employee HRMS marks Active is never read as a retiree, even when the leaving reason mentions retirement, so retiree registration refuses them (HR_CURRENT_EMPLOYEE_USE_SSO, 10171) and they register through SSO. An Active record with a non-retirement leaving reason and a last day that has passed is still read as a former employee. Other statuses are read as in 1.6.
Changes in 1.6: Registration reads ACTIVE_STATUS again, with one rule for both registration flows (Status Mapping). The first SSO sign-in refuses a former employee who is not a retiree (HR_EMPLOYEE_LEFT_SERVICE, 10170). Retiree registration refuses an employee whose ACTIVE_STATUS is Active (HR_CURRENT_EMPLOYEE_USE_SSO, 10171) and sends a former employee who is not a retiree to associate registration.
Changes in 1.5: HR_EMPLOYEE_NOT_FOUND_IN_HRMS is now code 10169. It shared 10080 with SSO_USER_INACTIVE, an unrelated sign-in failure. The HTTP status and the message are unchanged.
Changes in 1.4: The unused POST /members/register was removed (EPC-1295), and it was the trigger this document named. The Trigger section now names the live callers: the first SSO sign-in, retiree registration after the OTP, the back office "Update HRMS data" and the daily leaver reconciliation. The status check and its error HR_EMPLOYEE_NOT_ACTIVE went with it, so Status Mapping now says which flows read ACTIVE_STATUS. Error Handling says which flow returns which error. The retiree lookup is described as the main lookup for retiree registration, and the empty-BG_ID fallback no longer names a form company.
Changes in 1.3: Added GENDER and MARITAL_STATUS to the active-employee requested fields (EPC-1290), so the back office "Update HRMS data" applies them; both endpoints now request the same list. DATE_OF_BIRTH is stored as the day only, like JOINING_DATE. A requested column HRMS has no value for is omitted from the response, and EPC never clears a stored value because of it.
Changes in 1.2: Added BG_ID to the active-employee requested fields (EPC-1260), so the member's company is resolved from the HRMS Business Group on both endpoints. Corrected the field lists to what EPC sends (FIRST_NAME and MIDDLE_NAME, not EMP_NAME) and documented the retiree field list.
Changes in 1.1: Added DOJ to the requested fields (returned as JOINING_DATE) and mapped it to joiningDate on the member record.
---
EPC fetches employee details from HRMS to pre-populate a member at registration, to refresh it on the back office "Update HRMS data", and to recognise leavers in the daily leaver reconciliation. Two endpoints are used:
All requests are authenticated via PING OAuth2.0 Client Credentials flow.
| Parameter | Value |
|---|---|
| Token URL | SSM: /platinum-api/{stage}/hrms-ping-token-url |
| Grant Type | client_credentials |
| Auth Header | Basic {base64} from SSM: /platinum-api/{stage}/hrms-ping-auth-basic |
| Token TTL | 2 hours (refreshed 10 min before expiry) |
| Token Cache | In-memory (per Lambda instance) |
POST {tokenUrl}
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {hrms-ping-auth-basic}
Cache-Control: no-cache
grant_type=client_credentials
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 7200
}
---
GET {baseUrl}/platinum/erp/webservices/rest/EmployeeDetails/getEmployeeDetails/{employeeNumber}?fields={fields}
Authorization: Bearer {access_token}
Accept: application/json
| Parameter | Source | Description |
|---|---|---|
baseUrl |
SSM: /platinum-api/{stage}/hrms-api-base-url |
e.g. https://stgservices.emirates.dev |
employeeNumber |
User input (staffId) | URL-encoded employee number |
fields |
Static | See Fields Requested below |
DOJ,STAFFNR,EMAIL,MOBILE,FIRST_NAME,MIDDLE_NAME,LAST_NAME,GRADE,PERSON_TYPE,STATUS,WORK_LOCAITON,BG_NAME,BG_ID,CRA,TERM_DATE,DOB,PREV_EMP_NUM,CORE_GRADE,DEP_EXITS,LEAV_RES,GENDER,MARITAL_STATUS
{
"getEmployeeDetails_Output": {
"OutputParameters": {
"Output": {
"employeeResponse": [
{
"EMPLOYEE_NUMBER": "EK123456",
"EMAIL_ID": "john.doe@emirates.com",
"MOBILE": "+971501234567",
"FIRST_NAME": "John",
"MIDDLE_NAME": "",
"LAST_NAME": "Doe",
"GRADE_CODE": "EK.07",
"EMPLOYEE_TYPE": "EMP",
"ACTIVE_STATUS": "Active Assignment",
"WORKLOCATION": "Dubai Airport Free Zone",
"BG_COUNTRY": "Emirates Group - UAE",
"BG_ID": "61",
"CURRENT_RES_ADDRESS": "Dubai, UAE",
"ACTUAL_TERMINATION_DATE": "",
"DATE_OF_BIRTH": "1990-01-15",
"JOINING_DATE": "2008-09-23 00:00:00.0",
"PREV_EMP__ID": "",
"IS_DEP_EXISTS": "Y",
"CORE_GRADE": "EK.07",
"LEAVING_REASON": ""
}
]
}
}
}
}
---
Retiree registration calls it with the OTP-verified email. "Update HRMS data" calls it when the active employee endpoint returns no results.
GET {baseUrl}/platinum/erp/webservices/rest/EmpDetailsByEmail/getEmpDetailsbyEmail/?emailID={email}&fields={fields}
Authorization: Bearer {access_token}
Accept: application/json
DOJ,STAFFNR,EMAIL,MOBILE,FIRST_NAME,MIDDLE_NAME,LAST_NAME,GRADE,PERSON_TYPE,STATUS,WORK_LOCAITON,BG_NAME,BG_ID,CRA,TERM_DATE,DOB,PREV_EMP_NUM,CORE_GRADE,DEP_EXITS,LEAV_RES,GENDER,MARITAL_STATUS
{
"getEmpDetailsbyEmail_Output": {
"OutputParameters": {
"Output": {
"employeeResponse": [
{
"EMPLOYEE_NUMBER": "EK123456",
"ACTIVE_STATUS": "Terminate - Process",
"LEAVING_REASON": "Retirement",
"ACTUAL_TERMINATION_DATE": "2025-12-31",
...
}
]
}
}
}
}
---
| HRMS Field | EPC Field | Notes |
|---|---|---|
EMPLOYEE_NUMBER |
employeeId / staffId |
Primary identifier |
EMAIL_ID |
email |
|
MOBILE |
phone |
|
FIRST_NAME |
name |
|
MIDDLE_NAME |
middleName |
|
LAST_NAME |
surname |
|
GRADE_CODE |
grade |
|
EMPLOYEE_TYPE |
employmentType / memberType |
|
ACTIVE_STATUS |
status |
See Status Mapping below |
WORKLOCATION |
addressOptional |
|
BG_COUNTRY |
country / address.country |
|
BG_ID |
bgId → company |
Requested on both endpoints. Resolved to the Platinum company whose bgIds contains it. An unmapped value rejects the registration or the update; an empty value falls back to the SSO default company at registration and leaves the company unchanged on "Update HRMS data" |
CURRENT_RES_ADDRESS |
address |
|
ACTUAL_TERMINATION_DATE |
lastDayOfService |
Null if empty |
DATE_OF_BIRTH |
birthdate |
Oracle timestamp; stored as the day only (1990-01-15). Empty or unparseable leaves the stored value untouched |
GENDER |
gender |
Requested on both endpoints. M/Male and F/Female (any case); any other value is ignored |
MARITAL_STATUS |
maritalStatus |
Requested on both endpoints. M/Married is stored as married; S/Single, Divorced, Widowed and Separated as single; any other value keeps the stored one and is recorded in the member's history |
JOINING_DATE |
joiningDate |
Requested as DOJ. HRMS returns an Oracle timestamp (2008-09-23 00:00:00.0); normalized to the day only (2008-09-23) before storage. Empty or unparseable leaves the stored value untouched |
PREV_EMP__ID |
oldStaffId / previousStaffIds |
Null if empty |
IS_DEP_EXISTS |
dependantsExists |
"Y" or "N" |
CORE_GRADE |
coreGrade |
|
LEAVING_REASON |
leavingReason |
Null if empty |
Registration reads ACTIVE_STATUS together with LEAVING_REASON, ACTUAL_TERMINATION_DATE and JOINING_DATE (decision re1295). The same rule serves the first SSO sign-in and retiree registration. The rows are read in order: an Active status is read first.
| HRMS record | EPC reading | First SSO sign-in | Retiree registration |
|---|---|---|---|
ACTIVE_STATUS Active, a leaving reason that does not mention retirement, a last day that has passed, and JOINING_DATE not after the last day |
Left | Refused with HR_EMPLOYEE_LEFT_SERVICE (10170) |
Continues as an associate |
ACTIVE_STATUS Active, any other case, a retirement reason included |
Employed | Registers | Refused with HR_CURRENT_EMPLOYEE_USE_SSO (10171) |
Not Active, and LEAVING_REASON mentions retirement |
Retiree | Registers | Registers |
ACTIVE_STATUS In-Active, Inactive or Terminated, not a retiree |
Left | Refused with HR_EMPLOYEE_LEFT_SERVICE (10170) |
Continues as an associate |
Empty or unrecognised ACTIVE_STATUS, a leaving reason and a last day that has passed, not a retiree, and JOINING_DATE not after the last day |
Left | Refused with HR_EMPLOYEE_LEFT_SERVICE (10170) |
Continues as an associate |
Empty or unrecognised ACTIVE_STATUS, and the rows above do not apply |
Current | Registers | Registers, as before (no positive evidence of a current employee) |
A status value EPC does not recognise is judged on the leaving fields alone. The samples above show Active Assignment and Terminate - Process; the answers EPC received on its test stages in October 2026 carry Active and In-Active. Emirates to confirm the full list of ACTIVE_STATUS values, and whether the retiree endpoint also returns current employees.
ACTIVE_STATUS is also read by the back office "Update HRMS data" and by leaver reconciliation, to recognise a member who has left since registering. LEAVING_REASON and ACTUAL_TERMINATION_DATE are stored at registration, where a retirement leaving reason marks the member as a retiree.
---
| Scenario | HTTP Status | Behavior |
|---|---|---|
| Employee not found at the first SSO sign-in | N/A | Return HR_EMPLOYEE_NOT_FOUND_IN_HRMS (10169) |
| Retiree not found by email | N/A | Not an error: the registrant continues as an associate |
| Former employee, not a retiree, at the first SSO sign-in | N/A | Return HR_EMPLOYEE_LEFT_SERVICE (10170) |
| Former employee, not a retiree, found by email | N/A | Not an error: the registrant continues as an associate |
| Active employee found by email at retiree registration | 403 | Return HR_CURRENT_EMPLOYEE_USE_SSO (10171); staff of a company with no self-registration get COMPANY_EMAIL_PASSWORD_ONLY (10142), and a BG_ID that matches no company gets HRMS_COMPANY_NOT_FOUND (11016, 404) |
| HRMS unavailable (503, timeout, HTML) at retiree registration | 503 | Return HR_HRMS_UNAVAILABLE (10079) |
| HRMS unavailable at the SSO sign-in or "Update HRMS data" | N/A | The request fails with a generic error |
| Employee not found by number or email on "Update HRMS data" | N/A | Return HR_HRMS_UNAVAILABLE (10079) |
| ISGServiceFault | Varies | Retry up to 3 times, then fail |
| 401 Unauthorized | 401 | Clear token cache, retry once |
{
"ISGServiceFault": {
"Code": "500",
"Message": "Internal Server Error"
}
}
HRMS may return HTML (Weblogic bridge failure) instead of JSON on 503. Detected via Content-Type: text/html.
---
| Parameter | Value |
|---|---|
| Max Retries | 3 |
| Retry Delay | 5 seconds |
| Request Timeout | 10 seconds |
| 401 Handling | Clear token cache, retry once |
| 503 Handling | Retry with backoff |
---
Called synchronously by four flows:
| Flow | Handler | Lookup |
|---|---|---|
| First SSO sign-in, when no member holds the email or the staff ID | login-sso-callback |
getEmployeeByNumber(staffId), staff ID from the SAML response |
| Retiree registration after the OTP | verify-registration-otp |
getEmployeeByEmail(email), the OTP-verified email |
| Back office "Update HRMS data" | resync-hrms-member |
getEmployeeByNumber(staffId), then getEmployeeByEmail(email) if not found |
| Leaver reconciliation (daily cron) | reconcile-leavers |
getEmployeeByNumber(staffId) |
HR_EMPLOYEE_NOT_FOUND_IN_HRMS, and the retiree registration continues as an associatehrmsValidated: true on the member record---
| File | Purpose |
|---|---|
src/lib/services/hrms-auth.service.ts |
PING OAuth2.0 token management |
src/lib/services/hrms-employee.service.ts |
Employee fetch with retry logic |
src/lib/mappers/hrms-employee.mapper.ts |
Raw response to domain model mapping |
src/lib/entities/hr-integration.ts |
Type definitions (HrmsEmployeeRaw, HrmsEmployee) |
src/modules/member/login-sso-callback/handler.ts |
Trigger: first SSO sign-in |
src/modules/member/verify-registration-otp/handler.ts |
Trigger: retiree registration |
src/modules/admin/resync-hrms-member/handler.ts |
Trigger: back office "Update HRMS data" |
src/lib/utils/employment-state.ts |
registrationEmployment: the registration Status Mapping |
src/lib/services/leaver-reconciliation.service.ts |
Trigger: leaver reconciliation (cron reconcile-leavers) |
---
| Parameter | Type | Description |
|---|---|---|
/platinum-api/{stage}/hrms-ping-token-url |
String | PING OAuth2.0 token endpoint |
/platinum-api/{stage}/hrms-api-base-url |
String | HRMS API base URL |
/platinum-api/{stage}/hrms-ping-auth-basic |
SecureString | Base64 client credentials for HRMS APIs |