- Home
- Skills
- APIs & Backend
- hl7 fhir bridge
Works with the AI Agents you already use
hl7 fhir bridge
Convert HL7 v2 messages to FHIR R4 Bundles with deterministic mapping and regional profile validation.
Free
hl7 fhir bridge
Example session with this skill installed
I'm wiring Acme Lab's result feed into our patient-facing app, which consumes FHIR R4. Can you convert this ORU to FHIR and tell me if it's US Core-ready? I also need a short list of what to ask the lab to fix.
MSH|^~&|ACMELAB|ACME|PATAPP|CLINIC|20261014093000||ORU^R01^ORU_R01|ACME000417|P|2.5.1
PID|1||MRN-88231^^^CLINIC^MR||RIVERA^ANA^M||19870412|
OBR|1|ORD-5521^PATAPP|ACME-77310^ACMELAB|24357-6^URINALYSIS MACRO PANEL^LN|||20261014081500|||||||||||||||||F
OBX|1|NM|2350-7^GLUCOSE UR^LN||100|mg/dL|<15|H|||F|||20261014084000
OBX|2|CE|5778-6^COLOR OF URINE^LN||DKYEL^DARK YELLOW^99ACME||||||F|||20261014084000
NTE|1|L|Specimen received 35 min after collection
- Read your context and instructions
- Compiled the hl7 fhir bridge
Verdict: converted, but not US Core-ready. I validated against the us-core profile and got 1 error and 2 warnings. I also found two blocking problems the validator doesn't check: the DiagnosticReport has no status, and the timestamps have no time zone. Both are under "Not checked by the engine" below.
This is a translation and validation aid, not a medical device. These results will reach patients, so have someone check the mapped output before go-live.
Resources
| Resource | Key content |
|---|---|
| Patient | identifier MRN-88231 (system CLINIC), name (family + first given), birthDate 1987-04-12, no gender |
| Observation | 2350-7 GLUCOSE UR (LOINC), 100 mg/dL, reference range text <15, interpretation H (high), final, effective 2026-10-14T08:40:00 |
| Observation | 5778-6 COLOR OF URINE (LOINC), value DKYEL "DARK YELLOW" (no code system), final, effective 2026-10-14T08:40:00 |
| DiagnosticReport | 24357-6 URINALYSIS MACRO PANEL (LOINC), category LAB, links to both Observations and the Patient. No status, no effective/issued time, no identifier |
Observation.category = laboratory is a fixed value the ORU map always sets, because HL7 v2 has no field for it. It isn't a guess.
Issues (from the engine)
| Severity | Code | Location | What it means | How to fix |
|---|---|---|---|---|
| error | PROFILE_REQUIRED | Patient.gender | US Core Patient requires gender. PID-8 (the patient's administrative sex) is empty in the message. | The lab populates PID-8. Don't fill the gap from the M in PID-5. That's the middle-name component (RIVERA^ANA^M), not sex. |
| warning | CODING_NO_SYSTEM | Observation.valueCodeableConcept.coding[0] | The urine-color answer DKYEL has no system URI. OBX-5.3 is 99ACME, a lab-local code system, so nothing downstream can tell what the code means. | The lab adds a standard code (LOINC answer or SNOMED CT) in OBX-5.4–5.6 and/or gives us the canonical URI for 99ACME. We then register that URI in the ORU map. We don't guess it. |
| warning | UNMAPPED_SEGMENT | NTE | The ORU map doesn't read NTE (comment) segments, so the note "Specimen received 35 min after collection" is not in the Bundle. | Map side: NTE → Observation.note is still listed as pending in the ORU map. Until it's added, your app won't show this note. |
Not checked by the engine (my observations)
I confirmed each of these by parsing the message or reading the Bundle. They're my own findings, separate from the deterministic list above.
- The DiagnosticReport has no
status, which makes it invalid FHIR R4. The OBR segment has only 24 fields, so theFlanded in OBR-24 (diagnostic service section) instead of OBR-25 (result status). The|before it is one short.statusis mandatory (1..1) in base FHIR R4 and in US Core. I re-ran a synthetic message withFin OBR-25 and it mapped tostatus: final.
Timestamps have no time zone. OBX-14 20261014084000 becomes 2026-10-14T08:40:00. FHIR R4 doesn't allow a dateTime with a time but no offset, so strict FHIR parsers will reject it. MSH-7 and OBR-7 have the same problem. I checked that an offset sent by the lab (e.g. …084000-0500) is kept in the output.
3.
The DiagnosticReport has no effective/issued time. OBR-7 (collection time) is in the message, but the ORU map doesn't map OBR-7 or OBR-22 yet (documented as pending). OBR-22 (report time) is also empty in the message. US Core's lab DiagnosticReport expects an effective time. This is a gap in the map, not a lab error.
4.
The order and report IDs aren't carried over. OBR-2 (placer ORD-5521) and OBR-3 (filler ACME-77310) don't reach DiagnosticReport.identifier or basedOn. Without them, a later corrected result can't be matched to this one, and your app would show duplicates. Map side.
5.
Patient.identifier.system is "CLINIC", not a URI. The map copies PID-3.4 (assigning authority) as-is. This is a known, deliberate TODO(mapeo) gap in the ORU map. It passes the engine's US Core rule because system and value are both present, but it isn't a resolvable namespace. This is your own MRN echoed back, so you decide the URI, not the lab.
6.
The NTE probably sits in the wrong place. It comes right after OBX 2, so under HL7 rules it's a note on the urine-color result. The text reads like a note about the whole specimen.
7. Minor gaps:
- The middle initial (PID-5.3) isn't mapped.
valueQuantitycarries only themg/dLtext, with no UCUMsystem/code. It stays that way even if OBX-6 says^^UCUM, which I checked. It's fine for display, but the map needs it if the app compares or converts units.
The engine checks structure and must-support elements for Patient, Observation and codings. It doesn't check full IG conformance or terminology bindings. Before go-live, run the official HL7 FHIR Validator with US Core as a second pass.
What to ask Acme Lab to fix
- Populate PID-8 (administrative sex). This clears the only US Core error.
Put the result status in OBR-25. Add the missing | before F. Right now it lands in OBR-24 and the report has no status.
3. Send timestamps with a UTC offset in MSH-7, OBR-7 and OBX-14, e.g. 20261014084000-0500.
4.
Code coded results with a standard system. Send a LOINC answer or SNOMED CT code in OBX-5.4–5.6 with coding system LN or SCT, and/or tell us the canonical URI of 99ACME.
5. Two smaller items:
- Populate OBR-22 (report time).
- Check where the NTE belongs. If the note is about the whole specimen, they should send it right after OBR instead of after the urine-color OBX.
Bundle (exactly as produced)
{
"resourceType": "Bundle",
"type": "collection",
"entry": [
{
"fullUrl": "urn:uuid:4d76e1a9-53d4-42ad-94e1-04d8cd13997e",
"resource": {
"resourceType": "Patient",
"identifier": [
{
"value": "MRN-88231",
"system": "CLINIC"
}
],
"name": [
{
"family": "RIVERA",
"given": [
"ANA"
]
}
],
"birthDate": "1987-04-12"
}
},
{
"fullUrl": "urn:uuid:386051ae-c102-4c64-8ccc-da62ab40eca6",
"resource": {
"resourceType": "Observation",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "laboratory"
}
]
}
],
"status": "final",
"code": {
"coding": [
{
"code": "2350-7",
"display": "GLUCOSE UR",
"system": "http://loinc.org"
}
]
},
"valueQuantity": {
"value": 100,
"unit": "mg/dL"
},
"interpretation": [
{
"coding": [
{
"code": "H",
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
}
]
}
],
"referenceRange": [
{
"text": "<15"
}
],
"effectiveDateTime": "2026-10-14T08:40:00",
"subject": {
"reference": "urn:uuid:4d76e1a9-53d4-42ad-94e1-04d8cd13997e"
}
}
},
{
"fullUrl": "urn:uuid:f306779d-0e58-4e43-acd6-c051ed13b5c3",
"resource": {
"resourceType": "Observation",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "laboratory"
}
]
}
],
"status": "final",
"code": {
"coding": [
{
"code": "5778-6",
"display": "COLOR OF URINE",
"system": "http://loinc.org"
}
]
},
"valueCodeableConcept": {
"coding": [
{
"code": "DKYEL",
"display": "DARK YELLOW"
}
]
},
"effectiveDateTime": "2026-10-14T08:40:00",
"subject": {
"reference": "urn:uuid:4d76e1a9-53d4-42ad-94e1-04d8cd13997e"
}
}
},
{
"fullUrl": "urn:uuid:7e621ab3-2718-4b83-9759-7010e94d71dc",
"resource": {
"resourceType": "DiagnosticReport",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0074",
"code": "LAB"
}
]
}
],
"code": {
"coding": [
{
"code": "24357-6",
"display": "URINALYSIS MACRO PANEL",
"system": "http://loinc.org"
}
]
},
"result": [
{
"reference": "urn:uuid:386051ae-c102-4c64-8ccc-da62ab40eca6"
},
{
"reference": "urn:uuid:f306779d-0e58-4e43-acd6-c051ed13b5c3"
}
],
"subject": {
"reference": "urn:uuid:4d76e1a9-53d4-42ad-94e1-04d8cd13997e"
}
}
}
]
}
Next steps
- Send the lab list above to Acme. Items 1–3 block US Core and valid R4. Item 4 removes the coding warning.
- Extend the ORU map on our side:
- Map OBR-7 →
DiagnosticReport.effectiveDateTime, OBR-22 →issued, and OBR-2/OBR-3 →identifier. - Map NTE →
Observation.note. - Register the
99ACMEURI once Acme provides it. - Decide the URI for the
CLINICMRN namespace.
- Map OBR-7 →
Re-test with a corrected sample. Re-run map --profile us-core on a corrected sample from Acme, then run the official HL7 FHIR Validator (US Core) as a second pass before go-live.
Connects securely to your tools. The creator never sees your data.
About this skill
Handling HL7 v2 to FHIR R4 translation is notoriously error-prone, often resulting in silent failures or non-compliant JSON that breaks downstream systems. This skill integrates a deterministic offline engine that maps, validates, and explains interoperability issues with surgical precision.
What it does
- Deterministic mapping translates HL7 v2 messages (ADT, ORU, ORM, OUL) into FHIR R4 Bundles using declarative YAML maps.
- Localized validation checks outputs against US Core, CL Core (Chile), or CO Core (Colombia) profiles.
- Segment-level error reporting identifies the exact location of failures in both the source HL7 segment and the target FHIRPath.
- Message parsing breaks down MSH, PID, OBR, and OBX segments to inspect message structure without full conversion.
- Issue explanation interprets external logs or validation errors to provide actionable remediation steps.
How it works
- Command selection involves choosing
mapfor full conversion,validatefor existing JSON/HL7 checks, orparsefor structural inspection. - Execution runs the
hl7bridge.mjsengine via a Node.js environment, piping the message through stdin or a file path. - Review provides a structured verdict including a resource summary table, a detailed issues list, and the final FHIR Bundle.
Frameworks & tools
Built for Node.js 20+ environments. It utilizes a deterministic mapping engine with YAML-based transformation rules and a built-in validator for FHIR R4 and regional Implementation Guides.
Why this beats prompting it yourself
Generic LLMs often hallucinate FHIR mappings or invent missing values to satisfy schema requirements, creating dangerous data integrity risks. This skill uses a hard-coded engine that refuses to guess, ensuring that every resource produced is traceable, auditable, and production-ready.
Use cases
- Convert lab results (ORU^R01) into FHIR Observation resources for a patient portal.
- Audit a hospital admission feed to ensure it meets US Core requirements.
- Debug why a specific segment is causing a FHIR server to reject an incoming Bundle.
- Localize interface mappings for the Chilean or Colombian healthcare markets.
Known limitations
Supports only ADT^A01, ORU^R01, ORM^O01, and OUL^R22 to FHIR R4. Does not handle CDA documents, X12 claims, or FHIR-to-HL7 conversion. Offline validation does not include full terminology binding.
How to use
Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill passes our trust checks below.
Security scanned
Passed our 8-point scan before listing
1 install
Downloaded by developers to date
Free forever
No account required to browse
Trust & safety
Security scanned
Verified clean 4 days ago
- Free to download with an account
Requires