LCM Logo

Exchange standards

HL7 v2, HL7 v3, CDA/C-CDA, and FHIR - how clinical data moves between systems.

Sources last verified

These are the standards that move data between systems. They are optimized for messaging and documents, not for analysis: a decade of HL7 v2 messages is an audit trail, not a dataset. Turning one into the other is what the common data models are for.

Their history is unusually legible, because each generation was a reaction to the last. HL7 v2 was too loose, so v3 was made rigorous — and was so rigorous nobody implemented it. FHIR was the correction: web-native, pragmatic, and explicitly willing to leave 20% of cases to extensions.

HL7 v2

The pipe-delimited messaging standard that still carries the majority of clinical data movement inside hospitals.

  • Introduced: 1987 (HL7 founded); v2.1 in 1990. Current release is v2.9, but v2.3 and v2.5.1 are what you will actually meet
  • Purpose: Real-time event messaging between departmental systems — admissions, orders, results, transfers
  • Encoding: Plain text, segment-per-line, | fields, ^ components, ~ repeats
  • Body: Health Level Seven International
  • Status today: Overwhelmingly dominant for intra-hospital integration and not going anywhere. Estimates of coverage vary, but no serious observer disputes that v2 still moves more clinical messages per day than every other standard combined
  • Competing formats: FHIR is the designated successor and is winning at the API boundary, but has barely dented v2's position inside the hospital

Each message is a sequence of segments. MSH (header) is always first; the trigger event in MSH-9 tells you what kind of message it is (ADT^A01 = patient admission, ORU^R01 = lab result).

adt-a01.hl7
MSH|^~\&|EPIC|MEMORIAL|LABSYS|MEMORIAL|20260112143000||ADT^A01|MSG00001|P|2.5.1
EVN|A01|20260112143000
PID|1||10023847^^^MEMORIAL^MR||DOE^JANE^A||19850312|F|||123 MAIN ST^^BOSTON^MA^02115
PV1|1|I|ICU^301^A|||||1234567^SMITH^JOHN^^^^MD|||||||||||V0012345
oru-r01.hl7
MSH|^~\&|LABSYS|MEMORIAL|EPIC|MEMORIAL|20260112151500||ORU^R01|MSG00002|P|2.5.1
PID|1||10023847^^^MEMORIAL^MR||DOE^JANE^A||19850312|F
OBR|1|ORD9931|RES4471|24323-8^COMPREHENSIVE METABOLIC PANEL^LN|||20260112150000
OBX|1|NM|2345-7^Glucose^LN||142|mg/dL|70-99|H|||F|||20260112150000
OBX|2|NM|2160-0^Creatinine^LN||0.9|mg/dL|0.6-1.1|N|||F|||20260112150000

Note the LN in OBX-3 — that is a LOINC code. The exchange standard carries the terminology; it does not replace it.

The hard part of v2 is not parsing, it is that the standard is a negotiating framework rather than a specification. Z segments are locally defined, optional fields are used inconsistently, and every interface is its own dialect. Budget for a site-specific mapping document, always.

JavaPythonTypeScriptRustRJulia
LanguagePackageNotes
JavaHAPI HL7v2The reference implementation. Full message model for every version, plus MLLP transport. If correctness matters, this is the one
Pythonhl7apyFull message model with validation against the official schemas
Pythonhl7Lighter parsing plus an MLLP client. Good when you just need to read fields
TypeScriptsimple-hl7, hl7-standardParse and build; no schema validation
Rusthl7-parser, hl7-mllp-codecParsing and transport only. No message model
R—Nothing on CRAN. Parse in Python and hand over the table
Julia—Nothing in General
CLIMirth ConnectThe de facto interface engine. Note the open-source repo has been quiet since 2024 following the move to a commercial license — check the licensing before you build on it

HL7 v3 and the RIM

The attempt to put HL7 on a rigorous formal footing, via a single Reference Information Model from which all messages would be derived.

  • Introduced: Development from the mid-1990s; normative editions from 2005
  • Purpose: Eliminate v2's ambiguity with a model-driven, XML-based standard
  • Status today: Effectively abandoned as a messaging standard. Implementation cost was prohibitive and adoption never materialized outside a few national programs
  • What survived: Two things, and they matter. CDA, which is v3-derived and thriving, and the RIM's conceptual vocabulary, which shaped how the field talks about clinical data to this day

You will not build anything new on v3. It is here because you will encounter references to it and should know it is a dead end rather than a gap in your knowledge. Its failure is also the direct cause of FHIR's design philosophy.

CDA / C-CDA

The Clinical Document Architecture: an XML standard for documents — discharge summaries, care records, referral notes — rather than events.

  • Introduced: CDA R1 in 2000; CDA R2 in 2005; Consolidated CDA (C-CDA) R1.1 in 2012, R2.1 in 2015
  • Purpose: A persistent, human-readable, legally attestable clinical document that is also machine-parseable
  • Encoding: XML, derived from the HL7 v3 RIM
  • Status today: Entrenched in the US because Meaningful Use required it. The Continuity of Care Document (CCD) is the template you will meet most
  • Competing formats: FHIR Documents and the $docref operation are the successor, but C-CDA remains the required format for several certification criteria

The defining feature is the dual body: every section carries a human-readable narrative block and structured entries, and the two are required to agree. The narrative is what a clinician is legally attesting to; the entries are what your parser reads.

ccd-results-section.xml
<section>
  <templateId root="2.16.840.1.113883.10.20.22.2.3.1" extension="2015-08-01"/>
  <code code="30954-2" codeSystem="2.16.840.1.113883.6.1" displayName="Results"/>
  <title>Results</title>
  <!-- Human-readable narrative: what the clinician attests to -->
  <text>
    <table>
      <tbody>
        <tr><td>Glucose</td><td>142 mg/dL</td><td>High</td></tr>
      </tbody>
    </table>
  </text>
  <!-- Structured entry: what your parser reads. Must agree with the above. -->
  <entry>
    <observation classCode="OBS" moodCode="EVN">
      <templateId root="2.16.840.1.113883.10.20.22.4.2"/>
      <code code="2345-7" codeSystem="2.16.840.1.113883.6.1" displayName="Glucose"/>
      <statusCode code="completed"/>
      <effectiveTime value="20260112150000"/>
      <value xsi:type="PQ" value="142" unit="mg/dL"/>
      <interpretationCode code="H" codeSystem="2.16.840.1.113883.5.83"/>
    </observation>
  </entry>
</section>

Those templateId OIDs are the whole game. Conformance is defined by which templates a document claims, and validation means checking Schematron rules per template — not merely that the XML is well-formed. A document can be schema-valid and still wrong.

CLIJavaTypeScriptPythonRRustJulia

This is the weakest tooling story in the whole domain. There is no maintained, CDA-aware parsing library in any scripting language. In practice everyone writes XPath against the templates they care about.

LanguagePackageNotes
CLIxmllint, SaxonSchema validation, then Schematron for the template rules. This is the real workflow
JavaHL7 validatorValidates CDA as well as FHIR. MDHT, the old CDA-specific toolkit, is archived — do not start there
PythonlxmlGeneric XML with XPath. You supply the template knowledge
Rxml2As above
TypeScriptbluebuttonParses C-CDA/CCR to plain JSON. Long unmaintained, but still the shortest path to a quick read

Beware anything on PyPI or npm whose name merely looks like a CDA library. The PyPI package cda is "College Data Analysis" and has nothing to do with clinical documents.

FHIR

Fast Healthcare Interoperability Resources: the modern standard, and the only one being actively built on.

  • Introduced: Drafted by Graham Grieve in 2011; DSTU1 2014, STU3 2017, R4 2019 (first normative content), R4B 2022, R5 in March 2023. R6 is in ballot as of September 2026
  • Purpose: Web-native health data exchange — REST, JSON, OAuth — designed so that a competent web developer can be productive without a decade of HL7 background
  • Encoding: JSON (dominant), XML, or Turtle
  • Body: HL7 International
  • Status today: Mandated in the US by the 21st Century Cures Act. Every major EHR exposes a FHIR API. This is where new work goes
  • Competing formats: None with momentum. openEHR is the main alternative architecture and has real deployments in Europe, but FHIR has won the API layer

Build against R4, not R5. R5 is the current published release, but US regulation — USCDI, US Core, and the Cures Act certification criteria — is anchored to R4 (4.0.1). Real EHR endpoints serve R4. Use R5 only if you have a specific reason and control both ends.

Everything is a resource: a Patient, an Observation, a Condition. Each has a stable identity, a URL, and a version, and resources reference each other.

observation.json
{
  "resourceType": "Observation",
  "id": "glucose-20260112",
  "status": "final",
  "category": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/observation-category",
      "code": "laboratory"
    }]
  }],
  "code": {
    "coding": [{
      "system": "http://loinc.org",
      "code": "2345-7",
      "display": "Glucose [Mass/volume] in Serum or Plasma"
    }]
  },
  "subject": { "reference": "Patient/10023847" },
  "effectiveDateTime": "2026-01-12T15:00:00-05:00",
  "valueQuantity": {
    "value": 142,
    "unit": "mg/dL",
    "system": "http://unitsofmeasure.org",
    "code": "mg/dL"
  },
  "interpretation": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
      "code": "H",
      "display": "High"
    }]
  }]
}

The same glucose result, in all three standards above. That is the point worth internalizing: the LOINC code 2345-7 is constant across every one of them.

Profiles are where the real specification lives

Base FHIR is deliberately underspecified — roughly 80% of what everyone needs, with the rest left to profiles that constrain resources for a context. US Core is the profile set that US regulation actually requires; saying "we support FHIR" without naming a profile says almost nothing.

SMART on FHIR and Bulk FHIR

Two extensions you will need by name:

  • SMART on FHIR — the OAuth2/OpenID Connect layer that lets a third-party app launch inside an EHR with scoped access to one patient's chart. This is how EHR-embedded apps are built
  • Bulk FHIR (Flat FHIR) — the $export operation, returning newline-delimited JSON for a whole population rather than one patient at a time. This is the one that matters for research: it is the sanctioned route to a cohort-sized extract
bulk-export.txt
# Kick off an export; returns 202 with a Content-Location to poll
GET [base]/Group/[id]/$export?_type=Patient,Observation,Condition
Accept: application/fhir+json
Prefer: respond-async

# When complete, the status endpoint returns URLs to NDJSON files, one per resource type
JavaPythonTypeScriptCLIRustRJulia
LanguagePackageNotes
JavaHAPI FHIRThe reference implementation — client, server, validator, terminology. Every other library is measured against it
Pythonfhir.resourcesPydantic models for every resource in R4/R4B/R5. The default choice for validation and typed access
PythonfhirpyAsync REST client with a query builder
PythonfhirclientThe SMART on FHIR reference client
PythonfhirpathpyFHIRPath evaluation — the query language for navigating resources
PythonpathlingSpark-based analytics over FHIR at population scale. The right tool for Bulk FHIR output
RfhircrackrDownloads bundles and flattens them to data frames. Built for exactly the research use case; not a general-purpose client
TypeScript@medplum/coreFull-featured client from an actively developed FHIR platform
TypeScriptfhir-kit-client, fhirpath, @types/fhirClient, FHIRPath engine, and type definitions
Rustfhir-sdkModels and client for R4B/R5, actively maintained. Prefer it to fhirbolt, which has been quiet since 2023
Julia—Nothing in General. Call a FHIR server over plain HTTP and parse the JSON
CLIsushiCompiles FHIR Shorthand to an implementation guide. Standard for authoring profiles
CLIHL7 ValidatorThe official validator jar. Validates against profiles, not just structure
CLIInfernoONC's conformance test suite. What certification is measured with

USCDI

Not a format — a required content list. The United States Core Data for Interoperability specifies which data elements a certified EHR must be able to exchange, and US Core maps each one onto FHIR R4 profiles.

  • Introduced: v1 in 2020, replacing the older Common Clinical Data Set
  • Cadence: Annual. v7 was published on 23 July 2026, adding 31 data elements
  • Why it matters to you: It is the floor. If a data element is in USCDI, a certified EHR is obliged to expose it via FHIR — which makes USCDI the most reliable predictor of what you can actually get out of a system

Versions accumulate rather than replace, and certification criteria lag the newest version by years, so check which version your target system is certified against rather than assuming the latest.