Patient Portal & Mobile App Integration Specifications

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

HTTP
GET https://ehr.example.ae/fhir/Patient?identifier=784-1985-1234567-1
Authorization: Bearer {access_token}
Accept: application/fhir+json

Response (Patient)

JSON
{
  "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
HTTP
POST https://ehr.example.ae/fhir/Consent
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
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=active
  • GET /Condition?patient={id}&clinical-status=active
  • GET /DocumentReference?patient={id}&status=current
  • GET /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 internal outbox table 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 Bundle with transaction.
  • 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_dlq table
  • 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 (pending and error status) 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, existing Appointments.
  • 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

  • Appointment
  • Slot
  • Schedule
  • Practitioner, PractitionerRole, Location (read-only for display).

Example: Search Available Slots

HTTP
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)

JSON
{
  "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

HTTP
POST https://sched.example.ae/fhir/Appointment
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
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=booked
  • GET /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_dlq table 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 associated Observations (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

HTTP
GET https://lis.example.ae/fhir/DiagnosticReport?patient=PAT-2024001234&category=laboratory&_sort=-issued
Authorization: Bearer {access_token}

Response (excerpt)

JSON
{
  "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=-issued
  • GET /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_notifications or 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_dlq table
  • 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

HTTP
GET https://ris.example.ae/fhir/DiagnosticReport?patient=PAT-2024001234&category=imaging&_sort=-issued
Authorization: Bearer {access_token}
JSON
{
  "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_dlq table
  • 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

HTTP
GET https://pis.example.ae/fhir/MedicationRequest?patient=PAT-2024001234&status=active
Authorization: Bearer {access_token}

Response (excerpt)

JSON
{
  "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.

HTTP
POST https://pis.example.ae/fhir/ServiceRequest
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
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=active
  • GET /MedicationDispense?patient={id} (for dispensing history)

Error Handling

  • Refill submission 5xx
  • Retry: 20s, 40s, 80s (max 3).
  • If still failing, store request as pending in 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 pending in 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

HTTP
GET https://billing.example.ae/fhir/Account?patient=PAT-2024001234&status=active
Authorization: Bearer {access_token}
JSON
{
  "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.

HTTP
POST https://billing.example.ae/fhir/PaymentNotice
Authorization: Bearer {access_token}
Content-Type: application/fhir+json
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_payments with status pending_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_sync status 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

HTTP
POST https://physportal.example.ae/api/messages
Authorization: Bearer {internal_service_token}
Content-Type: application/json
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_messages with status pending_send.
  • Retry: 10s, 30s, 60s, 120s.
  • If still failing, mark as send_error and 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_messages with status send_error
  • Patient sees: "Your message is queued and will be delivered as soon as possible"
  • Background job retries send_error messages 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_error or pending_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

  1. Portal redirects user to UAE Pass authorization endpoint.
  2. UAE Pass authenticates user and redirects back with code.
  3. Portal backend exchanges code for tokens.

Token Request

HTTP
POST https://id.uaepass.ae/idshub/token
Authorization: Basic {client_id:client_secret (base64)}
Content-Type: application/x-www-form-urlencoded

Body:

Text
grant_type=authorization_code&
code={auth_code}&
redirect_uri=https%3A%2F%2Fportal.example.ae%2Fauth%2Fueapass%2Fcallback

ID Token (JWT payload example)

JSON
{
  "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 = TRUE for 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

HTTP
POST https://payments-gw.example.com/api/v1/payments
Authorization: Bearer {gateway_api_key}
Content-Type: application/json
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

JSON
{
  "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

HTTP
POST https://portal.example.ae/payments/callback
Content-Type: application/json
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:

  1. Verifies signature/HMAC.
  2. Updates patient_payments table.
  3. 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 unknown and 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 unknown and added to reconciliation queue
  • Finance team notified immediately
  • Refund failures → integration_dlq table; 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_payments records
  • 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

HL7 v2
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

HTTP
POST https://ehr.example.ae/api/hie/fetch
Authorization: Bearer {access_token}
Content-Type: application/json
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 Consent resources (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 = TRUE for 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 Consent resources

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_grants table.
  • Back-end services validate patient identity against patients.patient_id and 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.body
  • portal_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.

content/portals/patient-portal/05-integrations.md Generated 2026-02-20 22:54