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 dependant data | Retter | 10 Feb 2026 |
| 1.2 | Draft | Added status field for dependant activation/deactivation | Retter | 10 Feb 2026 |
| 1.3 | Draft | Changed to upsert mode — creates dependant if not found | Retter | 11 Feb 2026 |
| 1.4 | Draft | Made email field nullable; clarified type field uses dynamic back-office mapping (hrmsMapping) | Retter | 30 Mar 2026 |
| 1.5 | Draft | Status field changed to "Active"/"In-Active" (case-insensitive); unrecognized type no longer rejects — saves without mapping and returns warning | Retter | 30 Mar 2026 |
| 1.6 | Draft | Added optional, nullable phone field (dependant mobile number) |
Retter | 14 Jun 2026 |
| 1.7 | Draft | EPC-1289: added optional middleName, gender and address (address, country); missing values are read from the HRMS dependant lookup; pushes for primaries not registered through HRMS are accepted and not applied (skipped) |
Retter | 16 Oct 2026 |
| 1.8 | Draft | EPC-1290: birthdate is stored (it always was; "not stored" was wrong) as YYYY-MM-DD, from any of the date forms HRMS sends. "Active" reactivates only a dependant HRMS deactivated. The email is not written when another member already uses it |
Retter | 20 Oct 2026 |
| 1.9 | Draft | EPC-1259: pushes whose primary belongs to a company managed by hand in EPC are accepted and not applied (skipped: company_hrms_sync_disabled) |
Retter | 19 Oct 2026 |
---
| Abbreviation | Description |
|---|---|
| HRMS | Human Resource Management System |
| EPC | Emirates Platinum Card |
| API | Application Programming Interface |
| SSM | AWS Systems Manager (Parameter Store) |
---
When dependant data changes in the HRMS (name, email, relationship type, etc.), the HRMS system will push the updates to the Platinum Application. This endpoint operates in upsert mode: if the dependant exists it is updated; if not, a new dependant is created under the primary member.
> Standardized Payload: HRMS should always send the full dependant record in every request. The EPC system overwrites the dependant record with the incoming HRMS data, or creates a new dependant if the ID is not found.
---
| Risk | Mitigation |
|---|---|
| Network timeouts between HRMS and EPC | Configure appropriate timeout settings (recommended: 30s) |
| Duplicate dependant ID | EPC rejects creation if the ID already exists for a different member (403) |
| Dependant ownership mismatch | HRMS should verify dependant-employee relationship before sending updates |
---
| 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 | Dependant updated | Log success, continue |
| 201 | Dependant created (did not exist) | Log success, store returned dependantId |
| 400 | Validation error | Fix request data, retry |
| 401 | Invalid API key | Check API key, contact EPC team |
| 403 | Dependant ownership mismatch | Verify dependant belongs to employee |
| 404 | Primary member not found | Log for manual review |
| 500 | Server error | Retry with backoff |
| 503 | Service unavailable | Retry later |
Actors: HRMS System (automated)
Trigger: Dependant data change or new dependant in HRMS
Flow:
Actors: HRMS System (automated)
Trigger: Dependant's name changes in HRMS (e.g., after marriage)
Flow:
name and/or surnameActors: HRMS System (automated)
Trigger: Dependant's relationship type changes in HRMS
Flow:
type fieldActors: HRMS System (automated)
Trigger: Any dependant push
Flow:
middleName, gender and address (address, country); all are optionalwarnings; it never rejects the requestActors: HRMS System (automated)
Trigger: employeeId belongs to a primary who registered another way (self-registered, associate company or created in the back office)
Flow:
200 with updatedFields: [] and skipped: "not_hrms_registered"Actors: HRMS System (automated)
Trigger: employeeId belongs to a primary whose company has the EPC setting "Disable integrations and self-registrations" on
Flow:
200 with updatedFields: [] and skipped: "company_hrms_sync_disabled"---
> All fields except phone must be present in the payload. HRMS must always send the full dependant record in every request. The EPC system overwrites the dependant record with the incoming HRMS data. email and phone accept null when not available. Empty strings are rejected by validation (except email/phone, where empty/whitespace is normalized to null).
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| id | String | Yes | Dependant's ID in the EPC system | "660e8400-e29b-41d4-a716-446655440001" |
| employeeId | String | Yes | Primary member's staff ID (to verify ownership) | "EK123456" |
| status | String (enum) | Yes | "Active" or "In-Active" (case-insensitive). "In-Active" sets member status to Inactive; "Active" reactivates if Inactive and the deactivation came from HRMS. A dependant the Platinum programme team deactivated or removed stays Inactive. | "Active" |
| name | String | Yes | Dependant's first name | "Jane" |
| surname | String | Yes | Dependant's last name | "Doe" |
| String or null | Yes | Dependant's email address. Must be valid email format if provided; send null if not available. Not written when another member already uses this address; the rest of the request is still applied and the skip is recorded in the dependant's history. |
"jane.doe@example.com" or null | |
| phone | String or null | No | Dependant's mobile number. Optional — may be omitted. Send null (or omit) if not available; when null/omitted the stored phone is left unchanged. |
"+971501234567" or null |
| type | String | Yes | Relationship type. Member subtypes are defined dynamically in the back office by admins. A mapping field (hrmsMapping) is used to match HRMS values to EPC values. If the value is not recognized, the dependant is still saved but without type mapping, and a warning is returned in the warnings array. |
"Wife" |
| birthdate | String (date) | Yes | Birth date. Accepts YYYY-MM-DD, the HRMS form 1995-05-15 00:00:00.0 or a date-time with an offset such as 1995-05-15T00:00:00.000+04:00. Stored as the date part, YYYY-MM-DD, never converted to another time zone |
"1995-05-15" or "1995-05-15 00:00:00.0" |
| middleName | String or null | No | Dependant's middle name (DEP_MIDDLE_NAMES). Omitted, null or empty leaves the stored value unchanged | "Marie" |
| gender | String or null | No | Dependant's sex (DEP_SEX): M, F, Male or Female, case-insensitive. Omitted, null or empty leaves the stored value unchanged; any other value is ignored with a warning | "Female" |
| address | Object or null | No | Dependant's home address. Each part optional | See below |
| address.address | String or null | No | One line: address lines, town or city, postal code | "Villa 12, Al Wasl Road, Dubai" |
| address.country | String or null | No | DEP_COUNTRY as an ISO alpha-2 code or country name; an unrecognised value is ignored with a warning | "AE" |
| Field | Type | Description |
|---|---|---|
| success | Boolean | True if operation succeeded |
| message | String | Human-readable message |
| body.dependantId | String (UUID) | Dependant's ID in EPC system |
| body.primaryMemberId | String (UUID) | Primary member's ID in EPC system |
| body.updatedFields | Array[String] | List of fields that were updated |
| body.warnings | Array[String] (optional) | Warnings about the request (e.g., unrecognized type, gender or country). Only present when there are warnings. |
| body.skipped | String (optional) | Present when the push was accepted but not applied; updatedFields is then empty. not_hrms_registered: primary not registered through HRMS. company_hrms_sync_disabled: the primary's company is managed by hand in EPC |
---
Authentication: API Key via X-API-Key header
X-API-Key: <your-api-key>
Content-Type: application/json
HRMS should always send the complete dependant record with every request. The EPC system overwrites the dependant record with the incoming HRMS data, or creates a new dependant if the ID is not found. The updatedFields array in the response tells HRMS which fields were modified. An empty updatedFields with a 201 status indicates a newly created dependant.
> Null convention: For fields that support null (e.g., email), send null — not an empty string (""). Empty strings are rejected by validation.
Request:
POST /members/hr/dependant HTTP/1.1
Host: uat.api.platinum.retter.io
X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"employeeId": "EK123456",
"status": "Active",
"type": "Wife",
"name": "Jane",
"surname": "Doe",
"email": "jane.doe@example.com",
"phone": "+971501234567",
"birthdate": "1995-05-15",
"middleName": "Marie",
"gender": "Female",
"address": {
"address": "Villa 12, Al Wasl Road, Dubai",
"country": "AE"
}
}
Response (200 OK) — dependant existed, only name and email differed:
{
"success": true,
"message": "Dependant updated successfully",
"body": {
"dependantId": "660e8400-e29b-41d4-a716-446655440001",
"primaryMemberId": "550e8400-e29b-41d4-a716-446655440000",
"updatedFields": ["name", "email"]
}
}
Response (200 OK) — dependant existed, nothing changed:
{
"success": true,
"message": "Dependant updated successfully",
"body": {
"dependantId": "660e8400-e29b-41d4-a716-446655440001",
"primaryMemberId": "550e8400-e29b-41d4-a716-446655440000",
"updatedFields": []
}
}
Response (201 Created) — dependant did not exist, created under primary member:
{
"success": true,
"message": "Dependant added successfully.",
"body": {
"dependantId": "660e8400-e29b-41d4-a716-446655440001",
"primaryMemberId": "550e8400-e29b-41d4-a716-446655440000",
"updatedFields": []
}
}
---
{
"success": false,
"error": {
"code": 10001,
"message": "Validation error"
},
"details": {
"errors": {
"id": ["Required"],
"employeeId": ["Required"]
}
}
}
{
"success": false,
"error": {
"code": 10072,
"message": "Invalid API key"
}
}
{
"success": false,
"error": {
"code": 10074,
"message": "Dependant does not belong to the specified employee"
}
}
{
"success": false,
"error": {
"code": 10075,
"message": "Primary member not found"
}
}
{
"success": false,
"error": {
"code": 10000,
"message": "Internal server error"
}
}
---
| Environment | URL |
|---|---|
| Staging | https://staging.api.platinum.retter.io/members/hr/dependant |
| UAT | https://uat.api.platinum.retter.io/members/hr/dependant |
---
All HR integration calls are logged with:
Successful operations create audit log entries with:
hr-update-dependant, event hr-dependant-updated, includes previous data, new data, and updated fieldshr-create-dependant, event hr-dependant-created, includes new data