# HL7 data format

> The `hl7` data format parses HL7 v2 messages (ER7, the pipe-delimited text) into a structured value with the `CamelHL7*` headers, and encodes such values back to ER7 text.

**Name:** `hl7` · **Crate:** `cameleer-protocols` · **Cargo feature:** [`protocols`](../guides/build-features.md) ·
**Apache Camel:** [HL7](https://camel.apache.org/components/next/dataformats/hl7-dataformat.html)

## When to use it

- Integrate hospital systems: admissions (ADT), orders (ORM), results (ORU) and scheduling
  (SIU) feeds arrive as HL7 v2 over MLLP.
- Route messages by type and trigger event (`ADT^A01` to one system, `ORU^R01` to another).
- Turn lab results into JSON for an analytics pipeline or an assistant that summarizes them
  for clinicians.

## Marshal and unmarshal

| direction | input body | output body |
|---|---|---|
| unmarshal | ER7 text (segments separated by CR, LF or CRLF) | an object: `messageType`, `triggerEvent`, `messageControlId`, `versionId` and `segments` (each `{name, fields}`); sets the `CamelHL7*` headers |
| marshal | such an object, or ER7 text | ER7 text with CR segment separators (text bodies are parsed first, so invalid messages fail) |

Field values are kept as raw ER7 strings (components joined with `^`). To read single fields
or components use the [hl7terser language](../languages/hl7terser.md) (`PID-5-1`,
`OBX(1)-5`), which works on the ER7 text as well as on the unmarshalled value.

The data format is registered in the registry as `hl7` by `cameleer-protocols` (included in
`cameleer_main::new_main()` and the container image). It has no options.

## Example

Unmarshal an admission message and route on the headers.

<!-- example: hl7-unmarshal-route -->
```yaml
- route:
    id: hl7-adt
    from:
      uri: direct:hl7
      steps:
        - unmarshal:
            hl7: {}
        - choice:
            when:
              - simple: "${header.CamelHL7MessageType} == 'ADT' && ${header.CamelHL7TriggerEvent} == 'A01'"
                steps:
                  - setBody:
                      simple: "admit ${header.CamelHL7MessageControl} from ${header.CamelHL7SendingApplication}: ${body.segments[2].fields[4]}"
            otherwise:
              steps:
                - setBody:
                    simple: "ignored ${header.CamelHL7MessageType}^${header.CamelHL7TriggerEvent}"
```

<!-- input: hl7-unmarshal-route -->
```text
MSH|^~\&|ADMIT|WARD7|EHR|HOSP|20261105120000||ADT^A01^ADT_A01|MSG00042|P|2.5
EVN|A01|20261105120000
PID|1||12345^^^MRN||Doe^Jane||19800101|F
```

<!-- output: hl7-unmarshal-route -->
```text
admit MSG00042 from ADMIT: Doe^Jane
```

Marshal builds ER7 from the structured value, for example a result produced by an AI
summarizer that must go back to the EHR as an `ORU^R01`. MSH field 1 is the field separator
itself, as in HL7. The route replaces the CR segment separators with ` / ` only so the
result fits on one line here.

<!-- example: hl7-marshal -->
```yaml
- route:
    id: hl7-marshal
    from:
      uri: direct:hl7-marshal
      steps:
        - unmarshal:
            json: {}
        - marshal:
            hl7: {}
        - setBody:
            simple: '${body.replace("\r", " / ")}'
```

<!-- input: hl7-marshal -->
```json
{"segments": [
  {"name": "MSH", "fields": ["|", "^~\\&", "SUMMARIZER", "AI", "EHR", "HOSP", "20261105120000", "", "ORU^R01", "MSG8", "P", "2.5"]},
  {"name": "PID", "fields": ["1", "", "12345^^^MRN", "", "Doe^Jane"]},
  {"name": "OBX", "fields": ["1", "TX", "SUMMARY", "", "Glucose within normal range"]}
]}
```

<!-- output: hl7-marshal -->
```text
MSH|^~\&|SUMMARIZER|AI|EHR|HOSP|20261105120000||ORU^R01|MSG8|P|2.5 / PID|1||12345^^^MRN||Doe^Jane / OBX|1|TX|SUMMARY||Glucose within normal range /
```

## Rust DSL

```rust
b.from("direct:hl7-rust")
    .unmarshal(DataFormatDefinition::named("hl7"))
    .set_body(simple("${header.CamelHL7MessageType}^${header.CamelHL7TriggerEvent}"));
```

## Message headers

Set by unmarshal from the MSH segment:

| header | MSH field |
|---|---|
| `CamelHL7SendingApplication` | MSH-3 |
| `CamelHL7SendingFacility` | MSH-4 |
| `CamelHL7ReceivingApplication` | MSH-5 |
| `CamelHL7ReceivingFacility` | MSH-6 |
| `CamelHL7Timestamp` | MSH-7 |
| `CamelHL7Security` | MSH-8 |
| `CamelHL7MessageType` | MSH-9-1 |
| `CamelHL7TriggerEvent` | MSH-9-2 |
| `CamelHL7MessageControl` | MSH-10 |
| `CamelHL7ProcessingId` | MSH-11 |
| `CamelHL7VersionId` | MSH-12 |
| `CamelHL7Charset` | MSH-18 |

## Differences from Apache Camel

Camel's `hl7` data format produces HAPI `Message` objects with typed segment classes per HL7
version. Here the value is a generic structure (segments and raw fields) and there is no
version-specific validation; use terser paths to read fields. Acknowledgements (`AA`, `AE`)
are generated by the [MLLP component](../components/mllp.md) (`autoAck`).

## Common questions

### How do I receive HL7 messages over MLLP?

Use `from: mllp:0.0.0.0:2575` and `unmarshal: hl7: {}`; the MLLP consumer handles framing and
sends the ACK. See the [MLLP component](../components/mllp.md).

### How do I get the patient name from an HL7 message?

`hl7terser: PID-5` gives the whole name field (`Doe^Jane`), `PID-5-1` the family name and
`PID-5-2` the given name.

### Why do my segments run together?

HL7 separates segments with a carriage return (`\r`). Unmarshal also accepts LF and CRLF;
marshal always writes CR, which some viewers show as one line.

## Related

- [hl7terser language](../languages/hl7terser.md)
- [MLLP component](../components/mllp.md)
- [FHIR data format](fhir.md): the newer HL7 standard
- [Apache Camel HL7 data format](https://camel.apache.org/components/next/dataformats/hl7-dataformat.html)

## Found a problem?

If something on this page doesn't match what Cameleer does, or you hit a bug while building
with it, please help fix it, whether you are a person or a coding agent:
[open an issue](https://github.com/pmsudhi/cameleer/issues/new) that describes what you saw,
then fork the repository, fix it, add or update a test, run `cargo test --workspace`, and open
a pull request. [CONTRIBUTING.md](https://github.com/pmsudhi/cameleer/blob/main/CONTRIBUTING.md)
explains the ground rules.
