Longitudinal Patient Case Tracking | Medplum

Many clinical scenarios span multiple visits over weeks, months, or years. A patient managing type 2 diabetes, for example, will have quarterly check-ups, lab orders, specialist referrals, and lifestyle interventions that all belong to the same ongoing case. A single Encounter captures one visit, but it cannot represent the full arc of care.

FHIR provides two complementary resources for grouping this longitudinal activity: EpisodeOfCare for administrative encounter grouping, and CarePlan for clinical planning. Together, they give you both the temporal grouping you need and the rich goal and activity context you want.

Key Concepts

EpisodeOfCare is intentionally lightweight. Its job is purely administrative grouping — it acts as an umbrella that encounters attach to. It tracks which organization is responsible for a patient's care during a period, and which conditions are being addressed. It does not capture clinical planning details like goals, activities, or care team assignments.

CarePlan handles the clinical layer. It defines treatment goals, scheduled activities, conditions being addressed, and care team members. A CarePlan sits alongside an EpisodeOfCare and provides the planning context that the episode intentionally omits.

A patient can have multiple concurrent episodes and care plans. For instance, one episode for diabetes management and another for a weight loss program, each with its own CarePlan. A single encounter (like a quarterly check-up) can be linked to multiple episodes through Encounter.episodeOfCare, which accepts an array of references.

Resource Overview

Resource Role Description
EpisodeOfCare Administrative grouping Groups encounters into a named care episode for a specific condition
CarePlan Clinical protocol Defines goals, activities, and conditions for the treatment plan
Encounter Visit record Individual visit linked to one or more episodes
Condition Problem / diagnosis The clinical problem being tracked across the episode
Goal Treatment objective Measurable target within a care plan

How the Resources Fit Together

All resources below reference the Patient. The diagram omits those references to focus on the relationships that matter for longitudinal tracking.

episodeOfCare

episodeOfCare diagnosis.condition addresses goal supportingInfo Encounter (Visit 1) EpisodeOfCare Encounter (Visit 2) Condition CarePlan Goal

The key relationships are:

Navigating between EpisodeOfCare and CarePlan

The supportingInfo link is one-directional: you can follow it from CarePlan to EpisodeOfCare, but there is no field on EpisodeOfCare that points back to a CarePlan. To navigate in the reverse direction (episode to plan), query by the shared Condition: CarePlan?condition=Condition/{id}. This is the natural clinical query — "show me all plans addressing this diagnosis" — and works regardless of how many episodes or plans reference that condition.

Creating an Episode of Care

An EpisodeOfCare represents an administrative period of care for a specific concern. Create one when a patient begins a new care episode, such as starting chronic disease management or entering a treatment program.

Key fields:

Field Purpose
status Lifecycle state: planned, waitlist, active, onhold, finished, cancelled
patient Reference to the patient
type Category of care being provided
diagnosis Conditions being addressed, with optional role (chief complaint, comorbidity)
period Start and optional end date of the episode
managingOrganization Organization responsible for the episode
const episode = await medplum.createResource({

resourceType: 'EpisodeOfCare',

status: 'active',

patient: {

reference: 'Patient/homer-simpson',

display: 'Homer Simpson',

},

type: [\

{\

coding: [\

{\

system: 'http://terminology.hl7.org/CodeSystem/episodeofcare-type',\

code: 'hacc',\

display: 'Home and Community Care',\

},\

],\

},\

],

diagnosis: [\

{\

condition: {\

reference: 'Condition/diabetes-type-2',\

display: 'Type 2 Diabetes Mellitus',\

},\

role: {\

coding: [\

{\

system: 'http://terminology.hl7.org/CodeSystem/diagnosis-role',\

code: 'CC',\

display: 'Chief complaint',\

},\

],\

},\

},\

],

period: {

start: '2024-01-15',

},

managingOrganization: {

reference: 'Organization/springfield-clinic',

display: 'Springfield Medical Clinic',

},

});

console.log(episode);

Linking Encounters to an Episode

When a patient has a visit related to an ongoing care episode, link the Encounter to the episode via Encounter.episodeOfCare. This field is an array, so a single encounter can belong to multiple episodes simultaneously. This is useful when a visit addresses more than one ongoing concern.

const encounter = await medplum.createResource({

resourceType: 'Encounter',

status: 'finished',

class: {

system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode',

code: 'AMB',

display: 'ambulatory',

},

subject: {

reference: 'Patient/homer-simpson',

display: 'Homer Simpson',

},

episodeOfCare: [{ reference: 'EpisodeOfCare/diabetes-episode' }, { reference: 'EpisodeOfCare/weight-loss-episode' }],

period: {

start: '2024-06-01T09:00:00Z',

end: '2024-06-01T09:30:00Z',

},

});

console.log(encounter);

In this example, the encounter is linked to both a diabetes episode and a weight loss episode, reflecting that both concerns were addressed during the same visit.

You are not limited to encounters that already took place. Link future visits to an episode by creating encounters in advance and pointing Encounter.episodeOfCare at the EpisodeOfCare. Use Encounter.status (for example planned) to record that the encounter is scheduled or not yet started, in line with FHIR’s encounter lifecycle.

Creating a Care Plan with Goals

While the EpisodeOfCare groups encounters, the CarePlan captures what should happen clinically. Start by creating Goal resources for the treatment objectives, then reference them from the CarePlan.

Creating a Goal

const goal = await medplum.createResource({

resourceType: 'Goal',

lifecycleStatus: 'active',

description: {

text: 'Maintain HbA1c below 7%',

},

subject: {

reference: 'Patient/homer-simpson',

display: 'Homer Simpson',

},

target: [\

{\

measure: {\

coding: [\

{\

system: 'http://loinc.org',\

code: '4548-4',\

display: 'Hemoglobin A1c/Hemoglobin.total in Blood',\

},\

],\

},\

detailQuantity: {\

value: 7,\

unit: '%',\

system: 'http://unitsofmeasure.org',\

code: '%',\

},\

},\

],

});

console.log(goal);

Creating the Care Plan

The CarePlan brings together the condition being addressed, the goals to achieve, scheduled activities, and a reference to the associated EpisodeOfCare via supportingInfo.

const carePlan = await medplum.createResource({

resourceType: 'CarePlan',

status: 'active',

intent: 'plan',

title: 'Diabetes Management Plan',

subject: {

reference: 'Patient/homer-simpson',

display: 'Homer Simpson',

},

addresses: [\

{\

reference: 'Condition/diabetes-type-2',\

display: 'Type 2 Diabetes Mellitus',\

},\

],

goal: [\

{\

reference: 'Goal/hba1c-goal',\

display: 'Maintain HbA1c below 7%',\

},\

],

activity: [\

{\

detail: {\

kind: 'ServiceRequest',\

code: {\

coding: [\

{\

system: 'http://loinc.org',\

code: '4548-4',\

display: 'Hemoglobin A1c',\

},\

],\

},\

description: 'Quarterly HbA1c lab test',\

status: 'scheduled',\

scheduledPeriod: {\

start: '2024-07-01',\

},\

},\

},\

],

supportingInfo: [\

{\

reference: 'EpisodeOfCare/diabetes-episode',\

display: 'Diabetes Management Episode',\

},\

],

});

console.log(carePlan);

Cross-Linking EpisodeOfCare and CarePlan

FHIR R4 does not provide a direct field on EpisodeOfCare that points to a CarePlan, so the link between them is asymmetric. The recommended pattern uses two complementary strategies:

  1. CarePlan.supportingInfo directly references the EpisodeOfCare. This gives you an explicit, unambiguous pointer when reading a CarePlan — follow supportingInfo to find the episode it belongs to.
  2. Both resources reference the same Condition resources (EpisodeOfCare.diagnosis.condition and CarePlan.addresses). This shared condition serves as the queryable link in the reverse direction: starting from an EpisodeOfCare, read its conditions and then search CarePlan?condition=Condition/{id} to find the associated plans.
Direction How to navigate
CarePlan → EpisodeOfCare Follow CarePlan.supportingInfo directly
EpisodeOfCare → CarePlan Query CarePlan?condition={shared-condition-id}

Together, these two strategies cover both navigation directions. The supportingInfo pointer is explicit and fast for single-resource reads; the shared Condition query handles the reverse direction and is the natural clinical question ("what plans address this diagnosis?").

Querying Longitudinal Data

Finding All Encounters for an Episode

Retrieve every visit associated with a care episode using the episode-of-care search parameter on Encounter.

const encounters = await medplum.searchResources('Encounter', 'episode-of-care=EpisodeOfCare/diabetes-episode');

console.log(encounters);

Finding Care Plans for a Condition

Find all care plans addressing a specific condition for a patient.

const carePlans = await medplum.searchResources(

'CarePlan',

'patient=Patient/homer-simpson&condition=Condition/diabetes-type-2'
);

console.log(carePlans);

Finding All Episodes for a Patient

List all active care episodes for a patient to see their ongoing cases at a glance.

const episodes = await medplum.searchResources('EpisodeOfCare', 'patient=Patient/homer-simpson&status=active');

console.log(episodes);

Example: Concurrent Care Episodes

Consider a patient, Homer Simpson, who is managing two ongoing concerns: type 2 diabetes and a weight loss program. Each concern has its own EpisodeOfCare and CarePlan, but some visits address both.