FHIR Data Model
The Med-SEAL Suite uses HL7 FHIR R4 as its canonical data standard. All clinical data is stored in Medplum’s FHIR store and accessed through its REST API. This page explains the key resources, naming conventions, and read/write patterns used across the platform.
Key FHIR Resources
Resource |
Used For |
|---|---|
|
Patient demographics and identifiers |
|
Clinician identity |
|
Clinical visits and interactions |
|
Diagnoses and problem list entries |
|
Prescriptions |
|
Dose confirmations (from app) |
|
Vitals, lab results, biometric readings |
|
Scheduled visits |
|
Documented allergies |
|
Vaccination records |
|
Procedures performed during encounters |
|
Structured clinical documents (e.g. pre-visit briefs) |
|
Population and individual adherence metrics |
|
PRO (Patient-Reported Outcome) submissions |
FHIR Domain Model (Class Diagram)
Reading FHIR Data
All reads go through the Medplum FHIR API. The base URL is configured via MEDPLUM_BASE_URL.
Using the Medplum TypeScript SDK
import { MedplumClient } from '@medplum/core';
const medplum = new MedplumClient({
baseUrl: process.env.MEDPLUM_BASE_URL,
});
await medplum.startClientLogin(
process.env.MEDPLUM_CLIENT_ID!,
process.env.MEDPLUM_CLIENT_SECRET!
);
// Search for a patient by ID
const patient = await medplum.readResource('Patient', patientId);
// Search for active medications
const bundle = await medplum.search('MedicationRequest', {
patient: `Patient/${patientId}`,
status: 'active',
});
Raw FHIR REST
# Get a patient
GET /fhir/R4/Patient/{id}
# Search for observations (vitals) for a patient
GET /fhir/R4/Observation?patient=Patient/{id}&category=vital-signs
# Search for active medications
GET /fhir/R4/MedicationRequest?patient=Patient/{id}&status=active
All requests require a Bearer token obtained via the Medplum OAuth2 flow.
Writing FHIR Data
Creating a Resource
const observation = await medplum.createResource({
resourceType: 'Observation',
status: 'final',
category: [
{
coding: [
{
system: 'http://terminology.hl7.org/CodeSystem/observation-category',
code: 'vital-signs',
},
],
},
],
code: {
coding: [
{
system: 'http://loinc.org',
code: '55284-4', // Blood pressure
display: 'Blood pressure systolic and diastolic',
},
],
},
subject: { reference: `Patient/${patientId}` },
effectiveDateTime: new Date().toISOString(),
component: [
{
code: { coding: [{ system: 'http://loinc.org', code: '8480-6', display: 'Systolic BP' }] },
valueQuantity: { value: 120, unit: 'mmHg', system: 'http://unitsofmeasure.org', code: 'mm[Hg]' },
},
{
code: { coding: [{ system: 'http://loinc.org', code: '8462-4', display: 'Diastolic BP' }] },
valueQuantity: { value: 80, unit: 'mmHg', system: 'http://unitsofmeasure.org', code: 'mm[Hg]' },
},
],
});
Updating a Resource
await medplum.updateResource({
...existingResource,
status: 'completed',
});
Coding Systems
Always use the correct terminology system for coded fields:
Concept |
System |
Example code |
|---|---|---|
Vital sign types |
|
|
Diagnoses |
|
|
Medications |
RXNORM or NZulm |
|
Observation categories |
FHIR category CS |
|
Units |
UCUM ( |
|
Med-SEAL Conventions
Patient reference format: always
Patient/{id}(never bare ID strings).Timestamps: always ISO 8601 with timezone (
2026-03-19T08:00:00+08:00).Dose confirmations: use
MedicationAdministrationwithstatus: 'completed'andrequestpointing to the originatingMedicationRequest.AI-generated content: tag
CompositionandObservationresources with the extensionhttp://medseal.io/fhir/StructureDefinition/ai-generated: trueso downstream systems can distinguish AI output from clinician-entered data.
FHIR Subscriptions
The AI Service subscribes to Medplum FHIR Subscriptions for real-time event processing:
Subscription |
Trigger |
Handler |
|---|---|---|
New |
Patient logs a reading |
Threshold check, nudge evaluation |
New |
PRO submission |
Score computation, escalation |
Updated |
Status change |
Pre-visit brief generation |
Subscriptions are defined in apps/ai-service/src/subscriptions/.