Nirmitee.io
Integrating RPM Data into Epic and Cerner: The EHR Integration Playbook for Device Data

Integrating RPM Data into Epic and Cerner: The EHR Integration Playbook for Device Data

May 5, 2026
14 min read
Interoperability

"Difficulties integrating third-party RPM data into the EHR" consistently ranks as the top challenge healthcare organizations face when deploying Remote Patient Monitoring programs. In a 2025 KLAS Research survey, 67% of health systems cited EHR integration as the primary barrier to RPM program expansion. 

The core problem is deceptively simple: how do you take a blood pressure reading captured on a patient's kitchen counter at 7 AM and make it appear in the clinician's workflow inside Epic or Cerner as if it were any other vital sign?

The answer involves navigating vendor-specific APIs, mapping LOINC codes to proprietary flowsheet identifiers, handling timezone conflicts between devices and EHR servers, and ensuring that RPM data is distinguishable from in-office measurements while remaining clinically accessible. 

This guide provides the complete integration playbook for the two dominant EHR platforms — Epic and Oracle Health/Cerner — plus the FHIR-based approach that works across all modern systems.

Epic Integration: Flowsheets, MyChart, and Storyboard

Epic offers three primary integration points for RPM device data. Each serves a different clinical user and workflow.

Integration Path 1: Flowsheet Rows for Device Vitals

Flowsheet rows are Epic's mechanism for storing time-series clinical data. Each vital sign type maps to a specific flowsheet row ID. RPM data must be written to designated "remote" flowsheet rows that are separate from in-office vitals — this allows clinicians to view RPM data alongside traditional vitals while maintaining clear provenance about the data source.

LOINC to Epic Flowsheet Mapping

RPM VitalLOINC CodeEpic Flowsheet TemplateDisplay Location
Systolic Blood Pressure8480-6IP VITALS - REMOTE BP SYSVitals flowsheet, marked as "Remote"
Diastolic Blood Pressure8462-4IP VITALS - REMOTE BP DIAVitals flowsheet, marked as "Remote"
Blood Glucose2339-0IP GLUCOSE - REMOTEGlucose management flowsheet
SpO22708-6IP VITALS - REMOTE SPO2Vitals flowsheet, marked as "Remote"
Body Weight29463-7IP VITALS - REMOTE WTVitals flowsheet, marked as "Remote"
Heart Rate8867-4IP VITALS - REMOTE HRVitals flowsheet, marked as "Remote"
Body Temperature8310-5IP VITALS - REMOTE TEMPVitals flowsheet, marked as "Remote"

Critical detail: Epic flowsheet row IDs are organization-specific. The IDs shown above are template names — each Epic installation assigns unique internal IDs during build. Your integration team must work with Epic analysts at the target organization to obtain the correct flowsheet row IDs for their specific build.

# Python: Write RPM observation to Epic FHIR R4 API
import requests
from datetime import datetime

class EpicFHIRWriter:
    def __init__(self, base_url: str, access_token: str):
        self.base_url = base_url
        self.headers = {
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/fhir+json"
        }

    def write_blood_pressure(self, patient_fhir_id: str,
                              systolic: float, diastolic: float,
                              device_id: str, timestamp: str) -> dict:
        observation = {
            "resourceType": "Observation",
            "status": "final",
            "category": [{
                "coding": [{
                    "system": "http://terminology.hl7.org/CodeSystem/observation-category",
                    "code": "vital-signs"
                }]
            }],
            "code": {
                "coding": [{
                    "system": "http://loinc.org",
                    "code": "85354-9",
                    "display": "Blood pressure panel"
                }]
            },
            "subject": {"reference": f"Patient/{patient_fhir_id}"},
            "effectiveDateTime": timestamp,
            "device": {"reference": f"Device/{device_id}"},
            "component": [
                {
                    "code": {"coding": [{"system": "http://loinc.org", "code": "8480-6"}]},
                    "valueQuantity": {"value": systolic, "unit": "mmHg",
                                      "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}
                },
                {
                    "code": {"coding": [{"system": "http://loinc.org", "code": "8462-4"}]},
                    "valueQuantity": {"value": diastolic, "unit": "mmHg",
                                      "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}
                }
            ]
        }

        response = requests.post(
            f"{self.base_url}/api/FHIR/R4/Observation",
            json=observation,
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

Integration Path 2: MyChart for Patient-Facing Data

MyChart is Epic's patient portal. When RPM data is properly written to flowsheet rows, it automatically appears in the patient's MyChart Health Summary under vitals. Patients can view their blood pressure trends, glucose readings, and weight alongside data collected during office visits.

For RPM programs, MyChart integration serves two purposes: patient engagement (patients can see their own data and trends) and billing compliance (CMS requires that patients have access to their RPM data).

Integration Path 3: Storyboard for Clinician Context

Epic Storyboard is the sidebar that appears when a clinician opens a patient's chart. It shows a summary of recent activity, upcoming appointments, and clinical highlights. RPM data can be surfaced here through a custom Storyboard activity that shows the most recent device readings and any active alerts.

Storyboard integration requires building an Epic App Orchard application. The application uses the SMART on FHIR launch protocol to receive patient context when the clinician opens a chart, then queries the RPM platform for that patient's latest data and displays it in the Storyboard panel. For implementation details, see our guide on implementing SMART on FHIR.

Oracle Health/Cerner Integration: HL7v2 ORU Messages and Millennium API

Oracle Health (formerly Cerner) supports two primary integration methods for external device data: traditional HL7v2 messaging and the modern Millennium Data API.

HL7v2 ORU Messages

The ORU^R01 (Observation Result Unsolicited) message is the standard mechanism for sending device results into Cerner. Many Cerner installations still prefer HL7v2 over FHIR for high-volume data ingestion because their interface engines (Cloverleaf, Mirth Connect) are already configured for HL7v2 routing.

// HL7v2 ORU^R01 message for RPM blood pressure reading
MSH|^~\&|RPM_PLATFORM|RPM_FACILITY|CERNER|HOSPITAL|20260316083000||ORU^R01^ORU_R01|MSG00001|P|2.5.1
PID|1||MRN12345^^^HOSPITAL^MR||Smith^John||19650415|M
OBR|1||RPM20260316-001|85354-9^Blood pressure panel^LN|||20260316083000
OBX|1|NM|8480-6^Systolic blood pressure^LN||142|mm[Hg]|90-120|H|||F|||20260316083000
OBX|2|NM|8462-4^Diastolic blood pressure^LN||91|mm[Hg]|60-80|H|||F|||20260316083000
OBX|3|NM|8867-4^Heart rate^LN||78|/min|60-100|N|||F|||20260316083000
OBX|4|ST|RPM_DEVICE_ID^Device Identifier^L||OMRON-VS-88421||||||F
OBX|5|ST|RPM_SOURCE^Data Source^L||REMOTE_PATIENT_MONITORING||||||F

Key fields in the ORU message that are specific to RPM data:

  • OBX-8 (Abnormal Flags): H (High), L (Low), N (Normal) — based on clinical thresholds. Cerner uses these flags to trigger alerts in PowerChart
  • OBX-14 (Date/Time of Observation): Must be the device measurement timestamp, not the time of message transmission. This is critical for accurate clinical trending
  • Custom OBX segments: Include device identifier and data source indicator (RPM vs in-office) as additional OBX segments for provenance tracking

For organizations using Mirth Connect for HL7 integration, the RPM-to-Cerner ORU pipeline can be configured as a standard channel with source (RPM platform webhook), filter (validate required fields), transformer (map LOINC codes to Cerner result codes), and destination (Cerner MLLP endpoint).

Millennium Data API (FHIR R4)

Cerner Millennium supports FHIR R4 Observation writes through the Ignite/Code program APIs. The FHIR approach is identical to Epic FHIR writes — create an Observation resource with proper LOINC coding, patient reference, and device reference. The advantage of FHIR is a single integration codebase that works across both Epic and Cerner.

PowerChart RPM Workflow

Oracle Health PowerChart is the clinician-facing application. RPM data appears in the results section under the clinical category configured during interface setup. Best practice is to create a dedicated "Remote Monitoring" results category that aggregates all RPM vitals in a single view, separate from lab results and in-office vitals, but accessible from the same patient chart.

Common Integration Challenges and Solutions

Challenge 1: Device Timestamp vs. EHR Timestamp

A patient in the Eastern timezone takes a blood pressure reading at 8:30 AM ET. The device records the timestamp in UTC as 13:30. The EHR server runs in the Central timezone. If timezone conversion is not handled correctly, the reading could appear as 7:30 AM CT, 8:30 AM ET, or 1:30 PM UTC — three different times for the same measurement.

Solution: Normalize all timestamps to UTC at the point of ingestion in the RPM data pipeline. Store the patient's timezone as metadata. When writing to the EHR, convert to the timezone expected by the specific EHR installation (typically the facility's local timezone). Include the original device timezone in a FHIR extension for audit purposes.

Challenge 2: Duplicate Readings

Patients sometimes take multiple readings in rapid succession — two blood pressure readings 30 seconds apart, or accidentally triggering the scale twice. Without deduplication, both readings appear in the chart, potentially skewing trend analysis.

Solution: Implement a deduplication window per vital type: 2 minutes for blood pressure, 5 minutes for glucose (pre/post meal readings may be intentional), 1 minute for weight, 30 seconds for SpO2. If multiple readings arrive within the window, keep the last reading (most likely to be accurate as the patient re-positioned the device).

Challenge 3: Data Quality (Patient Measurement Errors)

RPM data quality is inherently lower than in-office measurements because there is no clinical staff supervising the patient. Common errors include: blood pressure taken with the cuff over clothing, glucose measured immediately after eating, weight measured with shoes and heavy clothing, pulse oximeter on a cold or wet finger.

Solution: Implement physiological plausibility checks before EHR write-back. Flag readings that are physiologically possible but likely erroneous (systolic BP of 220 in a patient whose baseline is 130 — possible but should be flagged for clinician review before auto-filing). Never auto-file flagged readings; route them to a clinical review queue.

Challenge 4: Where Does RPM Data Appear in the Chart?

The clinical workflow question is as important as the technical integration. If RPM data is buried in a results tab that clinicians rarely open, the integration is technically complete but clinically useless.

Best practices for clinical visibility:

  • Display RPM vitals on the same vitals trend graph as in-office readings, with a visual indicator (different color or icon) showing the data source
  • Show the most recent RPM reading and 7-day trend in the patient header or storyboard, so clinicians see it immediately upon opening the chart
  • Include RPM data in the clinical decision support rules that fire during the encounter (e.g., if the last 3 RPM blood pressures averaged over 150, suggest medication review)
  • Surface RPM adherence metrics (days with data, missed readings) alongside the vital signs, so clinicians can assess engagement

The FHIR-First Approach: One Integration for All EHRs

Rather than building separate integrations for Epic (flowsheets) and Cerner (ORU messages), a FHIR-first approach writes FHIR R4 Observations to a standardized API endpoint. Both Epic and Cerner support FHIR R4 Observation writes, making a single codebase possible.

FHIR Observation with Device Metadata and Provenance

// Complete FHIR Observation with device metadata for RPM write-back
{
  "resourceType": "Observation",
  "status": "final",
  "category": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/observation-category",
      "code": "vital-signs",
      "display": "Vital Signs"
    }]
  }],
  "code": {
    "coding": [{
      "system": "http://loinc.org",
      "code": "85354-9",
      "display": "Blood pressure panel"
    }]
  },
  "subject": {"reference": "Patient/epic-fhir-12345"},
  "effectiveDateTime": "2026-03-16T08:30:00-05:00",
  "device": {
    "reference": "Device/omron-vitalsight-88421",
    "display": "Omron VitalSight BP Monitor"
  },
  "extension": [{
    "url": "http://example.com/fhir/StructureDefinition/data-source",
    "valueString": "remote-patient-monitoring"
  }],
  "component": [
    {
      "code": {"coding": [{"system": "http://loinc.org", "code": "8480-6", "display": "Systolic BP"}]},
      "valueQuantity": {"value": 142, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}
    },
    {
      "code": {"coding": [{"system": "http://loinc.org", "code": "8462-4", "display": "Diastolic BP"}]},
      "valueQuantity": {"value": 91, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}
    }
  ]
}

// Provenance resource for data source tracking
{
  "resourceType": "Provenance",
  "target": [{"reference": "Observation/rpm-bp-20260316-001"}],
  "recorded": "2026-03-16T08:30:05-05:00",
  "agent": [{
    "type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", "code": "author"}]},
    "who": {"display": "RPM Platform v3.2"}
  }],
  "entity": [{
    "role": "source",
    "what": {"display": "Omron VitalSight BLE transmission, Device ID: VS-88421"}
  }]
}

Integration Timeline by EHR Vendor

PhaseEpic (App Orchard)Oracle Health/Cerner (Code)FHIR-Only EHRs
Registration/Enrollment2-4 weeks1-2 weeks1 week
Sandbox Development3-4 months3-4 months2-3 months
Integration Testing4-6 weeks3-4 weeks2-3 weeks
Vendor Review/Approval4-8 weeks2-4 weeks1-2 weeks
Customer Go-Live2-4 weeks per site2-3 weeks per site1-2 weeks per site
Total6-9 months4-7 months2-4 months

Epic's longer timeline is primarily due to the App Orchard review process, which involves a detailed security assessment, clinical workflow review, and EHR interaction validation. Cerner's Code program is somewhat faster but still requires formal enrollment and validation. For a deeper discussion of EHR integration timelines and challenges, see our comprehensive guide on how EHR integration unlocks interoperability.

Pre-Launch Integration Checklist

Before going live with RPM-to-EHR data integration, verify the following:

CategoryChecklist ItemStatus
TechnicalFHIR API credentials configured and tested in production 
TechnicalFlowsheet/result mapping validated for all vital types 
TechnicalTimezone handling tested across device, server, and EHR 
TechnicalDuplicate detection enabled with appropriate windows 
TechnicalError handling, retry logic, and dead-letter queue configured 
TechnicalPHI encryption verified end-to-end (device to EHR) 
ClinicalClinical workflow documented and approved by CMIO 
ClinicalNursing and provider staff trained on RPM data location in chart 
ClinicalAlert thresholds configured and validated by clinical team 
ClinicalEscalation protocols defined for critical RPM alerts 
ClinicalPatient consent captured and documented in EHR 
ClinicalGo-live date communicated to all clinical stakeholders 

Sources: Epic App Market Integration Guide, Oracle Health Code Program Documentation, HL7 FHIR R4 Observation Resource Specification, KLAS Research "Remote Patient Monitoring 2025" Report, CMS Interoperability and Patient Access Final Rule.

Frequently Asked Questions

Can I write RPM data to Epic without going through App Orchard?

No. All third-party applications that write data to Epic must be registered through App Orchard (now called the Epic App Market). This includes RPM platforms that write Observations via FHIR. The App Orchard review ensures that applications meet Epic's security, privacy, and clinical safety standards. The only exception is if the health system builds the RPM integration internally using their own Epic web services credentials.

Should I use FHIR or HL7v2 for Cerner integration?

If the target Cerner installation already has an active HL7v2 interface engine (Mirth Connect, Cloverleaf, Rhapsody), HL7v2 ORU messages are often the fastest path to production because the infrastructure is already in place. For new integrations or multi-EHR deployments, FHIR R4 is the better long-term investment because a single integration codebase works across both Epic and Cerner.

How do I distinguish RPM data from in-office vitals in the EHR?

Three mechanisms: (1) Write RPM data to dedicated flowsheet rows labeled "Remote" rather than the standard vital sign rows, (2) Include a data source extension in the FHIR Observation identifying the reading as remote patient monitoring, (3) Create a Provenance resource linking each Observation to the RPM platform and device that generated it. Clinicians should see a visual indicator (different color, icon, or label) in the chart that distinguishes remote readings from in-office measurements.

What if the patient's MRN in our RPM system doesn't match the EHR MRN?

Patient identity matching is critical. Options include: (1) Use the EHR's FHIR Patient/$match operation to perform probabilistic matching based on demographics, (2) Store the EHR patient FHIR ID at RPM enrollment time, (3) Integrate with a Master Patient Index (MPI) that maps identifiers across systems. Never write RPM data without a confirmed patient identity match — filing data on the wrong patient is a patient safety event.

How do I handle RPM data when the EHR is down for maintenance?

Implement a message queue (Kafka, RabbitMQ, or SQS) between the RPM platform and the EHR integration layer. During EHR downtime, messages queue up and are processed when the EHR comes back online. Include a dead-letter queue for messages that fail after multiple retry attempts, and alerting for queue depth that exceeds normal levels. RPM clinical alerts should still be delivered to clinicians via SMS/push even when the EHR integration is temporarily unavailable.

Do RPM integrations require a separate BAA with the EHR vendor?

The BAA is typically between the healthcare organization (the covered entity) and the RPM platform vendor (the business associate). Epic and Cerner do not require a separate BAA for API access, but they do require that connected applications comply with their security and privacy requirements. The App Orchard/Code program enrollment validates this compliance.