{"id":23125,"date":"2026-05-31T17:38:14","date_gmt":"2026-05-31T17:38:14","guid":{"rendered":"https:\/\/engineerbabu.com\/blog\/?p=23125"},"modified":"2026-05-31T18:25:38","modified_gmt":"2026-05-31T18:25:38","slug":"integrate-ai-into-existing-ehr-system","status":"publish","type":"post","link":"https:\/\/engineerbabu.com\/blog\/integrate-ai-into-existing-ehr-system\/","title":{"rendered":"How to Integrate AI Into Your Existing EHR System in the USA: The 2026 Technical Guide"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Most US health systems that want AI in their clinical workflows are not starting from scratch. They have Epic, Cerner, or Athenahealth. They have years of patient data. They have established clinical workflows they cannot disrupt.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">They don&#8217;t need a new EHR. They need AI layered on top of the one they have.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This is a different engineering challenge from building a healthcare app from scratch. It requires understanding the EHR&#8217;s integration surfaces, working within Epic&#8217;s or Cerner&#8217;s governance processes, and building AI services that can respond within the latency windows that EHR workflows impose.<\/span><\/p>\n<h2><b>How AI Integrates with Existing EHR Systems<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">AI integrates with existing EHR systems through four primary pathways: CDS Hooks (real-time decision support embedded in clinical workflows), SMART on FHIR apps (third-party AI applications launched from within the EHR context), FHIR Bulk Data Export (asynchronous population-level data access for training AI models and population health analytics), and ambient documentation integration (AI-generated clinical notes written back to the EHR via FHIR DocumentReference or Epic-specific note write-back APIs).<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Each pathway has different technical requirements, EHR approval processes, and latency constraints.<\/span><\/p>\n<h2><b>The Four AI Integration Pathways<\/b><\/h2>\n<ul>\n<li aria-level=\"1\">\n<h3><b>Pathway 1: CDS Hooks (Real-Time Decision Support)<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-23140\" src=\"https:\/\/engineerbabu.com\/blog\/wp-content\/uploads\/2026\/05\/02_cds_latency.png\" alt=\"\" width=\"1920\" height=\"1000\" title=\"\"><\/p>\n<p><span style=\"font-weight: 400;\">CDS Hooks fires at specific clinical events, opening a chart, placing an order, starting an encounter and your <\/span><a href=\"https:\/\/engineerbabu.com\/services\/ai-development\"><span style=\"font-weight: 400;\">AI service<\/span><\/a><span style=\"font-weight: 400;\"> receives patient FHIR context and must return actionable recommendations within 2\u20133 seconds.<\/span><\/p>\n<p><b>Best for:<\/b><span style=\"font-weight: 400;\"> Risk stratification (sepsis, readmission, fall risk), drug interaction enhancement, care gap alerts, prior authorization pre-check.<\/span><\/p>\n<p><b>Technical requirements:<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">CDS Hooks service endpoint (Python FastAPI or Node.js)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/engineerbabu.com\/blog\/fhir-r4-integration-for-healthcare-startups\/\"><span style=\"font-weight: 400;\">FHIR R4<\/span><\/a><span style=\"font-weight: 400;\"> client for additional data retrieval if needed<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">ML model inference under 500ms (to fit within 2-second response window including network latency)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Epic App Orchard registration (for Epic CDS Hooks integration)<\/span><\/li>\n<\/ul>\n<p><b>Epic-specific process:<\/b><span style=\"font-weight: 400;\"> Register your CDS Hooks service in Epic&#8217;s Vendor Services program, submit an Interoperability Request specifying which hooks you need, receive approval, configure at each site. Each hospital site goes through its own IT governance approval.<\/span><\/p>\n<p><b>Code skeleton: CDS Hooks service:<\/b><\/p>\n<p><span style=\"font-weight: 400;\">from <\/span><span style=\"font-weight: 400;\">fastapi <\/span><span style=\"font-weight: 400;\">import <\/span><span style=\"font-weight: 400;\">FastAPI<\/span><\/p>\n<p><span style=\"font-weight: 400;\">from <\/span><span style=\"font-weight: 400;\">pydantic <\/span><span style=\"font-weight: 400;\">import <\/span><span style=\"font-weight: 400;\">BaseModel<\/span><\/p>\n<p><span style=\"font-weight: 400;\">import <\/span><span style=\"font-weight: 400;\">json<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">app = FastAPI()<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">class <\/span><span style=\"font-weight: 400;\">CDSHookRequest(BaseModel):<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0hookInstance: <\/span><span style=\"font-weight: 400;\">str<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">hook: <\/span><span style=\"font-weight: 400;\">str<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">context: <\/span><span style=\"font-weight: 400;\">dict<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">prefetch: <\/span><span style=\"font-weight: 400;\">dict <\/span><span style=\"font-weight: 400;\">= {}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">@app.post(<\/span><span style=\"font-weight: 400;\">&#8220;\/cds-services\/sepsis-risk&#8221;<\/span><span style=\"font-weight: 400;\">)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">async def <\/span><span style=\"font-weight: 400;\">sepsis_risk_assessment(request: CDSHookRequest):<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\"># Extract FHIR patient context<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">patient_id = request.context.get(<\/span><span style=\"font-weight: 400;\">&#8220;patientId&#8221;<\/span><span style=\"font-weight: 400;\">)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0vitals = request.prefetch.get(<\/span><span style=\"font-weight: 400;\">&#8220;vitals&#8221;<\/span><span style=\"font-weight: 400;\">, {})<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0labs = request.prefetch.get(<\/span><span style=\"font-weight: 400;\">&#8220;labs&#8221;<\/span><span style=\"font-weight: 400;\">, {})<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\"># Run ML model (must complete in &lt; 500ms)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">risk_score = sepsis_model.predict(vitals, labs)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\"># Return CDS Cards<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">if <\/span><span style=\"font-weight: 400;\">risk_score &gt; <\/span><span style=\"font-weight: 400;\">0<\/span><span style=\"font-weight: 400;\">.<\/span><span style=\"font-weight: 400;\">7<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">return <\/span><span style=\"font-weight: 400;\">{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;cards&#8221;<\/span><span style=\"font-weight: 400;\">: [{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;summary&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">f&#8221;Sepsis Risk Score: {risk_score:.0%}&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;indicator&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;critical&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;detail&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;Elevated sepsis risk. Consider blood cultures and lactate.&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;source&#8221;<\/span><span style=\"font-weight: 400;\">: {<\/span><span style=\"font-weight: 400;\">&#8220;label&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;AI Sepsis Monitor&#8221;<\/span><span style=\"font-weight: 400;\">},<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;suggestions&#8221;<\/span><span style=\"font-weight: 400;\">: [{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;label&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;Order sepsis bundle&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;uuid&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;sepsis-bundle-suggestion&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;actions&#8221;<\/span><span style=\"font-weight: 400;\">: [{<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;type&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;create&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;description&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;Order sepsis bundle&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;resource&#8221;<\/span><span style=\"font-weight: 400;\">: {<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;resourceType&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;ServiceRequest&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;status&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;draft&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;code&#8221;<\/span><span style=\"font-weight: 400;\">: {<\/span><span style=\"font-weight: 400;\">&#8220;coding&#8221;<\/span><span style=\"font-weight: 400;\">: [{<\/span><span style=\"font-weight: 400;\">&#8220;system&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;http:\/\/loinc.org&#8221;<\/span><span style=\"font-weight: 400;\">, <\/span><span style=\"font-weight: 400;\">&#8220;code&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;600-7&#8221;<\/span><span style=\"font-weight: 400;\">}]}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">return <\/span><span style=\"font-weight: 400;\">{<\/span><span style=\"font-weight: 400;\">&#8220;cards&#8221;<\/span><span style=\"font-weight: 400;\">: []}<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>Pathway 2: SMART on FHIR App (Embedded AI Application)<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">A full web application embedded within the EHR&#8217;s interface, launched from a button or sidebar in Epic Hyperspace or Cerner PowerChart. The app receives launch context (patient, encounter, user) and can read and write FHIR resources.<\/span><\/p>\n<p><b>Best for:<\/b><span style=\"font-weight: 400;\"> Ambient documentation review, complex clinical decision support requiring a rich UI, specialty-specific clinical tools, AI documentation assistance.<\/span><\/p>\n<p><b>Technical requirements:<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/docs.smarthealthit.org\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">SMART on FHIR<\/span><\/a><span style=\"font-weight: 400;\"> authorization implementation (OAuth 2.0 with PKCE)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">FHIR R4 client for reading patient context<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">React or Angular frontend (renders in an iFrame within Epic)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Backend AI services<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Epic App Orchard \/ Connection Hub listing<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">This is the most flexible integration, your app has a rich <\/span><a href=\"https:\/\/engineerbabu.com\/services\/ui-ux-design\"><span style=\"font-weight: 400;\">UI\/UX design<\/span><\/a><span style=\"font-weight: 400;\"> surface and can interact with complex FHIR data. The limitation: the iFrame rendering environment constrains some UI capabilities.<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>Pathway 3: FHIR Bulk Data Export (Async Population Access)<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">For AI models that need to train on or analyze population-level data, population health analytics, model training, quality measure analysis, cohort identification for clinical trials.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">FHIR Bulk Data Export allows asynchronous export of large patient datasets across your entire population, rather than querying individual patients one at a time.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"># Initiate bulk export<\/span><\/p>\n<p><span style=\"font-weight: 400;\">import <\/span><span style=\"font-weight: 400;\">requests<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\"># Kick off the export<\/span><\/p>\n<p><span style=\"font-weight: 400;\">response = requests.get(<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">f&#8221;{FHIR_BASE_URL}\/Patient\/$export?_type=Patient,Condition,Observation,MedicationRequest&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0headers={<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;Authorization&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">f&#8221;Bearer {access_token}&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;Accept&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;application\/fhir+json&#8221;<\/span><span style=\"font-weight: 400;\">,<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">&#8220;Prefer&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">&#8220;respond-async&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">}<\/span><\/p>\n<p><span style=\"font-weight: 400;\">)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\"># Get status URL from Content-Location header<\/span><\/p>\n<p><span style=\"font-weight: 400;\">status_url = response.headers[<\/span><span style=\"font-weight: 400;\">&#8220;Content-Location&#8221;<\/span><span style=\"font-weight: 400;\">]<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\"># Poll for completion<\/span><\/p>\n<p><span style=\"font-weight: 400;\">import <\/span><span style=\"font-weight: 400;\">time<\/span><\/p>\n<p><span style=\"font-weight: 400;\">while <\/span><span style=\"font-weight: 400;\">True:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0status_response = requests.get(status_url, headers={<\/span><span style=\"font-weight: 400;\">&#8220;Authorization&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">f&#8221;Bearer {access_token}&#8221;<\/span><span style=\"font-weight: 400;\">})<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">if <\/span><span style=\"font-weight: 400;\">status_response.status_code == <\/span><span style=\"font-weight: 400;\">200<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0output_urls = status_response.json()[<\/span><span style=\"font-weight: 400;\">&#8220;output&#8221;<\/span><span style=\"font-weight: 400;\">]<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">break<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0elif <\/span><span style=\"font-weight: 400;\">status_response.status_code == <\/span><span style=\"font-weight: 400;\">202<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0time.sleep(<\/span><span style=\"font-weight: 400;\">30<\/span><span style=\"font-weight: 400;\">)\u00a0 <\/span><span style=\"font-weight: 400;\"># Still processing<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">else<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">raise <\/span><span style=\"font-weight: 400;\">Exception(<\/span><span style=\"font-weight: 400;\">f&#8221;Export failed: {status_response.status_code}&#8221;<\/span><span style=\"font-weight: 400;\">)<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\"># Download exported NDJSON files<\/span><\/p>\n<p><span style=\"font-weight: 400;\">for <\/span><span style=\"font-weight: 400;\">output <\/span><span style=\"font-weight: 400;\">in <\/span><span style=\"font-weight: 400;\">output_urls:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0download_response = requests.get(output[<\/span><span style=\"font-weight: 400;\">&#8220;url&#8221;<\/span><span style=\"font-weight: 400;\">], headers={<\/span><span style=\"font-weight: 400;\">&#8220;Authorization&#8221;<\/span><span style=\"font-weight: 400;\">: <\/span><span style=\"font-weight: 400;\">f&#8221;Bearer {access_token}&#8221;<\/span><span style=\"font-weight: 400;\">})<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\"># Process NDJSON data<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\">for <\/span><span style=\"font-weight: 400;\">line <\/span><span style=\"font-weight: 400;\">in <\/span><span style=\"font-weight: 400;\">download_response.text.splitlines():<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0resource = json.loads(line)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span style=\"font-weight: 400;\"># Process FHIR resource<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>Pathway 4: Ambient Documentation Write-Back<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-23138\" src=\"https:\/\/engineerbabu.com\/blog\/wp-content\/uploads\/2026\/05\/05_read_vs_write.png\" alt=\"\" width=\"1920\" height=\"1000\" title=\"\"><\/p>\n<p><span style=\"font-weight: 400;\">The AI ambient scribe use case: AI listens to the encounter, generates a clinical note, and writes it back to the EHR for physician review and co-signature.<\/span><\/p>\n<p><b>Write-back mechanisms:<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>FHIR DocumentReference<\/b><span style=\"font-weight: 400;\"> (ClinicalNotes write-back): FHIR R4 Document Reference resource allows creating clinical notes that appear in the patient&#8217;s chart. Available on most FHIR R4-compliant EHRs.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Epic-native note write-back via SMART on FHIR<\/b><span style=\"font-weight: 400;\">: Epic&#8217;s ambient documentation API (used by Nuance DAX, Abridge, and other approved ambient scribe vendors) allows writing structured notes directly into Epic&#8217;s native note types. Requires Epic Workshop partnership, the deep integration tier.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>HL7 v2 MDM message<\/b><span style=\"font-weight: 400;\">: For legacy EHR environments, HL7 v2 MDM (Medical Document Management) messages can inject documents into the EHR&#8217;s document management system.<\/span><\/li>\n<\/ul>\n<p><b>The write-back approval challenge:<\/b><span style=\"font-weight: 400;\"> Writing clinical notes into an EHR creates patient records. EHRs, especially Epic, require additional approval, security review, and per-site clinical informatics committee sign-off for any AI that writes to the chart. Read-only integrations get approved faster than write integrations.<\/span><\/p>\n<h2><b>The AI Model Architecture That Works in EHR Contexts<\/b><\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-23139\" src=\"https:\/\/engineerbabu.com\/blog\/wp-content\/uploads\/2026\/05\/04_ai_architecture.png\" alt=\"\" width=\"1920\" height=\"1080\" title=\"\"><\/p>\n<p><span style=\"font-weight: 400;\">Not all AI architectures work within EHR integration constraints. The latency window (2\u20133 seconds for CDS Hooks) and the data privacy requirements (PHI cannot leave the HIPAA-compliant environment) shape what&#8217;s feasible.<\/span><\/p>\n<p><b>Recommended architecture for EHR-integrated AI:<\/b><\/p>\n<p><span style=\"font-weight: 400;\">EHR Event \u2192 CDS Hooks\/SMART Launch<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">FHIR Patient Context Assembly<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Feature Engineering (from FHIR resources)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">ML Model Inference (SageMaker endpoint or local Docker)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">LLM Recommendation Generation (Azure OpenAI, BAA-covered)<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">CDS Cards Response \/ SMART App Render<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2193<\/span><\/p>\n<p><span style=\"font-weight: 400;\">FHIR Write-Back (if approved)<\/span><\/p>\n<p><b>Latency optimization:<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Pre-fetch FHIR data via CDS Hooks prefetch templates (EHR sends data with the hook call rather than requiring your service to query it separately)<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Cache non-PHI reference data (payer policies, clinical guidelines) locally<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Keep <\/span><a href=\"https:\/\/engineerbabu.com\/technologies\/machine-learning-development-services\"><span style=\"font-weight: 400;\">ML model<\/span><\/a><span style=\"font-weight: 400;\"> inference under 500ms using optimized model formats (ONNX, TensorRT) or quantized models<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Use async LLM calls for recommendation generation when the 2-second window allows<\/span><\/li>\n<\/ul>\n<h2><b>The Governance Process You Cannot Shortcut<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">For Epic specifically, every AI integration requires:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Vendor Services registration<\/b><span style=\"font-weight: 400;\"> \u2192 production API credentials<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Interoperability Request<\/b><span style=\"font-weight: 400;\"> \u2192 approval for specific API access<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Connection Hub listing<\/b><span style=\"font-weight: 400;\"> (recommended) \u2192 discovery visibility<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Per-site IT governance<\/b><span style=\"font-weight: 400;\"> \u2192 each hospital site approves independently<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Clinical informatics review<\/b><span style=\"font-weight: 400;\"> (for write-back integrations) \u2192 clinical committee approval at each site<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">This process cannot be parallelized or compressed significantly. Plan for 4\u20138 months from starting vendor registration to first production go-live at a single Epic site. Each additional site adds 2\u20134 months.<\/span><\/p>\n<p><b>Author:<\/b><span style=\"font-weight: 400;\"> Mayank Pratap | Co-Founder, EngineerBabu | Google AI Accelerator 2024 \u00b7 CMMI Level 5<\/span><\/p>\n<h2><b>FAQ<\/b><\/h2>\n<ul>\n<li aria-level=\"1\">\n<h3><b>What is the easiest way to add AI to an existing EHR?<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">CDS Hooks with read-only FHIR access is the fastest path to production AI in an existing EHR workflow. It requires registration with the EHR vendor, per-site IT approval, but no write-back permissions. A CDS Hooks sepsis prediction or care gap alert can go from development to production in 4\u20136 months for a single site.<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>Can AI write notes directly to Epic?<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Yes, via FHIR DocumentReference write-back or Epic&#8217;s native note APIs. Both require additional approvals beyond read-only integration, particularly clinical informatics committee review at each site. The deep note write-back (writing into Epic&#8217;s native note types) requires Epic Workshop partnership, reserved for select ambient documentation vendors.<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>How do I train an AI model on my EHR data?<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">FHIR Bulk Data Export provides async access to population-level patient data for training. The data must be de-identified (HIPAA Safe Harbor or Expert Determination) before use in model training on infrastructure not covered by a BAA. For HIPAA-covered training (training on identifiable data within a compliant environment), use AWS SageMaker on HIPAA-eligible infrastructure with a signed AWS BAA.<\/span><\/p>\n<ul>\n<li aria-level=\"1\">\n<h3><b>What is the latency constraint for CDS Hooks AI services?<\/b><\/h3>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">CDS Hooks responses must arrive within 2\u20133 seconds of the hook firing or the EHR may time out and proceed without displaying your recommendations. ML model inference must complete in under 500ms to leave room for network latency and FHIR data retrieval. LLM-based components should generate responses of under 1,000 tokens within 1 second using Azure OpenAI&#8217;s GPT-4o or equivalent.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most US health systems that want AI in their clinical workflows are not starting from scratch. They have Epic, Cerner, or Athenahealth. They have years of patient data. They have established clinical workflows they cannot disrupt. They don&#8217;t need a new EHR. They need AI layered on top of the one they have. This is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":23127,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1246],"tags":[],"class_list":["post-23125","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-healthtech"],"_links":{"self":[{"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/posts\/23125","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/comments?post=23125"}],"version-history":[{"count":3,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/posts\/23125\/revisions"}],"predecessor-version":[{"id":23142,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/posts\/23125\/revisions\/23142"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/media\/23127"}],"wp:attachment":[{"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/media?parent=23125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/categories?post=23125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineerbabu.com\/blog\/wp-json\/wp\/v2\/tags?post=23125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}