How HL7 and FHIR Improve Interoperability in Correctional Healthcare Systems
Correctional facilities run their own version of a hospital system. Tighter security, higher patient turnover, almost no continuity of care between locations. An inmate can enter a county jail, get transferred to a state facility, then end up in a community hospital for a procedure the jail can’t handle. At each stop, someone has to answer the same question: what medications is this person on, what conditions do they have, what happened at the last facility. If the answer lives in three different systems that don’t talk to each other, the honest answer is often “we don’t know yet.”
Correctional healthcare software has to move data between EHRs, laboratories, pharmacies, hospitals, HIEs, telemedicine platforms, and billing systems. HL7 v2, CDA, and FHIR are among the main healthcare interoperability standards used for healthcare data exchange.
Why Healthcare Interoperability is Harder in Correctional Settings
A county jail patient can get transferred mid-treatment, sometimes with almost no documentation following them to the new site. Civilian hospital systems deal with fragmented records too. Correctional facilities layer on faster turnover, higher rates of chronic conditions and substance use disorders, and a legal duty of care that turns a missing chart into a liability issue, not just an inconvenience.
Vendor fragmentation makes this worse. A county jail might run one EHR, a state prison system another, the outside hospital a third. Lab and pharmacy vendors are frequently separate contracts entirely.
HL7 v2: the Old Workhorse
Since the 1980s, HL7 version 2 (HL7 v2) has been the messaging backbone of healthcare IT. It’s a pipe-delimited text format and, despite not being something anyone would design today, it’s everywhere: lab systems, pharmacy systems, and older EHRs still speak it by default.
A typical HL7 v2 message breaks down into a few segments:
- MSH: message header, sender, receiver, timestamp
- PID: patient identification
- OBR/OBX: order and observation results
In a correctional setting, HL7 v2 usually shows up for lab orders and results. A facility nurse orders a basic metabolic panel. The order goes out as an ORM message. The result comes back as an ORU message once the lab processes the sample. This works reasonably well when both systems already support v2, which most legacy lab and pharmacy systems do.
The limitation is flexibility. HL7 v2 messages are loosely specified, and every vendor implements its own dialect. Two systems that both claim v2 compliance can still fail to talk to each other cleanly without a mapping layer in between.
CDA: Documents Instead of Messages
Clinical Document Architecture (CDA) takes a different approach. It packages a full clinical document instead of small transactional messages as structured XML. A Continuity of Care Document (CCD) is a constrained type of CDA document commonly used to exchange a summary of care.
Let’s consider an example of correctional healthcare: a discharge from a hospital back to the facility, or a referral going out to a specialist. In this case, CDA earns its keep because the document can carry problem lists, medication lists, allergies, and recent procedures, all in one package. That matters when a clinician has ten minutes to review a new patient before shift change.
CDA documents are readable by humans and machines, part of why the format stuck around even as newer standards appeared. However, the document-centric approach is the main tradeoff of CDA. If you need to extract a specific value from CDA, additional parsing and mapping is required, in particular, when implementations differ in their use of optional sections and structured entries. That gap is part of what FHIR was built to close.
FHIR: Resource-Based Data Exchange
Fast Healthcare Interoperability Resources (FHIR) has changed the model once more. It aims to represent healthcare data as small, discrete resources: Patient, Observation, MedicationRequest, Encounter, and more. These resources are commonly exposed through RESTful healthcare APIs using JSON or XML (although the standard also supports documents, messages, transactions, subscriptions, and other exchange patterns).
For correctional care, this matters because FHIR supports selective queries and, when the connected systems expose current data, near-real-time access. For instance, a telemedicine provider connecting into a facility for a consult doesn’t need the whole chart. While a FHIR API can retrieve active medication orders, a complete medication-reconciliation workflow may also require administration, dispense, and reported-use data from other connected systems. If you want to achieve that same granularity cleanly out of HL7 v2 or CDA, it will take extra tooling most facilities don’t already have.
Integration Architecture
An interface engine (or integration platform) placed between the correctional EHR and external systems is a common architectural pattern in EHR integration. Such a layer handles much of the HL7 integration work:
- receive and route HL7 v2 messages from laboratories and pharmacies
- perform validations on required fields
- handle CDA documents from hospital referrals
- connect applications to a FHIR server or API layer for authorized queries
- manage acknowledgments or failed deliveries
- map identifiers and codes
This helps reduce the number of direct point-to-point connections each system must maintain. If you need to offer document exchange and FHIR integration, other components may be used: an API gateway may impose authentication and access policies, a master patient index may correlate records by distinct identifiers between facilities, and an FHIR server may store and expose resources.
How Standards Support Correctional Healthcare Workflows
Because different clinical workflows might have different needs, different exchange patterns could be applied. While HL7 v2 remains common for transactional integrations, CDA works well for complete clinical summaries; FHIR supports granular API access, and billing generally relies on a separate standard family.
These patterns often overlap. Let’s consider an outside hospital that might return a CDA discharge summary while exposing selected results through a FHIR API. For medication reconciliation, the system could apply a combination of orders from the correctional EHR, administration records from the facility, and dispense history from a pharmacy system. What’s the real challenge? To bring those sources together without treating any single resource as a complete clinical record.
The diagram below summarizes the most common use cases, what information is shared, and the most common integration pattern for each of them:
Security and Authentication
Exchanged data may be subject to restrictions imposed by law or specific regulations. For example, records maintained by a federally assisted substance use disorder program may fall under 42 CFR Part 2, while state laws may impose separate requirements for mental health and other sensitive information. Any integration architecture has to account for that.
Dev teams need to consider, at least, some basic security practices:
- Authentication and authorization: FHIR APIs should enforce both at the API gateway or FHIR server.
- Data transit: this should be done using TLS.
- Scoped access: both for users and applications. This can be done using OAuth 2.0 and SMART on FHIR
- System-to-system connections: they may use private-key authentication or mutual TLS.
- Access policies: they should follow least-privilege principles, be based on short-lived credentials, include secure key rotation, encryption at rest, and role-or attribute-based controls for sensitive records
A note on legacy connections: since HL7 v2 does not provide native authentication, they usually rely on secured network channels.
Correctional systems are often subject to legal discovery, so there must be a clear trail for every access to a patient record. You need to track who accessed or exchanged information, which records were involved, and whether the transaction succeeded. All the records should correlate activity across the API gateway, interface engine, FHIR server, and EHR. This way, administrators can reconstruct an exchange and investigate failed or unauthorized access.
Patient Matching: the Quiet, Hard Problem
Real correctional intake data is what breaks patient matching in ways a clean demo database never shows. Several reasons behind this: names get misspelled at intake; aliases are common. Date of birth fields sometimes get entered wrong under pressure during processing. If a person is booked under a nickname at one facility and their legal name at another, you’ll have a mismatch.
Having a wrong match means more than just a data-cleanup task. You could end up with two different people’s records being merged, or a returning patient’s history fails to link to their new intake. In both cases, this is a patient-safety problem.
One of the common techniques to prevent cases like the above is to combine deterministic rules with probabilistic or algorithmic matching across names, dates of birth, aliases, and other verified identifiers. This is often supported with a master patient index.
For instance, the following snippet uses weighted deterministic scoring in case a single exact identifier does not produce a reliable match:
function exactTextMatch(left, right) {
return Boolean(
left &&
right &&
left.trim().toLowerCase() === right.trim().toLowerCase()
);
}
function exactValueMatch(left, right) {
return Boolean(left && right && left === right);
}
function matchScore(candidate, incoming, weights = {}) {
const appliedWeights = {
lastName: 0.3,
firstName: 0.2,
dob: 0.35,
ssnLast4: 0.15,
...weights
};
let score = 0;
if (exactTextMatch(candidate.lastName, incoming.lastName)) {
score += appliedWeights.lastName;
}
if (exactTextMatch(candidate.firstName, incoming.firstName)) {
score += appliedWeights.firstName;
}
if (exactValueMatch(candidate.dob, incoming.dob)) {
score += appliedWeights.dob;
}
if (exactValueMatch(candidate.ssnLast4, incoming.ssnLast4)) {
score += appliedWeights.ssnLast4;
}
return score;
}
function findBestMatch(
candidates,
incoming,
threshold = 0.75,
minimumMargin = 0.15
) {
const ranked = candidates
.map(candidate => ({
candidate,
score: matchScore(candidate, incoming)
}))
.sort((a, b) => b.score - a.score);
const best = ranked[0];
const secondBest = ranked[1];
if (!best || best.score < threshold) {
return { status: "no_match" };
}
if (secondBest && best.score - secondBest.score < minimumMargin) {
return {
status: "manual_review",
candidates: [best, secondBest]
};
}
return {
status: "matched",
candidate: best.candidate,
score: best.score
};
}
Of course, several improvements should be made to this example before it reaches production. The implementation should normalize demographic data, account for aliases, missing values, and spelling variations, and validate its rules against local patient data. The purpose here is only to illustrate how the thresholds could be applied to automatically exclude candidates below them. If some candidate is closely ranked, it requires manual revision.
A Practical Example: Inmate Transfer with Lab Pending
Let’s imagine an inmate is transferred from a county jail to a state facility and a lab result is pending. The jail’s EHR sent the original order as an HL7 v2 ORM message. At the time of the transfer, the lab has not yet returned the ORU result.
If the correctional network maintains a shared master patient index and an up-to-date transfer registry, then the integration layer will be capable of detecting that the patient has moved. As soon as the ORU results are available, the system can map the identifiers used by the sending and receiving facilities, correlate the obtained results with the original order, and deliver such results to the authorized destination.
Of course, do not forget audits. On each of the steps of this workflow, acknowledgments should be preserved. The system should also prevent duplicate filings and provide clear definitions about which care team is responsible for reviewing and acting on critical results.
Code Example
Chudovo’s engineering team has been involved in the development of this kind of FHIR client logic. Based on their experience, the example below shows a simplified FHIR request that retrieves a patient’s active medication orders before a transfer is made. Of course, this does not represent a complete, reconciled list of everything the patient is currently taking.
async function getActiveMedicationOrders(patientId, fhirBaseUrl, accessToken) {
const response = await fetch(
`${fhirBaseUrl}/MedicationRequest?patient=${patientId}&status=active`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/fhir+json",
},
}
);
if (!response.ok) {
throw new Error(`FHIR request failed: ${response.status}`);
}
const bundle = await response.json();
return bundle.entry?.map((e) => e.resource) ?? [];
}
Wrap Up
Below are the main ideas you might take from this article:
- Some vendor systems still support only HL7 v2 and may not offer FHIR endpoints, so adoption remains uneven.
- On the other hand, while having an interface engine could provide better integration, it comes with operational complexity. Moreover, someone has to maintain the mapping rules as vendor formats shift.
- Patient-matching controls could help to reduce mismatches, not to eliminate them. A wrong match in a correctional setting carries real consequences, not just data cleanup work.
- Every integration point creates ongoing maintenance, not just a one-time build. Where budgets are limited, this also matters.
In addition to existing HL7 v2 and CDA, an FHIR-based exchange can be deployed to reduce the delay in obtaining the records, and it also provides clinicians with better information to reconcile the medications and a more traceable exchange process. That requires properly implemented identity matching, workflow ownership, and access controls; in a setting where missing information is not administrative friction but a patient-safety hazard, that tradeoff is worth making.