Interface Control Document (ICD): HRMS - EPC
---
| Version | Status | Update Comment | By | Date |
|---|---|---|---|---|
| 1.0 | Draft | Initial Draft | Retter | 05 Feb 2026 |
| 1.1 | Draft | Standardized payload: HRMS sends full record, overwrites member data | Retter | 10 Feb 2026 |
| 1.2 | Draft | Added companyId (HRMS Business Group ID / BG_ID, required & non-null) and middleName fields; documented company-reassignment use case and the 404/409 BG_ID error responses |
Retter | 23 Jun 2026 |
---
| Abbreviation | Description |
|---|---|
| HRMS | Human Resource Management System |
| EPC | Emirates Platinum Card |
| API | Application Programming Interface |
| SSM | AWS Systems Manager (Parameter Store) |
---
When employee data changes in the HRMS (name, email, phone, grade, status, etc.), the HRMS system will push the updates to the Platinum Application. This ensures that member profiles remain synchronized with the authoritative HR data source.
> Standardized Payload: HRMS should always send the full employee record in every request. The EPC system overwrites the member record with the incoming HRMS data.
---
| Risk | Mitigation |
|---|---|
| Network timeouts between HRMS and EPC | Configure appropriate timeout settings (recommended: 30s) |
| Employee not found in EPC | HRMS should handle 404 responses gracefully and log for review |
---
| Sr No. | Non-Functional Requirement | Value |
|---|---|---|
| 1 | PHI or Credit Card data? | No credit card data & no patient health information |
| 2 | Authentication Pattern | API Key (X-API-Key header) |
| 3 | Secure Channel / Encryption in Transit | TLS 1.2+ (HTTPS only) |
| 4 | Input validation | Yes - all fields validated via JSON Schema |
| 5 | Internet exposed? IP whitelisting? | Yes, exposed via CloudFront. IP whitelisting can be implemented via WAF rules |
| 6 | Logging | All requests logged with sanitized payloads (sensitive fields redacted) |
| 7 | Data exposure | Only necessary fields returned. Sensitive member data not included in response |
---
| Component | Value |
|---|---|
| Source | HRMS (Human Resource Management System) |
| Source Protocol | HTTPS REST API |
| Source Data Format | JSON |
| Target | EPC (Emirates Platinum Card) |
| Target Protocol | HTTPS |
| Target Data Format | JSON |
┌─────────────┐ ┌─────────────┐
│ │ HTTPS / JSON │ │
│ HRMS │ ─────────────────────────▶ │ EPC API │
│ (Source) │ POST │ (Target) │
│ │ ◀───────────────────────── │ │
└─────────────┘ HTTPS / JSON └─────────────┘
Flow:
X-API-Key headerAPI Monitoring
Exception Handling
| HTTP Status | Scenario | HRMS Action |
|---|---|---|
| 200 | Success | Log success, continue |
| 400 | Validation error | Fix request data, retry |
| 401 | Invalid API key | Check API key, contact EPC team |
| 404 | Employee not found, or companyId (BG_ID) not mapped to any Platinum company |
Log for manual review |
| 409 | companyId (BG_ID) resolves to a company of a different group type than the member's current company |
Log for manual review; do not retry without correction |
| 500 | Server error | Retry with backoff |
| 503 | Service unavailable | Retry later |
Actors: HRMS System (automated)
Trigger: Employee data change in HRMS (name, email, phone, grade, address)
Flow:
Actors: HRMS System (automated)
Trigger: Employee's staff ID changes in HRMS
Flow:
employeeId and oldStaffIdemployeeIdoldStaffIdpreviousStaffIds arraystaffIdMigrated: trueActors: HRMS System (automated)
Trigger: Employee status changes to terminated/inactive in HRMS
Flow:
status: "terminated" and optional leavingReasonInactiveActors: HRMS System (automated)
Trigger: Previously terminated employee is rehired
Flow:
status: "active"Actors: HRMS System (automated)
Trigger: Employee moves to a different business group in HRMS
Flow:
companyId set to the employee's current HRMS Business Group ID (BG_ID) — this key is always sent (required, non-null)bgId matches companyId404 (HRMS_COMPANY_NOT_FOUND, code 11016) and rejects the whole requestcompany is not added to updatedFieldsinternal ↔ external) than the member's current company, EPC returns 409 (HRMS_COMPANY_GROUP_TYPE_MISMATCH, code 11017) and rejects the requestcompany in updatedFields---
> Null convention: For optional fields that have no value, send null — not an empty string (""). Empty strings are rejected by validation.
>
> Full-record convention: HRMS should always send the full employee record in every request. The EPC system overwrites the member record with the incoming HRMS data.
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| employeeId | String | Yes | Employee's current staff ID (primary lookup key) | "EK123456" |
| name | String | Yes | Employee's first name | "John" |
| middleName | String | Optional | Employee's middle name | "William" |
| surname | String | Yes | Employee's last name | "Doe" |
| String | Yes | Employee's email address (must be valid email format) | "john.doe@emirates.com" | |
| phone | String | Yes | Employee's phone number | "+971501234567" |
| grade | String | Yes | Employee's job grade | "EK.07" |
| employmentType | String | Yes | Type of employment. Must match an active member type configured in the backoffice. Returns 400 if invalid. | "EMP" |
| status | String | Yes | HR status: "active", "inactive", "terminated" | "active" |
| address | Object | Yes | Address information | See below |
| address.country | String | Yes | Country (Staff Cost Centre belongs to) | "Emirates Group - UAE" |
| address.address | String | Yes | Work address | "Dubai Airport Free Zone" |
| birthdate | String (date) | Yes | Birth date (verification only, not stored) | "1990-01-15" |
| gender | String | Yes | Employee's gender | "Male" |
| maritalStatus | String | Yes | Employee's marital status | "Married" |
| coreGrade | String | Yes | Core grade code | "EK.07" |
| dependantsExists | String | Yes | Whether employee has dependants ("Y" or "N") | "Y" |
| oldStaffId | String | Nullable | Previous staff ID (for migration scenarios) | "EK000001" |
| lastDayOfService | String (date) | Nullable | Termination date (ISO 8601) | "2026-03-31" |
| leavingReason | String | Nullable | Reason for leaving (terminations) | "Retirement" |
| companyId | String | Yes | HRMS Business Group ID (BG_ID). Always send the employee's current BG_ID. The member is reassigned to the Platinum company whose bgId matches this value; sending the unchanged BG_ID is a no-op. Returns 404 (code 11016) if unmapped, 409 (code 11017) on group-type mismatch. |
"61" |
| Field | Type | Description |
|---|---|---|
| success | Boolean | True if operation succeeded |
| message | String | Human-readable message |
| body.memberId | String (UUID) | Member's ID in EPC system |
| body.staffId | String | Current staff ID |
| body.updatedFields | Array[String] | List of fields that were updated |
| body.staffIdMigrated | Boolean | True if staff ID was migrated |
---
Authentication: API Key via X-API-Key header
X-API-Key: <your-api-key>
Content-Type: application/json
HRMS should always send the complete employee record with every request. The EPC system overwrites the member record with the incoming HRMS data. The updatedFields array in the response tells HRMS which fields were modified.
> Null convention: For fields that have no value, send null — not an empty string (""). Empty strings are rejected by validation.
Request:
POST /members/hr/employee HTTP/1.1
Host: uat.api.platinum.retter.io
X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"employeeId": "EK123456",
"oldStaffId": null,
"email": "john.doe@emirates.com",
"phone": "+971501234567",
"name": "John",
"middleName": "William",
"surname": "Doe",
"grade": "EK.07",
"employmentType": "EMP",
"status": "active",
"address": {
"country": "Emirates Group - UAE",
"address": "Dubai Airport Free Zone"
},
"lastDayOfService": null,
"birthdate": "1990-01-15",
"gender": "Male",
"maritalStatus": "Married",
"dependantsExists": "Y",
"coreGrade": "EK.07",
"leavingReason": null,
"companyId": "61"
}
Response (200 OK) — only grade and phone differed from current record:
{
"success": true,
"message": "Employee updated successfully.",
"body": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": "EK123456",
"updatedFields": ["phone", "grade"]
}
}
Response (200 OK) — nothing changed (all values match current record):
{
"success": true,
"message": "Employee updated successfully.",
"body": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": "EK123456",
"updatedFields": []
}
}
| Scenario | Key Fields | Response Indicator |
|---|---|---|
| Profile Update | Any non-null field | updatedFields lists written fields |
| Staff ID Migration | oldStaffId provided, member found by oldStaffId |
staffIdMigrated: true in response |
| Termination | status: "terminated" or "inactive" |
updatedFields includes "status" |
| Reactivation | status: "active" (member currently Inactive) |
updatedFields includes "status" |
Staff ID Migration example — employee's ID changed from EK123456 to EK999999:
{
"employeeId": "EK999999",
"oldStaffId": "EK123456",
"email": "john.doe@emirates.com",
"phone": "+971501234567",
"name": "John",
"surname": "Doe",
"grade": "EK.07",
"employmentType": "EMP",
"status": "active",
"...": "... (all other fields)"
}
Response:
{
"success": true,
"message": "Employee updated successfully.",
"body": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": "EK999999",
"updatedFields": ["staffId", "previousStaffIds"],
"staffIdMigrated": true
}
}
Termination example — status changed to "terminated":
{
"employeeId": "EK123456",
"status": "terminated",
"leavingReason": "Retirement",
"lastDayOfService": "2026-03-31",
"...": "... (all other fields)"
}
Response:
{
"success": true,
"message": "Employee updated successfully.",
"body": {
"memberId": "550e8400-e29b-41d4-a716-446655440000",
"staffId": "EK123456",
"updatedFields": ["status", "statusReason"]
}
}
---
{
"success": false,
"error": {
"code": 10001,
"message": "Validation error"
},
"details": {
"errors": {
"employeeId": ["Required"]
}
}
}
{
"success": false,
"error": {
"code": 10072,
"message": "Invalid API key"
}
}
{
"success": false,
"error": {
"code": 10073,
"message": "Employee not found"
}
}
Returned when companyId is non-null but no Platinum company is configured with that HRMS Business Group ID.
{
"success": false,
"error": {
"code": 11016,
"message": "No Platinum company is mapped to the supplied HRMS business group id (BG_ID)."
}
}
Returned when the company resolved from companyId belongs to a different group type (internal ↔ external) than the member's current company.
{
"success": false,
"error": {
"code": 11017,
"message": "Cannot move member: target company belongs to a different company group type."
}
}
{
"success": false,
"error": {
"code": 10000,
"message": "Internal server error"
}
}
---
| Environment | URL |
|---|---|
| Staging | https://staging.api.platinum.retter.io/members/hr/employee |
| UAT | https://uat.api.platinum.retter.io/members/hr/employee |
---
| Status | Description |
|---|---|
| Active | Member is active and can use benefits |
| Inactive | Member is deactivated (terminated/suspended) |
| Pending | Member registration pending (not applicable for HR updates) |
All HR integration calls are logged with:
Successful updates create audit log entries with: