Chapter 7: EHR Integration & Interoperability

弘익人間 (Hongik Ingan) - "Benefit All Humanity"
Seamless integration with existing healthcare systems ensures digital therapy becomes part of comprehensive care rather than an isolated silo. True benefit comes from coordination across the entire care ecosystem.

7.1 HL7 FHIR Standards

Fast Healthcare Interoperability Resources (FHIR) is the latest standard for exchanging healthcare information electronically. Developed by HL7, FHIR uses modern web technologies (RESTful APIs, JSON/XML, OAuth2) making integration far simpler than previous standards.

FHIR Resource Types for Mental Health

FHIR Resource Purpose Example Use Mapped WIA Data
Patient Demographics and identifiers Linking digital therapy user to EHR patient Name, DOB, MRN, contact info
Condition Diagnoses and problem list Major Depressive Disorder, GAD Primary diagnosis, comorbidities
Observation Clinical measurements PHQ-9 score, mood ratings Assessment scores, vital signs
CarePlan Treatment plans and goals 12-week CBT program, therapy goals Treatment plan, milestones
Procedure Interventions performed Therapy session, assessment administration Session records, interventions
MedicationStatement Current medications Antidepressant regimen Medication tracking (if applicable)
DocumentReference Clinical documents Progress notes, discharge summaries Session notes, reports

FHIR Implementation

// FHIR Client Implementation
class FHIRIntegrationService {
  private client: FHIRClient;
  private baseUrl: string;
  
  constructor(config: FHIRConfig) {
    this.client = new FHIRClient({
      baseUrl: config.ehrBaseUrl,
      auth: {
        type: 'oauth2',
        clientId: config.clientId,
        clientSecret: config.clientSecret,
        tokenUrl: config.tokenUrl
      }
    });
    this.baseUrl = config.ehrBaseUrl;
  }
  
  // Read patient data from EHR
  async getPatientData(patientId: string): Promise {
    const response = await this.client.request({
      url: `${this.baseUrl}/Patient/${patientId}`,
      method: 'GET',
      headers: {
        'Accept': 'application/fhir+json'
      }
    });
    
    return this.parseFHIRResource(response.data);
  }
  
  // Write PHQ-9 score to EHR as Observation
  async recordPHQ9Score(
    patientId: string,
    score: number,
    date: Date
  ): Promise {
    const observation: FHIRObservation = {
      resourceType: 'Observation',
      status: 'final',
      category: [{
        coding: [{
          system: 'http://terminology.hl7.org/CodeSystem/observation-category',
          code: 'survey',
          display: 'Survey'
        }]
      }],
      code: {
        coding: [{
          system: 'http://loinc.org',
          code: '44249-1',
          display: 'PHQ-9 total score'
        }]
      },
      subject: {
        reference: `Patient/${patientId}`
      },
      effectiveDateTime: date.toISOString(),
      valueInteger: score,
      interpretation: [{
        coding: [{
          system: 'http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation',
          code: this.getInterpretationCode(score)
        }]
      }],
      component: this.getPHQ9Components(score)
    };
    
    await this.client.create(observation);
  }
  
  // Create therapy session as Procedure
  async recordTherapySession(
    patientId: string,
    session: TherapySession
  ): Promise {
    const procedure: FHIRProcedure = {
      resourceType: 'Procedure',
      status: 'completed',
      category: {
        coding: [{
          system: 'http://snomed.info/sct',
          code: '276205009',
          display: 'Psychotherapy'
        }]
      },
      code: {
        coding: [{
          system: 'http://snomed.info/sct',
          code: this.getTherapyCode(session.type),
          display: this.getTherapyDisplay(session.type)
        }]
      },
      subject: {
        reference: `Patient/${patientId}`
      },
      performedPeriod: {
        start: session.startTime.toISOString(),
        end: session.endTime.toISOString()
      },
      note: [{
        text: this.generateSessionNote(session)
      }]
    };
    
    await this.client.create(procedure);
  }
  
  private getTherapyCode(type: string): string {
    const codes = {
      'CBT': '38678006',      // Cognitive behavioral therapy
      'DBT': '719984009',     // Dialectical behavior therapy
      'ACT': '710925007',     // Acceptance and commitment therapy
      'IPT': '76168009',      // Interpersonal psychotherapy
      'MBCT': '718077003'     // Mindfulness-based cognitive therapy
    };
    return codes[type] || '75516001'; // Generic psychotherapy
  }
}

7.2 Major EHR System Integration

The US healthcare market is dominated by a few major EHR vendors. Successful integration requires vendor-specific implementations alongside standard FHIR APIs.

EHR Vendor Integration

EHR System Market Share Integration Method Key Challenges
Epic 31% (largest) FHIR API + App Orchard marketplace Strict certification requirements, slow approval
Cerner (Oracle Health) 25% FHIR API + Cerner Code Console Multiple data models across versions
Meditech 16% FHIR API (newer versions), HL7 v2 (legacy) Wide variation in implementation maturity
Allscripts 7% FHIR API + Developer Portal Multiple EHR products with different APIs
athenahealth 5% athenaNet API (proprietary + FHIR) Cloud-only, specific auth requirements

Epic Integration Example

// Epic-specific integration via App Orchard
class EpicIntegration extends FHIRIntegrationService {
  private epicClient: EpicClient;
  
  async initialize(config: EpicConfig): Promise {
    // Epic uses SMART on FHIR for authorization
    const smartClient = await this.initializeSMART({
      clientId: config.clientId,
      redirectUri: config.redirectUri,
      scope: [
        'launch',
        'patient/Patient.read',
        'patient/Observation.read',
        'patient/Observation.write',
        'patient/Condition.read',
        'patient/Procedure.write',
        'patient/CarePlan.read',
        'patient/CarePlan.write'
      ].join(' ')
    });
    
    this.epicClient = new EpicClient(smartClient);
  }
  
  // Epic-specific: Create Questionnaire Response for PHQ-9
  async recordPHQ9Epic(
    patientId: string,
    responses: PHQ9Responses
  ): Promise {
    // Epic prefers QuestionnaireResponse over generic Observation
    const questionnaireResponse: FHIRQuestionnaireResponse = {
      resourceType: 'QuestionnaireResponse',
      status: 'completed',
      questionnaire: 'http://epic.com/fhir/Questionnaire/PHQ9',
      subject: {
        reference: `Patient/${patientId}`
      },
      authored: new Date().toISOString(),
      item: responses.answers.map((answer, index) => ({
        linkId: `PHQ9-${index + 1}`,
        text: this.getQuestionText(index + 1),
        answer: [{
          valueInteger: answer
        }]
      }))
    };
    
    await this.epicClient.create(questionnaireResponse);
    
    // Also create summary Observation
    await this.recordPHQ9Score(
      patientId,
      responses.totalScore,
      new Date()
    );
  }
  
  // Epic In Basket messaging for therapist communication
  async sendInBasketMessage(
    providerId: string,
    subject: string,
    body: string,
    priority: 'routine' | 'urgent'
  ): Promise {
    // Epic proprietary API for provider messaging
    await this.epicClient.sendMessage({
      recipient: {
        type: 'Provider',
        id: providerId
      },
      subject,
      body,
      priority,
      category: 'Clinical'
    });
  }
}

7.3 Data Synchronization Strategy

Bidirectional sync between digital therapy platform and EHR systems requires careful orchestration to avoid data conflicts, duplication, and compliance issues.

Sync Architecture

Data Synchronization Flow: ══════════════════════════════════ ┌─────────────────────┐ ┌──────────────────┐ │ Digital Therapy │◄───────►│ EHR System │ │ Platform │ FHIR │ (Epic, etc.) │ └──────────┬──────────┘ API └────────┬─────────┘ │ │ │ │ ▼ ▼ ┌────────────┐ ┌────────────┐ │ Local │ │ EHR │ │ Database │ │ Database │ └────────────┘ └────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Sync Orchestration Engine │ ├────────────────────────────────────────┤ │ • Conflict Resolution │ │ • Deduplication │ │ • Transformation & Mapping │ │ • Retry Logic │ │ • Audit Logging │ └────────────────────────────────────────┘ Sync Operations: 1. Patient demographics: EHR → Platform (read-only) 2. Assessment scores: Platform → EHR (write) 3. Session records: Platform → EHR (write) 4. Diagnoses: EHR → Platform (read) 5. Medications: EHR → Platform (read) 6. Care plans: Bidirectional (merge strategy)
class DataSyncOrchestrator {
  private syncLog: SyncLog;
  private conflictResolver: ConflictResolver;
  
  async syncPatientData(patientId: string): Promise {
    const results = [];
    
    try {
      // 1. Pull updates from EHR (read-only data)
      const ehrUpdates = await this.pullFromEHR(patientId, [
        'Patient',        // Demographics
        'Condition',      // Diagnoses
        'MedicationStatement'  // Current meds
      ]);
      
      for (const resource of ehrUpdates) {
        await this.updateLocalResource(resource);
        results.push({ resource: resource.resourceType, success: true });
      }
      
      // 2. Push updates to EHR (therapy data)
      const localUpdates = await this.getUnsyncedLocalData(patientId);
      
      for (const update of localUpdates) {
        try {
          const fhirResource = this.transformToFHIR(update);
          await this.pushToEHR(fhirResource);
          await this.markSynced(update.id);
          results.push({ 
            resource: update.type, 
            success: true 
          });
        } catch (error) {
          results.push({ 
            resource: update.type, 
            success: false, 
            error 
          });
          // Add to retry queue
          await this.addToRetryQueue(update, error);
        }
      }
      
      // 3. Handle bidirectional resources (CarePlan)
      await this.syncCarePlan(patientId);
      
      // 4. Log sync operation
      await this.syncLog.record({
        patientId,
        timestamp: new Date(),
        results,
        success: results.every(r => r.success)
      });
      
      return {
        success: results.every(r => r.success),
        results
      };
      
    } catch (error) {
      await this.handleSyncFailure(patientId, error);
      throw error;
    }
  }
  
  private async syncCarePlan(patientId: string): Promise {
    // Get both versions
    const [ehrPlan, localPlan] = await Promise.all([
      this.getEHRCarePlan(patientId),
      this.getLocalCarePlan(patientId)
    ]);
    
    // Check for conflicts
    const conflict = this.detectConflict(ehrPlan, localPlan);
    
    if (!conflict) {
      // No conflict: simple merge
      const merged = this.mergeCarePlans(ehrPlan, localPlan);
      await this.updateBothSystems(merged);
    } else {
      // Conflict: apply resolution strategy
      const resolved = await this.conflictResolver.resolve({
        ehr: ehrPlan,
        local: localPlan,
        strategy: 'most-recent-wins' // or 'manual-review'
      });
      
      await this.updateBothSystems(resolved);
      await this.notifyOfConflict(patientId, conflict);
    }
  }
}

7.4 Cross-Platform Data Portability

Patients should own their health data and be able to move it between systems. The WIA-MENTAL-001 standard mandates comprehensive data export capabilities.

Data Export Formats

Format Use Case Technical Spec Completeness
FHIR Bundle (JSON) Import to other EHR/digital health apps HL7 FHIR R4, complete resource set 100% clinical data
PDF Report Human-readable summary for patients/providers PDF/A (archival format) Summary + visualizations
CSV Data analysis, spreadsheet import RFC 4180, UTF-8 encoding Tabular data only
JSON API Format Developer access, custom integrations JSON API v1.0 specification 100% including metadata

Key Takeaways

Review Questions

  1. What is FHIR and how does it improve on earlier HL7 standards? Describe the key technical differences.
  2. Which FHIR resources are most relevant for mental health data exchange? Provide examples of how each is used.
  3. What are the integration challenges specific to Epic compared to other EHR vendors?
  4. How does the data sync orchestrator handle conflicts when the same resource is modified in both systems?
  5. Why is data portability important from both patient and regulatory perspectives?
  6. What compliance requirements apply when sharing data with EHR systems? How do Business Associate Agreements work?