Longitudinal Patient Case Tracking
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.
The key relationships are:
- Encounters link to their episode via
Encounter.episodeOfCare - Both EpisodeOfCare and CarePlan reference the same
Conditionresources, creating an implicit link through shared diagnoses CarePlan.supportingInfoexplicitly references the EpisodeOfCare, providing a direct link from plan to episodeGoalresources are referenced from the CarePlan viaCarePlan.goal
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);
CarePlan.supportingInfo accepts Reference<Resource>[], making it flexible enough to reference an EpisodeOfCare, related documents, or any other supporting resource.
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:
CarePlan.supportingInfodirectly references the EpisodeOfCare. This gives you an explicit, unambiguous pointer when reading a CarePlan — followsupportingInfoto find the episode it belongs to.- Both resources reference the same
Conditionresources (EpisodeOfCare.diagnosis.conditionandCarePlan.addresses). This shared condition serves as the queryable link in the reverse direction: starting from an EpisodeOfCare, read its conditions and then searchCarePlan?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.
- TypeScript
- CLI
- cURL
const encounters = await medplum.searchResources('Encounter', 'episode-of-care=EpisodeOfCare/diabetes-episode');
console.log(encounters);
medplum get 'Encounter?episode-of-care=EpisodeOfCare/diabetes-episode'
curl 'https://api.medplum.com/fhir/R4/Encounter?episode-of-care=EpisodeOfCare/diabetes-episode' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
Finding Care Plans for a Condition
Find all care plans addressing a specific condition for a patient.
- TypeScript
- CLI
- cURL
const carePlans = await medplum.searchResources(
'CarePlan',
'patient=Patient/homer-simpson&condition=Condition/diabetes-type-2'
);
console.log(carePlans);
medplum get 'CarePlan?patient=Patient/homer-simpson&condition=Condition/diabetes-type-2'
curl 'https://api.medplum.com/fhir/R4/CarePlan?patient=Patient/homer-simpson&condition=Condition/diabetes-type-2' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
Finding All Episodes for a Patient
List all active care episodes for a patient to see their ongoing cases at a glance.
- TypeScript
- CLI
- cURL
const episodes = await medplum.searchResources('EpisodeOfCare', 'patient=Patient/homer-simpson&status=active');
console.log(episodes);
medplum get 'EpisodeOfCare?patient=Patient/homer-simpson&status=active'
curl 'https://api.medplum.com/fhir/R4/EpisodeOfCare?patient=Patient/homer-simpson&status=active' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
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.
All resources reference Patient: Homer Simpson. Those references are omitted below to keep the diagram focused.
In this setup:
- Each concern has its own EpisodeOfCare, Condition, CarePlan, and Goal
- The Q2 check-up Encounter is linked to both episodes because both concerns were addressed during that visit
- To get the full picture of Homer's diabetes care, query
Encounter?episode-of-care=EpisodeOfCare/diabetes-episodefor all visits, andCarePlan?condition=Condition/diabetes-type-2for the treatment plan - To see all of Homer's active cases, query
EpisodeOfCare?patient=Patient/homer-simpson&status=active
See Also
EpisodeOfCareFHIR resource APICarePlanFHIR resource APIEncounterFHIR resource APIConditionFHIR resource APIGoalFHIR resource API- Care Plans
- Using Tasks to Manage Clinical Workflow
- Creating SOAP Notes