Patient Portal & Mobile App Integration Specifications
Integration Summary
| ID | Target System | Direction | Trigger Event | Data Exchanged | Protocol | Frequency | Auth |
|---|---|---|---|---|---|---|---|
| INT-PPT-001 | EHR & Patient Management | Bidirectional | Patient views/updates profile, consents, documents | Inbound: demographics, allergies, problems, documents, consents. Outbound: updates, uploads | FHIR R4 REST API | On-demand (real-time) | OAuth 2.0 + mTLS |
| INT-PPT-002 | Scheduling | Bidirectional | Patient books/changes appointment | Inbound: slots, confirmations. Outbound: booking/cancel/reschedule requests | FHIR R4 REST API (Appointment/Slot) | On-demand (real-time) | OAuth 2.0 + mTLS |
| INT-PPT-003 | LIS | Inbound | New lab result released to patient | Lab results and history (DiagnosticReport, Observation) | FHIR R4 REST API | On-demand + event-driven push | OAuth 2.0 + mTLS |
| INT-PPT-004 | RIS | Inbound | Radiology report finalized and released | Radiology reports (DiagnosticReport) | FHIR R4 REST API | On-demand + event-driven push | OAuth 2.0 + mTLS |
| INT-PPT-005 | PIS | Bidirectional | Medication dispensed / refill requested | Inbound: medication list, dispensing status. Outbound: refill requests | FHIR R4 REST API | On-demand (real-time) | OAuth 2.0 + mTLS |
| INT-PPT-006 | Billing & Claims | Bidirectional | Statement generated / patient initiates payment | Inbound: statements, balances. Outbound: payment notifications | FHIR R4 REST API + payment gateway | On-demand (real-time) | OAuth 2.0 + mTLS |
| INT-PPT-007 | Physician Portal | Bidirectional | Patient/provider sends secure message | Secure messages, attachments, read status | Internal REST API | Real-time | Internal token + mTLS |
| INT-PPT-008 | UAE Pass | Bidirectional | Patient registration, login, identity verification | Identity assertions, tokens, profile attributes | OAuth 2.0 / OpenID Connect | On-demand (per login) | OIDC (UAE Pass) + mTLS |
| INT-PPT-009 | Payment Gateway | Bidirectional | Patient initiates payment or refund | Payment authorisation, capture, refund, status | REST API (PCI DSS compliant) | On-demand (real-time) | Payment-gateway keys + mTLS |
| INT-PPT-010 | NABIDH / Malaffi (HIE) | Outbound | Patient requests external records / consented sharing | Patient summary, results, documents (per HIE profiles) | HL7 v2.5.1 over MLLP/TLS + FHIR R4 | On-demand + near real-time | mTLS + OAuth 2.0 (HIE) |
INT-PPT-001: EHR & Patient Management
Business Context
What flows
- Inbound to portal (read from EHR):
- Patient demographics (name, Emirates ID, contact details, address, language).
- Clinical summary: allergies, problem list, medications (read-only here), immunisations, encounters.
- Patient documents (discharge summaries, clinic letters, PDFs).
- Consents (treatment, data sharing, telehealth, UAE PDPL consents).
- Outbound from portal (write to EHR):
- Demographic updates (phone, email, address, preferred language).
- New/updated consents (digital signatures, timestamps, scope).
- Patient-uploaded documents (e.g., external reports, ID copies).
- Patient-submitted forms (pre-registration, history, questionnaires).
When
- On login and when patient navigates to Health Records, Profile, or Forms screens.
- On submission of profile updates, consents, or forms.
Why
- Provide a single, trusted source of truth for patient data.
- Enable self-service updates while maintaining clinical data integrity.
- Support UAE PDPL requirements for consent capture and transparency.
How often
- On-demand, real-time REST calls; caching of non-sensitive read-only data (e.g., provider list) for performance.
Error impact
- Inbound failure: Patient may see stale or partial data; portal shows “data temporarily unavailable” banners and hides write actions that depend on missing context.
- Outbound failure: Demographic updates or consents may not reach EHR; must be queued and retried. If unrecoverable, registration staff alerted to reconcile manually.
FHIR R4 Technical Detail
The portal acts as a FHIR client to the EHR FHIR server.
Key Resources
Patient– demographics.AllergyIntolerance– allergies.Condition– problem list (ICD-10-AM / SNOMED CT).DocumentReference– clinical documents.Consent– data sharing and treatment consents.QuestionnaireResponse– patient-submitted forms (where EHR supports it).
Example: Read Patient Profile
Request
GET https://ehr.example.ae/fhir/Patient?identifier=784-1985-1234567-1
Authorization: Bearer {access_token}
Accept: application/fhir+json
Response (Patient)
{
"resourceType": "Bundle",
"type": "searchset",
"total": 1,
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "PAT-2024001234",
"identifier": [
{
"use": "official",
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "EID",
"display": "Emirates ID"
}
]
},
"system": "http://id.gov.ae/emirates-id",
"value": "784-1985-1234567-1"
},
{
"use": "usual",
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR",
"display": "Medical record number"
}
]
},
"system": "https://ehr.gatesgroup.ae/mrn",
"value": "MRN-00098765"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "Al-Maktoum",
"given": ["Ahmed"]
},
{
"use": "usual",
"text": "أحمد المكتوم"
}
],
"telecom": [
{
"system": "phone",
"value": "+971 50 123 4567",
"use": "mobile"
},
{
"system": "email",
"value": "[email protected]",
"use": "home"
}
],
"gender": "male",
"birthDate": "1985-03-15",
"address": [
{
"use": "home",
"text": "PO Box 12345, Dubai, United Arab Emirates",
"city": "Dubai",
"country": "AE"
}
],
"communication": [
{
"language": {
"coding": [
{
"system": "urn:ietf:bcp:47",
"code": "ar",
"display": "Arabic"
}
]
},
"preferred": true
}
],
"managingOrganization": {
"reference": "Organization/DUBAI-HOSP-01",
"display": "Dubai Gates General Hospital"
}
}
}
]
}
Mapping to Portal Tables
| FHIR Element | Portal Table | Column |
|---|---|---|
Patient.id |
portal_accounts |
patient_id (FK) |
Patient.identifier[MRN].value |
portal_accounts |
mrn (if stored) |
Patient.telecom[phone].value |
portal_accounts |
phone |
Patient.telecom[email].value |
portal_accounts |
email |
Patient.communication.language |
portal_preferences |
language |
Patient.name[ar].text |
portal_accounts |
display_name_ar |
Example: Create Consent from Portal
POST https://ehr.example.ae/fhir/Consent
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
{
"resourceType": "Consent",
"status": "active",
"scope": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/consentscope",
"code": "patient-privacy"
}
]
},
"category": [
{
"coding": [
{
"system": "http://snomed.info/sct",
"code": "57016003",
"display": "Consent given"
}
],
"text": "UAE PDPL data processing consent"
}
],
"patient": {
"reference": "Patient/PAT-2024001234",
"display": "Ahmed Al-Maktoum"
},
"dateTime": "2026-02-07T10:15:00+04:00",
"performer": [
{
"reference": "Patient/PAT-2024001234"
}
],
"organization": [
{
"reference": "Organization/DUBAI-HOSP-01"
}
],
"policy": [
{
"authority": "https://mohap.gov.ae",
"uri": "https://facility.example.ae/privacy/uae-pdpl"
}
],
"provision": {
"type": "permit",
"period": {
"start": "2026-02-07T10:15:00+04:00"
}
}
}
Search Parameters
GET /Patient?_id={id}GET /Patient?identifier={system}|{value}GET /AllergyIntolerance?patient={id}&clinical-status=activeGET /Condition?patient={id}&clinical-status=activeGET /DocumentReference?patient={id}&status=currentGET /Consent?patient={id}&status=active
Error Handling
- Network / 5xx from EHR
- Retry with exponential backoff: 15s, 30s, 60s, 120s (max 4 attempts).
- Queue outbound changes in
patient_submitted_forms/ an internaloutboxtable with status. -
If still failing after 4 attempts or >15 minutes, mark as
error, write to dead-letter queue, and:- Show non-technical message to patient: “We received your update and will process it shortly.”
- Notify registration/medical records work queue for manual reconciliation.
-
4xx (validation / authorization)
- Parse FHIR
OperationOutcome. - Do not retry automatically.
-
Log details; show patient a generic failure message for profile updates and prompt to contact support if persistent.
-
Partial failures (e.g., demographics updated, consent failed)
- Use transactional grouping where EHR supports FHIR
Bundlewithtransaction. -
If not supported, track each sub-operation; if any fails, flag the entire submission for manual review.
-
Data consistency
- Portal treats EHR as source of truth; after successful write, re-read the resource to confirm persisted state and update portal cache.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| Network timeout / EHR 5xx (read) | Exponential backoff | 15s, 30s, 60s, 120s | 4 |
| Network timeout / EHR 5xx (write) | Exponential backoff | 15s, 30s, 60s, 120s | 4 |
| EHR 4xx validation error | No auto-retry | N/A | Parse OperationOutcome; log; show generic error to patient |
| EHR 401/403 | No auto-retry | N/A | Refresh OAuth token; if still failing, raise security alert |
| Partial bundle failure | Selective retry | 30s, 60s | 2 (failed entries only) |
Dead Letter Queue:
- Outbound changes (demographics, consents, forms) exhausting all retries →
integration_dlqtable - Registration/medical records team notified for manual reconciliation
- Patient sees: "We received your update and will process it shortly"
- Retention: 30 days active, then archive
- Alert: portal admin + registration team notified on DLQ insertion
Idempotency:
- Deduplication key:
[patient_id]_[resource_type]_[operation]_[submission_datetime] - EHR performs idempotent upsert on matching resource ID
- Re-reads after successful write confirm persisted state
Reconciliation:
- Hourly comparison: portal outbox (
pendinganderrorstatus) vs EHR confirmed writes - Unprocessed outbound changes flagged for registration team
- Daily reconciliation report for portal admin
- Weekly audit of consent synchronisation between portal and EHR
INT-PPT-002: Scheduling
Business Context
What flows
- Inbound to portal:
- Provider schedules, appointment types (in-person, telehealth), available
Slots, existingAppointments. - Outbound from portal:
- New appointment bookings, reschedules, cancellations, pre-visit questionnaire submissions (via INT-PPT-001 forms).
When
- Patient searches for appointments, confirms booking, reschedules, or cancels.
- Appointment reminders/updates may be pulled to display on dashboard.
Why
- Reduce call centre load, improve patient convenience, and ensure real-time synchronisation with the enterprise scheduling system.
How often
- On-demand, real-time; no batch.
Error impact
- Booking failures may cause double-booking risk if not handled; portal must only confirm to patient after successful EHR response.
- If read failures occur, patient may not see latest availability.
FHIR R4 Technical Detail
Key Resources
AppointmentSlotSchedulePractitioner,PractitionerRole,Location(read-only for display).
Example: Search Available Slots
GET https://sched.example.ae/fhir/Slot?schedule.actor=Practitioner/PROV-1001&start=ge2026-02-10T00:00:00+04:00&end=lt2026-02-17T00:00:00+04:00&status=free
Authorization: Bearer {access_token}
Accept: application/fhir+json
Response (excerpt)
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "Slot",
"id": "SLOT-20260212-0900",
"status": "free",
"start": "2026-02-12T09:00:00+04:00",
"end": "2026-02-12T09:20:00+04:00",
"schedule": {
"reference": "Schedule/SCHED-CLINIC-01"
},
"serviceType": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/service-type",
"code": "57",
"display": "General practice"
}
]
}
]
}
}
]
}
Example: Create Appointment
POST https://sched.example.ae/fhir/Appointment
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
{
"resourceType": "Appointment",
"status": "booked",
"serviceCategory": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/service-category",
"code": "17",
"display": "General practice"
}
]
}
],
"serviceType": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/service-type",
"code": "57",
"display": "General consultation"
}
]
}
],
"appointmentType": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0276",
"code": "FOLLOWUP",
"display": "Follow-up"
}
]
},
"reasonCode": [
{
"text": "Follow-up for hypertension"
}
],
"start": "2026-02-12T09:00:00+04:00",
"end": "2026-02-12T09:20:00+04:00",
"slot": [
{
"reference": "Slot/SLOT-20260212-0900"
}
],
"participant": [
{
"actor": {
"reference": "Patient/PAT-2024001234",
"display": "Ahmed Al-Maktoum"
},
"status": "accepted"
},
{
"actor": {
"reference": "Practitioner/PROV-1001",
"display": "Dr. Sara Khan"
},
"status": "accepted"
}
]
}
Mapping to Portal Tables
| FHIR Element | Portal Table | Column |
|---|---|---|
Appointment.id |
telehealth_sessions |
appointment_id (FK) |
Appointment.start/end |
telehealth_sessions |
scheduled_datetime |
Appointment.participant |
telehealth_sessions |
patient_account_id, provider_id |
Search Parameters
GET /Appointment?patient={id}&date=ge{today}GET /Appointment?actor=Practitioner/{id}&status=bookedGET /Slot?schedule.actor=Practitioner/{id}&start=ge{start}&end=lt{end}&status=free
Error Handling
- Create/Update Appointment 409 Conflict
- Indicates slot already booked.
- Do not retry automatically.
-
Show patient message: “This time slot has just been taken. Please choose another slot.”
-
5xx from Scheduling
- Retry: 10s, 20s, 40s (max 3 attempts).
-
If still failing, do not show confirmation; show error and do not create local record.
-
Cancellation / Reschedule
-
If outbound cancel fails after retries, mark appointment in portal as “Cancellation pending” and create a manual work item for scheduling team.
-
Dead Letter
- Failed appointment operations stored in an integration DLQ with payload, error, and correlation ID; interface team dashboard for resolution.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| Scheduling API 5xx (slot query) | Exponential backoff | 10s, 20s, 40s | 3 |
| Scheduling API 5xx (create/update appointment) | Exponential backoff | 10s, 20s, 40s | 3 |
| Scheduling API 409 Conflict (slot taken) | No auto-retry | N/A | Refresh available slots; prompt patient to choose another |
| Cancellation/reschedule failure | Exponential backoff | 10s, 30s, 60s | 3 |
Dead Letter Queue:
- Failed appointment operations →
integration_dlqtable with payload, error, and correlation ID - Interface team dashboard for resolution
- If appointment creation fails: do not show confirmation to patient; show error
- If cancellation fails: mark as "Cancellation pending" in portal; create manual work item
- Retention: 30 days active, then archive
Idempotency:
- Deduplication key:
[patient_id]_[provider_id]_[slot_datetime]_[operation_type] - Scheduling module checks for duplicate appointment bookings for same patient/provider/time
- Duplicate create requests return existing appointment reference
Reconciliation:
- Hourly comparison: portal appointment references vs Scheduling module confirmed appointments
- Appointments in portal but not in Scheduling flagged for scheduling team
- Cancelled appointments in portal but still active in Scheduling flagged for resolution
- Daily reconciliation report for scheduling coordinator
INT-PPT-003: Laboratory Information System (LIS)
Business Context
What flows
- Inbound only to portal:
- Lab
DiagnosticReports and associatedObservations (results, units, reference ranges, abnormal flags). - Historical results for trend charts.
When
- When LIS marks a result as final and the EHR releases it to patients per facility “Result Release Rules” (e.g., 24–72 hour delay, or manual release).
Why
- Provide timely access to results, reduce phone calls, and meet patient engagement expectations while respecting clinical review and UAE PDPL transparency.
How often
- On-demand (when patient opens Results) plus event-driven push from LIS/EHR to trigger notifications.
Error impact
- If integration fails, results may not appear or notifications may be delayed; clinical care is not directly impacted but patient satisfaction is.
FHIR R4 Technical Detail
Portal reads from an EHR/LIS FHIR facade.
Example: DiagnosticReport + Observation
GET https://lis.example.ae/fhir/DiagnosticReport?patient=PAT-2024001234&category=laboratory&_sort=-issued
Authorization: Bearer {access_token}
Response (excerpt)
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "DiagnosticReport",
"id": "LAB-REP-20260205-001",
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0074",
"code": "LAB",
"display": "Laboratory"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "58410-2",
"display": "Complete blood count (CBC) panel"
}
],
"text": "CBC"
},
"subject": {
"reference": "Patient/PAT-2024001234",
"display": "Ahmed Al-Maktoum"
},
"effectiveDateTime": "2026-02-05T09:30:00+04:00",
"issued": "2026-02-05T11:00:00+04:00",
"performer": [
{
"reference": "Organization/DUBAI-LAB-01",
"display": "Dubai Gates Central Laboratory"
}
],
"result": [
{
"reference": "Observation/LAB-OBS-20260205-HB"
}
]
}
},
{
"resource": {
"resourceType": "Observation",
"id": "LAB-OBS-20260205-HB",
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "laboratory"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "718-7",
"display": "Hemoglobin [Mass/volume] in Blood"
}
],
"text": "Hemoglobin"
},
"subject": {
"reference": "Patient/PAT-2024001234"
},
"effectiveDateTime": "2026-02-05T09:30:00+04:00",
"valueQuantity": {
"value": 13.8,
"unit": "g/dL",
"system": "http://unitsofmeasure.org",
"code": "g/dL"
},
"referenceRange": [
{
"low": { "value": 13.0, "unit": "g/dL" },
"high": { "value": 17.0, "unit": "g/dL" }
}
],
"interpretation": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code": "N",
"display": "Normal"
}
]
}
]
}
}
]
}
Mapping to Portal Tables
| FHIR Element | Portal Table | Column |
|---|---|---|
DiagnosticReport.id |
lab_results (ref) |
result_id (FK) |
DiagnosticReport.issued |
portal_notifications |
sent_datetime (for result notification) |
Observation.valueQuantity |
UI only | Rendered in charts |
Search Parameters
GET /DiagnosticReport?patient={id}&category=laboratory&_sort=-issuedGET /Observation?patient={id}&code={LOINC}(for trends)
Error Handling
- Result fetch failure
- Show message: “Lab results are temporarily unavailable. Please try again later.”
- No retries on user-triggered GET; user can refresh manually.
-
Background notification jobs (for new results) retry 30s, 60s, 120s; then DLQ and alert portal admin.
-
Notification send failure
- If portal cannot create
portal_notificationsor send push/SMS, log and retry via internal notification queue; does not affect data integrity.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| LIS API 5xx (on-demand query) | No auto-retry | N/A | Patient can manually refresh; show "temporarily unavailable" |
| Background notification job (new result push) | Exponential backoff | 30s, 60s, 120s | 3 |
| Push/SMS notification send failure | Exponential backoff | 30s, 60s, 120s | 3 |
Dead Letter Queue:
- Background notification jobs exhausting all retries →
integration_dlqtable - Portal admin alerted; patient will see results on next manual refresh even if notification failed
- Retention: 30 days active, then archive
- Alert: portal admin notified on DLQ insertion
Idempotency:
- Deduplication key:
[patient_id]_[diagnostic_report_id]_[notification_type] - Duplicate new-result notifications suppressed; patient receives at most one notification per result
- On-demand queries are inherently idempotent (read-only)
Reconciliation:
- Daily comparison: LIS released results (patient-visible per release rules) vs portal notification records
- Results released but without corresponding patient notification flagged for re-notification
- Weekly report on notification delivery success rates
INT-PPT-004: Radiology Information System (RIS)
Business Context
What flows
- Inbound only:
- Radiology
DiagnosticReports (report text, impression, status). - Optionally links to imaging studies (DICOM URLs handled by imaging module, not directly in portal).
When
- When radiology report is finalized and released to patient per facility policy.
Why
- Provide patients with access to imaging reports and explanations, reducing follow-up calls.
How often
- On-demand + event-driven push for new reports.
Error impact
- Similar to LIS: delays in visibility and notifications; no direct clinical impact.
FHIR R4 Technical Detail
Example: Radiology Report
GET https://ris.example.ae/fhir/DiagnosticReport?patient=PAT-2024001234&category=imaging&_sort=-issued
Authorization: Bearer {access_token}
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "DiagnosticReport",
"id": "RAD-REP-20260203-001",
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0074",
"code": "RAD",
"display": "Radiology"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "36626-4",
"display": "CT Chest"
}
],
"text": "CT Chest"
},
"subject": {
"reference": "Patient/PAT-2024001234",
"display": "Ahmed Al-Maktoum"
},
"effectiveDateTime": "2026-02-03T14:00:00+04:00",
"issued": "2026-02-03T16:30:00+04:00",
"conclusion": "No evidence of pulmonary embolism.",
"conclusionCode": [
{
"coding": [
{
"system": "http://snomed.info/sct",
"code": "371923003",
"display": "Normal radiologic study"
}
]
}
]
}
}
]
}
Search Parameters
GET /DiagnosticReport?patient={id}&category=imaging&_sort=-issued
Error Handling
Same pattern as LIS (INT-PPT-003).
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| RIS API 5xx (on-demand query) | No auto-retry | N/A | Patient can manually refresh; show "temporarily unavailable" |
| Background notification job (new report push) | Exponential backoff | 30s, 60s, 120s | 3 |
| DICOM viewer link generation failure | Retry on next patient request | N/A | Show "Images temporarily unavailable; report text available" |
Dead Letter Queue:
- Same pattern as INT-PPT-003 (LIS); background notification jobs exhausting all retries →
integration_dlqtable - Portal admin alerted
- Retention: 30 days active, then archive
Idempotency:
- Deduplication key:
[patient_id]_[diagnostic_report_id]_[notification_type] - Same deduplication logic as INT-PPT-003
Reconciliation:
- Daily comparison: RIS finalized reports (patient-visible per release rules) vs portal notification records
- Reports released but without corresponding notification flagged for re-notification
- Weekly report on notification delivery success rates
INT-PPT-005: Pharmacy Information System (PIS)
Business Context
What flows
- Inbound:
- Current and historical medications, dispensing status (read-only for portal).
- Outbound:
- Refill requests initiated by patient, including preferred pharmacy and comments.
When
- Patient opens Medication screen or submits a refill request.
Why
- Reduce phone calls and manual refill workflows; improve adherence.
How often
- On-demand, real-time.
Error impact
- If inbound fails, medication list may be incomplete.
- If outbound fails, refill request may not reach provider/pharmacy; must be queued and monitored.
FHIR R4 Technical Detail
Example: Read Medication List
GET https://pis.example.ae/fhir/MedicationRequest?patient=PAT-2024001234&status=active
Authorization: Bearer {access_token}
Response (excerpt)
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "MedicationRequest",
"id": "ORD-20260115-001",
"status": "active",
"intent": "order",
"medicationCodeableConcept": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "314076",
"display": "Lisinopril 10 MG Oral Tablet"
}
],
"text": "Lisinopril 10mg Tablet"
},
"subject": {
"reference": "Patient/PAT-2024001234"
},
"authoredOn": "2026-01-15T10:00:00+04:00",
"dosageInstruction": [
{
"text": "Take 1 tablet by mouth once daily in the morning"
}
]
}
}
]
}
Example: Refill Request (ServiceRequest or Communication)
Implementation option 1: ServiceRequest for refill.
POST https://pis.example.ae/fhir/ServiceRequest
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
{
"resourceType": "ServiceRequest",
"status": "active",
"intent": "order",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/service-category",
"code": "RX",
"display": "Prescription management"
}
]
}
],
"code": {
"text": "Medication refill request"
},
"subject": {
"reference": "Patient/PAT-2024001234"
},
"authoredOn": "2026-02-07T09:45:00+04:00",
"requester": {
"reference": "Patient/PAT-2024001234"
},
"reasonReference": [
{
"reference": "MedicationRequest/ORD-20260115-001"
}
],
"note": [
{
"text": "Please refill at Dubai Hospital Outpatient Pharmacy."
}
]
}
Mapping to Portal Tables
| FHIR Element | Portal Table | Column |
|---|---|---|
ServiceRequest.id |
patient_submitted_forms or dedicated refill_requests |
form_id / request_id |
ServiceRequest.reasonReference |
pharmacy_orders (ref) |
order_id (FK) |
Search Parameters
GET /MedicationRequest?patient={id}&status=activeGET /MedicationDispense?patient={id}(for dispensing history)
Error Handling
- Refill submission 5xx
- Retry: 20s, 40s, 80s (max 3).
-
If still failing, store request as
pendingin portal DB and notify provider inbox via INT-PPT-007 as fallback. -
Validation 4xx
- If medication is no longer active or refill not allowed, show patient message and suggest contacting provider.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| PIS API 5xx (medication list query) | No auto-retry | N/A | Patient can manually refresh |
| Refill submission 5xx | Exponential backoff | 20s, 40s, 80s | 3 |
| Refill submission 4xx (validation) | No auto-retry | N/A | Show explanation to patient |
Dead Letter Queue:
- Refill requests exhausting all retries → stored as
pendingin portal DB - Provider notified via INT-PPT-007 (secure messaging) as fallback
- Portal shows: "Your refill request has been received and will be processed shortly"
- Retention: 30 days active, then archive
- Alert: portal admin + pharmacy team notified
Idempotency:
- Deduplication key:
[patient_id]_[medication_request_id]_[request_datetime] - PIS checks for duplicate refill requests for same medication within configurable window (e.g., 24 h)
- Duplicate requests acknowledged without creating new orders
Reconciliation:
- Daily comparison: portal refill requests (submitted) vs PIS refill orders (received)
- Requests submitted but not received by PIS flagged for pharmacy team
- Approved refills without corresponding dispensing action flagged for pharmacy
- Weekly refill request success rate report
INT-PPT-006: Billing & Claims
Business Context
What flows
- Inbound:
- Patient statements, balances, invoice details, payment plans.
- Outbound:
- Payment notifications to billing system after successful payment via payment gateway.
When
- Patient opens Billing screen or completes a payment.
Why
- Enable online bill viewing and payment; reduce in-person queues and paper statements.
How often
- On-demand, real-time.
Error impact
- If inbound fails, patient may not see up-to-date balances.
- If outbound fails, payment may not be reflected in billing even though gateway succeeded; reconciliation is critical.
FHIR R4 Technical Detail
Billing system exposes FHIR-like resources (e.g., Account, Invoice, PaymentNotice) or a custom REST API. Below assumes FHIR.
Example: Get Account Summary
GET https://billing.example.ae/fhir/Account?patient=PAT-2024001234&status=active
Authorization: Bearer {access_token}
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "Account",
"id": "ACC-2026-000123",
"status": "active",
"name": "Outpatient visits 2026",
"subject": [
{
"reference": "Patient/PAT-2024001234"
}
],
"balance": {
"value": 850.0,
"currency": "AED"
}
}
}
]
}
Example: Notify Payment (PaymentNotice)
After payment gateway confirms success, portal notifies billing.
POST https://billing.example.ae/fhir/PaymentNotice
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
{
"resourceType": "PaymentNotice",
"status": "active",
"created": "2026-02-07T11:20:00+04:00",
"paymentStatus": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/paymentstatus",
"code": "paid",
"display": "Paid"
}
]
},
"amount": {
"value": 250.0,
"currency": "AED"
},
"request": {
"reference": "Account/ACC-2026-000123"
},
"recipient": {
"reference": "Organization/DUBAI-HOSP-01"
}
}
Mapping to Portal Tables
| FHIR Element | Portal Table | Column |
|---|---|---|
Account.id |
patient_invoices |
invoice_id (FK) |
PaymentNotice.amount |
patient_payments |
amount |
PaymentNotice.created |
patient_payments |
payment_datetime |
Search Parameters
GET /Account?patient={id}GET /Invoice?account={id}(if implemented)GET /PaymentNotice?request=Account/{id}
Error Handling
- PaymentNotice failure
- Payment gateway success is source of truth for funds.
- If billing API fails, store payment in
patient_paymentswith statuspending_sync. - Retry every 5 minutes up to 24 hours.
-
After 24 hours, escalate to finance team with reconciliation report.
-
Balance display failure
- Show message: “Billing information is temporarily unavailable.”
- Do not block other portal functions.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| Billing API 5xx (balance/statement query) | No auto-retry | N/A | Patient can manually refresh |
| PaymentNotice POST failure to Billing | Timed retry | Every 5 min | Up to 288 (24 h) |
| Payment gateway callback not received | Poll gateway status API | Every 5 min for 1 h | 12 |
Dead Letter Queue:
- PaymentNotice failures after 24 h → escalate to finance team with reconciliation report
- Payment stored locally with
pending_syncstatus until confirmed - Retention: 90 days (financial data), then archive
- Alert: finance team notified for manual reconciliation
Idempotency:
- Deduplication key:
[payment_gateway_transaction_id] - Billing module checks for duplicate PaymentNotice by transaction ID
- Portal uses idempotency keys with payment gateway to prevent duplicate charges
Reconciliation:
- Daily: portal
patient_payments(status =pending_sync) vs Billing confirmed payments - Payments confirmed by gateway but not in Billing flagged for finance team
- Payments in Billing but not confirmed by gateway flagged for investigation
- Monthly financial reconciliation between portal, gateway, and Billing
INT-PPT-007: Physician Portal (Secure Messaging)
Business Context
What flows
- Bidirectional:
- Messages, attachments, read receipts between patient and provider teams.
When
- Patient sends a message from portal.
- Provider replies from Physician Portal.
Why
- Provide secure, auditable communication channel; reduce phone calls and paper notes.
How often
- Real-time.
Error impact
- Message delivery failures can delay communication; must be retried and surfaced to support if persistent.
Technical Detail (Internal REST API)
No HL7/FHIR; internal JSON over HTTPS.
Example: Send Message
POST https://physportal.example.ae/api/messages
Authorization: Bearer {internal_service_token}
Content-Type: application/json
{
"threadId": null,
"sender": {
"type": "patient",
"portalAccountId": "PA-100045"
},
"recipient": {
"type": "provider",
"providerId": "PROV-1001"
},
"subject": "Blood pressure readings",
"body": "Dear Doctor, my home readings this week are attached.",
"isUrgent": false,
"encounterId": null,
"attachments": [
{
"fileId": "ATT-20260207-001",
"fileName": "bp_readings.pdf",
"contentType": "application/pdf"
}
]
}
Mapping to Portal Tables
| Field | Portal Table | Column |
|---|---|---|
threadId |
portal_messages |
thread_id |
sender.portalAccountId |
portal_messages |
sender_id |
recipient.providerId |
portal_messages |
recipient_id |
subject |
portal_messages |
subject |
body |
portal_messages |
body |
isUrgent |
portal_messages |
is_urgent |
Error Handling
- Send failure (5xx / network)
- Write message to
portal_messageswith statuspending_send. - Retry: 10s, 30s, 60s, 120s.
-
If still failing, mark as
send_errorand show banner to patient: “Your message is queued and will be delivered as soon as possible.” -
Provider portal 4xx
- If invalid provider or patient context, log and show generic error; do not retry.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| Physician Portal 5xx / network timeout | Exponential backoff | 10s, 30s, 60s, 120s | 4 |
| Physician Portal 4xx | No auto-retry | N/A | Log error; show generic message to patient |
Dead Letter Queue:
- Messages exhausting all retries → stored in
portal_messageswith statussend_error - Patient sees: "Your message is queued and will be delivered as soon as possible"
- Background job retries
send_errormessages every 5 min for up to 4 h - After 4 h, alert portal admin and provider inbox team
- Retention: messages retained indefinitely as part of patient communication record
Idempotency:
- Deduplication key:
[thread_id]_[message_id]_[sent_datetime] - Physician Portal checks for duplicate messages by message ID
- Duplicate deliveries ignored; read status tracked independently
Reconciliation:
- Hourly: portal
portal_messages(status =send_errororpending_send) vs Physician Portal inbox - Messages in portal but not in provider inbox flagged for re-delivery
- Daily report on message delivery success rates and SLA compliance
INT-PPT-008: UAE Pass (Identity & SSO)
Business Context
What flows
- Inbound from UAE Pass:
- Identity token (Emirates ID, name, mobile, email), authentication assurance level.
- Outbound to UAE Pass:
- Authn requests, redirect URIs, logout requests.
When
- Patient registration/onboarding.
- Login and account linking.
Why
- Strong identity verification aligned with UAE government standards; reduces manual ID verification and supports PDPL-compliant consent.
How often
- On-demand per login/registration.
Error impact
- If UAE Pass is unavailable, patients cannot use SSO; fallback to local credentials if allowed.
Technical Detail (OAuth 2.0 / OpenID Connect)
Authorization Code Flow
- Portal redirects user to UAE Pass authorization endpoint.
- UAE Pass authenticates user and redirects back with
code. - Portal backend exchanges
codefor tokens.
Token Request
POST https://id.uaepass.ae/idshub/token
Authorization: Basic {client_id:client_secret (base64)}
Content-Type: application/x-www-form-urlencoded
Body:
grant_type=authorization_code&
code={auth_code}&
redirect_uri=https%3A%2F%2Fportal.example.ae%2Fauth%2Fueapass%2Fcallback
ID Token (JWT payload example)
{
"sub": "ae-uaepass-1234567890",
"name": "Ahmed Al-Maktoum",
"given_name": "Ahmed",
"family_name": "Al-Maktoum",
"phone_number": "+971501234567",
"email": "[email protected]",
"eid_number": "784-1985-1234567-1",
"acr": "urn:uae:uaepass:loa:high",
"iss": "https://id.uaepass.ae",
"aud": "PORTAL-CLIENT-ID",
"exp": 1767523200,
"iat": 1767519600
}
Mapping to Portal Tables
| Claim | Portal Table | Column |
|---|---|---|
sub |
portal_accounts |
uae_pass_subject |
eid_number |
portal_accounts |
emirates_id |
phone_number |
portal_accounts |
phone |
email |
portal_accounts |
email |
acr |
portal_accounts |
assurance_level |
link to patients.patient_id via Emirates ID or MRN match |
Error Handling
- UAE Pass unavailable
-
Show message: “UAE Pass is temporarily unavailable. You may try again later or use your portal username/password (if enabled).”
-
Token validation failure
- Reject login; do not create or link account.
- Log details for security audit (per ADHICS/TDRA guidance).
Retry and Recovery
Timeout and Circuit Breaker (synchronous integration — no DLQ/retry pattern):
| Scenario | Strategy | Threshold | Fallback |
|---|---|---|---|
| UAE Pass OIDC timeout | Hard timeout per request | 10 seconds | Show "UAE Pass is temporarily unavailable"; offer username/password login if enabled |
| Consecutive UAE Pass failures | Circuit breaker opens | 5 failures in 60 seconds | All UAE Pass logins bypassed for 60s; offer alternative login; circuit half-opens to test |
| Token validation failure | Immediate reject | N/A | Reject login; log security event; do not retry with same token |
Recovery:
- Circuit breaker states: Closed (normal) → Open (bypassed) → Half-Open (testing)
- When circuit opens: banner displayed "UAE Pass login temporarily unavailable; please use portal credentials"
- When circuit half-opens: single test request to UAE Pass; if successful, circuit closes
- All logins during outage logged with
uae_pass_bypassed = TRUEfor security audit
Reconciliation:
- Daily UAE Pass availability report: uptime percentage, average response time, timeout count
- Weekly review of accounts created during UAE Pass outage (verify identity was properly established)
INT-PPT-009: Payment Gateway
Business Context
What flows
- Outbound:
- Payment initiation (amount, currency, invoice reference, return URLs).
- Inbound:
- Payment success/failure callbacks, transaction IDs, authorisation codes.
When
- Patient clicks “Pay” and completes payment.
Why
- Enable secure online payments; gateway must be PCI DSS compliant.
How often
- On-demand, real-time.
Error impact
- If gateway call fails before payment, patient cannot pay.
- If callback fails after payment, billing may not be updated (handled via INT-PPT-006 reconciliation).
Technical Detail (REST API)
Example is generic; actual gateway (e.g., Network International, PayFort) will define specifics.
Initiate Payment
POST https://payments-gw.example.com/api/v1/payments
Authorization: Bearer {gateway_api_key}
Content-Type: application/json
{
"amount": 250.0,
"currency": "AED",
"reference": "INV-2026-000789",
"customer": {
"name": "Ahmed Al-Maktoum",
"email": "[email protected]",
"phone": "+971501234567"
},
"callbackUrl": "https://portal.example.ae/payments/callback",
"returnUrl": "https://portal.example.ae/billing/thank-you"
}
Gateway Response
{
"paymentId": "PG-20260207-001234",
"redirectUrl": "https://payments-gw.example.com/checkout/PG-20260207-001234",
"status": "pending"
}
Portal stores paymentId and redirects patient to redirectUrl.
Callback from Gateway
POST https://portal.example.ae/payments/callback
Content-Type: application/json
{
"paymentId": "PG-20260207-001234",
"reference": "INV-2026-000789",
"status": "success",
"amount": 250.0,
"currency": "AED",
"authCode": "A12345",
"transactionTime": "2026-02-07T11:20:00+04:00"
}
Portal:
- Verifies signature/HMAC.
- Updates
patient_paymentstable. - Calls INT-PPT-006 to notify billing.
Error Handling
- Gateway initiation failure
- Show message: “Payment service is temporarily unavailable. Please try again later.”
-
Do not create local payment record.
-
Callback not received
- Poll gateway status API every 5 minutes for up to 1 hour for
paymentId. -
If still unknown, mark payment as
unknownand notify finance. -
Security
- Validate gateway IPs and HMAC signatures.
- Never log full card data; only masked PAN and token if provided.
Retry and Recovery
Retry Strategy:
| Scenario | Strategy | Intervals | Max Attempts |
|---|---|---|---|
| Gateway initiation failure (5xx / network) | No auto-retry for patient-facing flow | N/A | Show "Payment service temporarily unavailable"; patient retries manually |
| Callback not received | Poll gateway status API | Every 5 min for 1 h | 12 |
| Refund API failure | Exponential backoff | 5m, 15m, 30m | 3 |
Dead Letter Queue:
- Payments with missing callbacks after 1 h → marked as
unknownand added to reconciliation queue - Finance team notified immediately
- Refund failures →
integration_dlqtable; finance team processes manually - Retention: 90 days (financial data), then archive
Idempotency:
- Deduplication key:
[paymentId]_[invoiceReference] - Gateway enforces idempotency via portal-generated idempotency key in payment initiation request
- Duplicate payment attempts return existing transaction result
Reconciliation:
- Daily end-of-day: gateway settlement report vs portal
patient_paymentsrecords - Payments in gateway but not in portal flagged for investigation
- Payments in portal but not in gateway flagged for investigation
- Monthly reconciliation between gateway, portal, and Billing module
- PCI DSS quarterly review of payment processing logs
INT-PPT-010: NABIDH / Malaffi (HIE Connectivity)
Business Context
What flows
- Outbound from HIS/EHR to HIE (not directly from portal, but portal relies on this data):
- Clinical documents, results, encounters per NABIDH (Dubai) and Malaffi (Abu Dhabi) profiles.
- Portal usage:
- Portal may query local EHR, which in turn has aggregated data from HIE.
- Optionally, portal may trigger EHR to query HIE on-demand (e.g., “View external records”).
When
- Patient requests to view external records and has given explicit consent for HIE access.
- Background synchronisation from EHR to HIE occurs per DOH/DHA policies (outside portal scope).
Why
- Provide a longitudinal view of patient data across facilities in UAE, respecting consent and PDPL.
HL7 v2.5.1 Technical Detail (HIE Feeds)
Although portal does not send HL7 directly, it must understand that some data originates from HIE and may be flagged as such.
Example ORU^R01 Result Message to NABIDH
MSH|^~\&|HIS_EHR|DUBAI-HOSP-01|NABIDH|DHA|20260207113000||ORU^R01|MSG20260207113000001|P|2.5.1|||AL|NE||UTF-8
PID|1||MRN-00098765^^^DUBAI-HOSP-01^MR~784-1985-1234567-1^^^UAE^EID||AL-MAKTOUM^AHMED^^^MR||19850315|M|||PO BOX 12345^^DUBAI^^00000^AE||+971501234567
OBR|1|LAB-ORD-20260205-001|LAB-RES-20260205-001|718-7^Hemoglobin [Mass/volume] in Blood^LN||20260205093000|||||||||KHAN^SARA^^^^^DR|||||||||F
OBX|1|NM|718-7^Hemoglobin [Mass/volume] in Blood^LN||13.8|g/dL|13.0-17.0|N|||F|||20260205110000
Key Segment Notes
PID-3: Includes Emirates ID as required by NABIDH/Malaffi.OBR/OBX: LOINC-coded tests and results.
Portal may display an “External source: NABIDH/Malaffi” indicator based on metadata from EHR.
FHIR R4 Technical Detail (HIE Queries via EHR)
Portal → EHR → HIE (indirect).
Example: Trigger External Records Fetch
POST https://ehr.example.ae/api/hie/fetch
Authorization: Bearer {access_token}
Content-Type: application/json
{
"patientId": "PAT-2024001234",
"hie": "NABIDH",
"consentId": "CONSENT-2026-0001"
}
EHR then uses HIE-specific FHIR/HL7 endpoints; portal only polls EHR for updated data.
HIE Compliance Requirements (Portal Perspective)
- Display clear indication when data originates from external HIE.
- Respect consent status from
Consentresources (no HIE-triggered fetch if consent withdrawn). - Ensure audit logging of patient-initiated HIE views (who, when, what).
Error Handling
| Scenario | Handling |
|---|---|
| HIE query timeout (via EHR proxy) | Show message: "External records are temporarily unavailable. Your local records are shown below." |
| HIE authentication failure | Stop HIE queries; alert integration security team; show only local records |
| Consent not found or withdrawn | Do not initiate HIE query; show message: "To view external records, please update your consent in Settings." |
| Partial HIE results | Display available data with indicator: "Some external records may not be shown" |
Retry and Recovery
Timeout and Circuit Breaker (synchronous integration — no DLQ/retry pattern):
| Scenario | Strategy | Threshold | Fallback |
|---|---|---|---|
| HIE query timeout (via EHR) | Hard timeout per request | 15 seconds | Show local records only with banner "External records unavailable" |
| Consecutive HIE failures | Circuit breaker opens | 3 failures in 5 minutes | All HIE queries bypassed for 5 min; circuit half-opens to test |
| HIE authentication failure | Immediate stop | N/A | Alert security team; do not degrade to unauthenticated queries |
Recovery:
- Circuit breaker states: Closed (normal) → Open (bypassed) → Half-Open (testing)
- When circuit opens: portal shows only local EHR data; "External records unavailable" banner
- When circuit half-opens: single test query; if successful, circuit closes
- All queries during outage logged with
hie_bypassed = TRUEfor audit
Reconciliation:
- Daily HIE connectivity report: availability, average response time, timeout count
- Weekly review of patient-initiated HIE queries vs successful responses
- Consent status audit: ensure portal consent flags match EHR
Consentresources
Authentication & Security
Transport Security
- All integrations use TLS 1.2+ with mutual TLS (mTLS) for:
- EHR & Patient Management (INT-PPT-001)
- Scheduling (INT-PPT-002)
- LIS/RIS/PIS/Billing (INT-PPT-003–006)
- HIE (INT-PPT-010)
- Certificates managed per TDRA/NESA and ADHICS guidelines:
- Hardware Security Module (HSM) or secure key vault for private keys.
- Regular rotation and revocation procedures.
Application-Level Authentication
- OAuth 2.0 / OpenID Connect
- Portal as OAuth client to EHR FHIR server, scheduling, LIS/RIS/PIS, billing.
- UAE Pass as external IdP for patient authentication (INT-PPT-008).
-
Access tokens are short-lived; refresh tokens stored securely server-side only.
-
Internal Tokens
-
For internal APIs (Physician Portal, HIE trigger endpoints), use signed JWTs with:
- Audience and issuer validation.
- Short expiry (e.g., 5–15 minutes).
-
API Keys
- Payment gateway uses API keys or OAuth depending on provider.
- Keys stored encrypted and rotated regularly.
Authorization
- Portal enforces role-based access:
- Patient vs Parent/Guardian vs Provider vs Portal Administrator.
- Proxy access rules from
proxy_access_grantstable. - Back-end services validate patient identity against
patients.patient_idand Emirates ID.
Data Protection & UAE PDPL
- Only minimum necessary data requested from external systems.
- Audit logs for:
- Logins (including UAE Pass).
- Record views (especially results and external HIE data).
- Consent changes and form submissions.
- Data at rest encrypted for:
portal_messages.bodyportal_notifications.body- Sensitive form data (
patient_submitted_forms.form_data_json).
Error Handling & Monitoring (Cross-Cutting)
- Centralised integration log with:
- Correlation IDs per request.
- Request/response metadata (no sensitive payloads in logs).
- Alert thresholds:
-
5% error rate for any integration over 15 minutes.
- Any sustained 5xx from core systems >10 minutes.
- Dead Letter Queues:
- Separate DLQ per integration (EHR, Scheduling, LIS, PIS, Billing, Messaging).
- Admin UI for replay after correction.