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 | 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 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
}
}
The US healthcare market is dominated by a few major EHR vendors. Successful integration requires vendor-specific implementations alongside standard FHIR APIs.
| 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-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'
});
}
}
Bidirectional sync between digital therapy platform and EHR systems requires careful orchestration to avoid data conflicts, duplication, and compliance issues.
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);
}
}
}
Patients should own their health data and be able to move it between systems. The WIA-MENTAL-001 standard mandates comprehensive data export capabilities.
| 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 |