# SonoScribe — HL7 v2 Interface Specification **Version:** 2.5.1 · **Date:** 2026-07-31 **Transport:** MLLP over TCP · **Direction:** outbound `ORU^R01` to your RIS; inbound `ORM^O01` / `OMI^O23` orders accepted Every message shape below is produced by the running code, not written by hand. The samples are literal output. --- ## 1. Why the report goes to the RIS at all For ultrasound the report usually lives in the **RIS**, not the PACS. A DICOM SR makes the report visible in the image viewer (see the DICOM Conformance Statement); the HL7 ORU is what puts it where the referring clinician, the print queue and the patient record actually read it. Most sites need both; they are not substitutes. --- ## 2. Outbound — `ORU^R01` (the finished report) ### 2.1 A real message ``` MSH|^~\&|SONOSCRIBE||||20260730185655||ORU^R01|MSG00001|P|2.5.1 PID|1||P12345 OBR|1||1.2.840.113619.2.55.3.1|CT BRAIN PLAIN|||20260730185655||||||||||||||CT OBX|1|TX|CEREBRAL_PARENCHYMA||Acute infarct in the left frontoparietal region.||||||F OBX|2|TX|VENTRICLES||Ventricles are normal in size.||||||F OBX|3|TX|IMPRESSION||Acute infarct, left MCA territory.||||||F OBX|4|TX|RECOMMENDATION||Clinical correlation.||||||F OBX|5|ST|SOFTWARE||SonoScribe 0.1.0||||||F OBX|6|TX|ATTRIBUTION||Drafted with SonoScribe||||||F OBX|7|TX|CRITICAL||Acute infarct|||A|||F ``` ### 2.2 Segment by segment **MSH — Message Header** | Field | Value | Note | |---|---|---| | MSH-3 | `SONOSCRIBE` | Sending application | | MSH-7 | `YYYYMMDDHHMMSS` | Message datetime | | MSH-9 | `ORU^R01` | | | MSH-10 | Message Control ID | Unique per message; echo it in your `MSA-2` | | MSH-11 | `P` | Processing ID | | MSH-12 | `2.5.1` | | MSH-2 declares the encoding characters `^~\&` that the rest of the message uses. **PID — Patient Identification** | Field | Value | |---|---| | PID-3 | Patient identifier **exactly as it appeared in your order** | We never invent or re-format a patient identifier. What you sent us is what comes back, so your matching logic does not have to guess. **OBR — Observation Request** | Field | Value | |---|---| | OBR-3 | Filler order number = the `StudyInstanceUID` | | OBR-4 | Universal Service ID = the study description | | OBR-7 | Observation datetime | | OBR-24 | Diagnostic Service Section ID — `CT`, `MR`, `US` | **OBX — Observation/Result**, one segment per line of the report | Field | Value | |---|---| | OBX-1 | Set ID, sequential from 1 | | OBX-2 | Value type — `TX` for prose, `ST` for the software identifier | | OBX-3 | Observation identifier — the report region (`IMPRESSION`, `RECOMMENDATION`, `CRITICAL`, or the template section key) | | OBX-5 | The text | | OBX-8 | Abnormal flags — `A` on a flagged finding, otherwise empty | | OBX-11 | Observation result status — see §2.4 | **Order matters.** OBX segments appear in the order a radiologist reads them: body regions first, then impression, then recommendations. Preserve the order and you reproduce the report. ### 2.3 Escaping — read this if you parse by splitting on `|` Radiology prose contains `|`, `^`, `&` and `\` regularly ("T2 hyperintense | mild", "S/D ratio"). All are escaped per HL7 rules (`\F\`, `\S\`, `\T\`, `\E\`) before they reach a field. A real newline inside prose is emitted as `\.br\` rather than a segment terminator — an unescaped one would silently truncate the message at that line and every downstream segment would be lost. **Un-escape OBX-5 before display.** If you render the raw field you will show escape sequences. ### 2.4 Result status and what we will never send | `OBX-11` | Meaning | |---|---| | `F` | Final — reviewed and signed by a radiologist | | `C` | Corrected — an amended report for a study already sent | **An unsigned draft is never transmitted.** There is no preliminary/`P` path in this interface: if a message reaches you, a radiologist signed it. ### 2.5 Flagged findings A time-critical finding rides as an `OBX` with observation identifier `CRITICAL` and abnormal flag `A`. It is **surfaced, never auto-communicated** — nothing in this interface pages anyone, routes to an on-call queue, or asserts that a human has been told. Acting on it is the clinician's decision and your workflow's responsibility. ### 2.6 Emphasis arrives as an abnormal flag, not as markup Our drafter marks a dictated abnormal finding with emphasis, which the Word and PDF renderers turn into bold. HL7 v2 `OBX-5` (`TX`) is plain text with no markup layer, so **we strip the markers** — you will never receive asterisks in the text. The information is not lost. A line that carried emphasis is sent with **`OBX-8 = A`** (abnormal), which is the standard's own field for exactly this. Two useful consequences: - Your RIS renders clean prose. - If your RIS highlights abnormal results, it highlights precisely the lines the radiologist dictated as abnormal, with no extra work on your side. A normal line carries an empty `OBX-8`, so the flag means something. A site that ignores abnormal flags on `TX` segments simply sees clean text. The text you receive is **character-for-character what the sonologist signed** in the Word document, including where the drafter left an unclosed emphasis span — that residue is dropped in the signed document, so it is dropped here too. The DICOM SR carries the identical text. One signed report, one wording, whichever system you read it in. ``` OBX|1|TX|CEREBRAL_PARENCHYMA||Acute infarct in the left frontoparietal region.|||A|||F OBX|2|TX|VENTRICLES||Ventricles are normal in size.||||||F ``` --- ## 3. Acknowledgements — strict by design We wait for your `ACK` on every message and read `MSA-1`: | `MSA-1` | Our behaviour | |---|---| | `AA` (Application Accept) | Delivered. Marked complete | | `CA` (Commit Accept) | Delivered. Marked complete | | `AE` / `AR` / `CE` / `CR` | **Failure.** Retried with backoff, and the reason recorded | | no ACK before timeout | **Failure.** Retried | **Only `AA` and `CA` count as delivered.** Anything else is a failure, including a malformed ACK we cannot parse. This is deliberate: a report that vanishes silently between two systems is the worst outcome in the whole integration, so we prefer to redeliver a report you already have than to lose one. Your interface engine should be idempotent on `MSH-10`. Default ACK timeout: 30 s (configurable per site). --- ## 4. Inbound — orders (`ORM^O01`, `OMI^O23`) We accept an order message to carry the two things that most improve report quality: | Field | Why it matters | |---|---| | **Clinical indication** (`OBR-31` reason for study, or `NTE`) | Reporting without an indication produces generic, low-value output. This is the single highest-value field you can send | | Accession / order number | Ties the report back to your order | Priors are retrieved separately — prior *studies* from the PACS, prior *report text* from the RIS. --- ## 5. Transport - **MLLP framing:** `` `message` `` (0x0B … 0x1C 0x0D). Standard. - **Segment terminator:** `` (0x0D). Our parser tolerates `\r`, `\n` and `\r\n` on the way in. - **Character encoding:** UTF-8. - **MLLP is unencrypted.** It runs between the edge connector and your interface engine **inside your network**, or across a VPN. Nothing on your firewall needs to open to the internet. --- ## 6. Not implemented - `ADT` patient administration feeds - `MDM` document management messages - HL7 v3 / CDA - FHIR — **is** supported, but as a separate interface; see the Partner Integration Guide --- ## 7. Testing your interface 1. Point `hl7_out_host` / `hl7_out_port` at your test interface engine. 2. We send one `ORU^R01` per signed report and wait for the ACK. 3. Return `MSA|AA|` to accept. Return anything else and we will retry — which is a useful way to test your own retry handling deliberately. Mirth Connect is a good place to absorb site-specific dialect differences. We have kept this interface deliberately plain rather than adding per-site variants to our own code, so that your channel — not our release cycle — is where a local quirk gets handled.