# HL7 Desk > Paste one HL7 version 2 message captured off a hospital interface. A real HL7 v2 parser runs free > in the browser; four AI lanes then decode it, check its conformance, build the acknowledgment the > receiver should return, or map it to FHIR R4. Live at https://hl7-desk.skillsafe.ai/ · API tutorial at https://hl7-desk.skillsafe.ai/api.html ## What it is for The audience is the people who build and debug health interfaces: integration analysts, interface engineers, and the developers who put a FHIR facade in front of a v2 feed. The work object is exactly one thing — **one HL7 v2 message** — and every lane runs over that same paste. It is a developer tool. It is not a medical device, not clinical software, and it gives no clinical, diagnostic or treatment advice. Its prompt forbids interpreting a diagnosis, a result value or an allergy clinically: to HL7 Desk those are field values. ## The free lane: a real HL7 v2 parser in the browser No account, no network call, no model. This is the part that makes the AI lanes accountable, because the model is sent these facts and must reconcile against them. - **Delimiters are read, not assumed.** The five delimiters come out of MSH-1 and MSH-2. A message using `#` and `@` instead of `|` and `^` parses identically, and every HL7 text the app produces uses the characters that message declared. - **MSH field numbering is handled correctly.** MSH-1 *is* the field separator and MSH-2 *is* the encoding-character set, so field *n* of MSH sits at split index *n-1* while every other segment's field *n* sits at index *n*. MSH-2 is never subdivided by the characters it declares. - **Repetitions, components and subcomponents** are split, and the exact split is kept, so the parsed message re-serialises byte for byte. - **HL7 escape sequences are decoded** for display only — `\F\ \S\ \R\ \T\ \E\`, `\Xdd..\` hex, `\C..\`/`\M..\` charset, `\H\`/`\N\` highlighting, `\Z..\` local. An undefined sequence is reported rather than silently passed through, because an unresolved backslash in a clinical field is a real corruption risk. - **Timestamps are validated as real calendar dates**, not as digit counts. `20260231` is eight digits and is not a date. Leap years are computed properly: 2024 yes, 1900 no, 2000 yes. Legal lengths are 4, 6, 8, 10, 12 or 14 digits with an optional fraction and an optional ±HHMM offset, and a time of day in a date-only field is reported. - **HL7-defined code tables are expanded** — 0001 sex, 0004 patient class, 0008 acknowledgment code, 0076 message type, 0103 processing ID, 0104 version, 0125 value type, 0136 yes/no, 0155 acknowledgment conditions, 0357 error conditions — and only those. A site's own value in a *user-defined* table is legitimate and is not flagged. - **The message structure is resolved the way HL7 actually groups trigger events.** A04, A08 and A13 all use `ADT_A01`; A28 and A31 use `ADT_A05`; A40 uses `ADT_A39`. Deriving the structure as "code + underscore + event" invents names like `ADT_A08` that no grammar exists for. The resolved structure is reported separately from whatever MSH-9.3 claims, so a disagreement is visible. - **Required segments and segment order** are checked against that resolved structure. Order is checked as a subsequence, so omitting optional segments is free and only a genuine inversion is reported — HL7 error condition 100, segment sequence error. - **Required fields, data types, and cross-field consistency** — a discharge before its admit, an EVN-1 that disagrees with MSH-9.2, an inpatient with no ward, an event recorded after the message was built. - **Every patient identifier is inventoried**, and can be redacted in one click. See below. - **Exports with no run and no account:** a field-level CSV (one row per populated field: location, the standard's name for it, the exact bytes, the decoded reading), a flags CSV, and the full parsed structure as JSON. ## De-identification, in the browser, before anything is sent Two properties, both deliberate and both tested: - **Stable pseudonyms.** The same source value always maps to the same pseudonym, so relationships survive: a next of kin who shared the patient's surname still does, and a merge message still shows two *distinct* identities. Random-per-occurrence replacement would destroy exactly what a decode lane is for. - **Structure preserved.** Only identifying components are replaced. An identifier's assigning authority and type stay; a name's suffix and name-type-code stay; a date of birth is generalised to its year (still a legal HL7 timestamp); an address keeps its state and country and truncates the postal code to three characters. The redacted message must pass and fail the same conformance checks the real one did, or the review would be of a message the user never had. The asymmetric invariant, which is what makes it trustworthy: redaction may never **add** a conformance defect, and may only **remove** one whose location is a field it actually rewrote. When that happens — an invalid escape sequence inside a patient's surname, say — the app lists the defects redaction hid rather than letting them vanish, and says a lane run on the redacted message will not see them. The prescan sent with a run is computed from **exactly the text that is sent**. Redaction rewrites the input box itself, so a run after redaction sends the redacted message and facts derived from it, never readings of the original. ## The four lanes One work object, one composed system prompt, one model, an explicit `task` router, and a shared envelope. Each result offers the next lane as a button. | `task` | Stage | What it produces | | --- | --- | --- | | `decode` | Read | What the message says: one reading per segment in message order, 4–10 notable fields with why each matters *in this message*, and what the receiver is expected to do on receipt. | | `conformance` | Verify | Nine named check areas, each pass/warn/fail, plus a corrected message using the message's own delimiters and one note per real change. It never invents a value to fill an empty required field — the field stays empty and the need for it is raised. | | `ack` | Respond | The acknowledgment the receiver should return: an `AA`/`AE`/`AR` code from HL7 table 0008, one ERR per error with a real table 0357 code, and the MSH direction reversed correctly (the ACK's MSH-3/4 are the original's MSH-5/6). MSA-2 echoes the original's MSH-10 exactly. | | `fhir` | Produce | FHIR R4 resources with a field-level mapping table and a Bundle, with v2 timestamps converted honestly (no invented time-zone offset, a date staying a `date`), identifier assigning authorities carried into `Identifier.system`, and an explicit list of what could not be mapped. | ## How the AI pass is held to the parse - **Reconciliation.** Exactly one entry per distinct flag the parser raised. A flag the model ignored renders as "NOT ACCOUNTED FOR", not as a gap nobody sees. - **Grounding.** Every HL7 location the model cites — in findings, segment readings, notable fields, correction notes, ERR segments, mapping rows and the unmapped list — is checked against the set the parsed message actually contains. An unknown location is kept and marked, never dropped. - **Bundle validity.** The FHIR lane's `bundle_json` is parsed. If it is not valid JSON the panel says so rather than presenting unpostable text as a Bundle. - **Partial results.** A stream that dies mid-flight renders whatever sections parsed, with an honest note. ## Contract - Input: `{task, message_text, profile, receiver_note, deidentified, prescan}`, plus `prior_conformance` on a handoff and `clip_note` when the paste was cut. `profile` is one of `hl7-v2-generic`, `ihe-pam`, `us-core-adt`, `unknown`. - Output: one JSON object — a common envelope (`task`, `task_inferred`, `title`, `verdict`, `summary`, `assumptions`, `open_questions`, `findings`, `reconciliation`, `next_lane`) plus a per-lane `body`. - `verdict` describes the **message** in every lane: `conformant`, `minor-issues`, `non-conformant`, `unparseable`. - Model `gpt-terra`, publisher markup 1000 bps. A run is metered and needs a signed-in account; everything described under "the free lane" is not. - Long pastes are cut on whole-segment boundaries, never mid-segment, and the cut is stated to both the user and the model. ## Sources and attribution HL7 Desk is a derived work inspired by two published agent skills: - **@dedalus-erp-pas/hl7-pam-parser** — parsing, explaining and structurally validating HL7 v2.5 IHE PAM patient-administration messages. https://github.com/dedalus-erp-pas/foundation-skills - **@anthropics/fhir-developer-skill** — FHIR development. https://github.com/anthropics/healthcare The in-browser parser, validator and de-identifier are an independent implementation written for this app. HL7 Desk is not affiliated with, endorsed by, or maintained by Dedalus ERP-PAS or by the maintainers of either repository. HL7 and FHIR are trademarks of Health Level Seven International. Both bundled examples are synthetic. No real patient, clinician or facility is described in them.