# HL7 ADT to FHIR: mapping Patient and Encounter from A01, A04, A08 and A03

> A playbook for projecting HL7 v2 ADT A01, A04, A08 and A03 onto FHIR Patient and Encounter — PID-3 versus identifier.system, PV1-19 visit numbers, and de-identified test fixtures.

Author: Ala Ben Aicha

Canonical page: https://alabenaicha.me/insights/hl7-adt-to-fhir-patient-encounter

Updated: 2026-10-05

## Direct answer

ADT A01, A04 and A08 project PID to Patient and PV1 to Encounter. A03 ends the visit. PID-3 is Patient.identifier; PV1-19 is the visit number. Pin assigning authorities before you upsert.

## Message and resource mapping

Pin the published [HL7 Version 2 to FHIR IG 1.0.0 (STU 1)](https://hl7.org/fhir/uv/v2mappings/) (generated 7 October 2025, package `hl7.fhir.uv.v2mappings#1.0.0` on FHIR R4). The STU 1 [message maps](https://hl7.org/fhir/uv/v2mappings/message_maps.html) include [ADT\_A01 → Bundle](https://hl7.org/fhir/uv/v2mappings/ConceptMap-message-adt-a01-to-bundle.html). A04, A08 and A03 are still Chapter 3 Patient Administration trigger events; they reuse the same [PID → Patient](https://hl7.org/fhir/uv/v2mappings/ConceptMap-segment-pid-to-patient.html) and [PV1 → Encounter](https://hl7.org/fhir/uv/v2mappings/ConceptMap-segment-pv1-to-encounter.html) segment maps even when a dedicated message ConceptMap is not in that STU.

| Trigger (MSH-9) | v2 event                   | FHIR effect                                                                             | Status / class notes                                                                                                 |
| --------------- | -------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **ADT^A01**     | Admit / visit notification | Upsert Patient from PID; upsert Encounter from PV1; `Encounter.subject` → that Patient  | `Encounter.class` from PV1-2 (table 0004 → ActCode: I→IMP, O→AMB, E→EMER). Status `in-progress` when PV1-45 is empty |
| **ADT^A04**     | Register a patient         | Same Patient + Encounter pair                                                           | Typically outpatient (PV1-2 = O → AMB). Not a bed assignment                                                         |
| **ADT^A08**     | Update patient information | Patch Patient; update Encounter only when PV1 is present and the visit is already known | Not a new visit. Do not mint a second Encounter.identifier                                                           |
| **ADT^A03**     | Discharge / end visit      | Encounter.status → `finished`; `Encounter.period.end` from PV1-45                       | Do not delete Patient. Do not reuse the visit number on the next A01                                                 |

Segment-level pins from the IG:

| v2 field                      | FHIR path                | Type map                                                                     |
| ----------------------------- | ------------------------ | ---------------------------------------------------------------------------- |
| PID-3 Patient Identifier List | `Patient.identifier`     | CX → Identifier                                                              |
| PID-5 Patient Name            | `Patient.name`           | XPN → HumanName                                                              |
| PID-7 Date/Time of Birth      | `Patient.birthDate`      | TS → date                                                                    |
| PV1-2 Patient Class           | `Encounter.class`        | CWE → Coding (ActCode)                                                       |
| PV1-19 Visit Number           | `Encounter.identifier`   | CX → Identifier, type `VN` (`http://terminology.hl7.org/CodeSystem/v2-0203`) |
| PV1-44 Admit Date/Time        | `Encounter.period.start` |                                                                              |
| PV1-45 Discharge Date/Time    | `Encounter.period.end`   |                                                                              |

MSH still becomes MessageHeader / Provenance. EVN-2 is event time, not a second birth date. NK1 is RelatedPerson or `Patient.contact`, not another Patient. This page does not map A40 merges — that is an MPI problem, covered in the [master patient index article](https://alabenaicha.me/insights/master-patient-index-identity-management).

The hybrid engine pattern (keep v2 as the hospital nervous system, project FHIR for new consumers) is the [v2-to-FHIR migration playbook](https://alabenaicha.me/insights/hl7v2-to-fhir-migration-strategy). This page is the ADT mapping contract inside that engine.

## Identifier traps

PID-3 is a repeating CX. Each repeat is `ID^^^AssigningAuthority^IdentifierType`. FHIR needs `identifier.system` (the assigning authority as a URI you actually own) and `identifier.value`. A bare MRN string with no system will collide the first time two hospitals share a FHIR server.

Do not put PV1-19 on `Patient.identifier`. The IG types the visit number as `VN`. PID-18 (Patient Account Number) is not the visit number; the PID map even warns that some senders stuff a visit id into PID-18 — if yours does, treat it as a documented exception, not as the default.

National identifiers (AHV, NHS number, KVNR, IHI) belong in PID-3 with their published `system` URIs. Local MRNs stay local. The MPI article is where matching rules live; the ADT mapper's job is to *preserve* the CX, not to invent a golden record.

| Trap                               | Symptom                                              | Fix                                                                       |
| ---------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------- |
| PID-3 without assigning authority  | Duplicate Patient resources on the first A08         | Require CX.4 (or CX.4.2 namespace ID) before upsert; reject or quarantine |
| PV1-19 copied onto Patient         | Two patients share a visit number after transfer     | Visit → Encounter.identifier only                                         |
| A08 creating Encounter             | Census doubles on demographic fixes                  | Match Encounter by PV1-19 + Patient; insert only on A01/A04               |
| A03 deleting Patient               | Downstream Observation orphans                       | Patient stays; Encounter.status = finished                                |
| MSH-3 / MSH-4 as identifier.system | Systems collide when a vendor reuses a facility name | Pin a URI per sending application × assigning authority                   |

## Test fixtures

De-identified examples only. These values are specification fixtures, not a person.

```
MSH|^~\&|HIS|HOSP-A|FHIR-BRIDGE|HOSP-A|20260917103000||ADT^A01^ADT_A01|MSG0001|P|2.5
EVN|A01|20260917103000
PID|1||HOSP-MRN-0001^^^HOSP-A^MR||EXAMPLE^JANE^Q||19770412|F|||1 TEST STREET^^TESTVILLE^TN^10000
PV1|1|I|WARD-A^101^A||||ATTEND^SMITH^A|||SUR||||A0|||||VN-2026-00042^^^HOSP-A^VN|||||||||||||||||||||||||20260917103000
```

Projected Patient (truncated):

```json
{
  "resourceType": "Patient",
  "identifier": [
    {
      "type": {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
            "code": "MR"
          }
        ]
      },
      "system": "https://hosp-a.example.org/mrn",
      "value": "HOSP-MRN-0001"
    }
  ],
  "name": [{ "family": "EXAMPLE", "given": ["JANE", "Q"] }],
  "birthDate": "1977-04-12",
  "gender": "female"
}
```

Projected Encounter (truncated):

```json
{
  "resourceType": "Encounter",
  "status": "in-progress",
  "class": {
    "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
    "code": "IMP"
  },
  "identifier": [
    {
      "type": {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
            "code": "VN"
          }
        ]
      },
      "system": "https://hosp-a.example.org/visit",
      "value": "VN-2026-00042"
    }
  ],
  "subject": { "reference": "Patient?identifier=https://hosp-a.example.org/mrn|HOSP-MRN-0001" },
  "period": { "start": "2026-09-17T10:30:00+01:00" }
}
```

Minimum fixture set before cutover:

1. A01 then A08 (name correction) — one Patient, one Encounter, name updated.
2. A01 then A03 — Encounter.period.end set, status finished, Patient still searchable.
3. A04 outpatient register — class AMB, no bed in `Encounter.location` unless PV1-3 is valued.
4. Two A01 with the same PID-3 and different PV1-19 — two Encounters, one Patient.
5. A08 with missing PV1-19 — Patient updates; no new Encounter.

Never copy a live PID into a ticket, a git repo, or this site.

## Failure modes and rollback

| Failure                                         | Detection                                                                                     | Rollback                                                                                       |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Duplicate Patient                               | Two resources share PID-3 value with different systems, or the same system after a mapper bug | Stop the writer; merge only with an agreed MPI rule; replay A08 from the v2 store              |
| Duplicate Encounter                             | Two in-progress Encounters for one PV1-19                                                     | Treat PV1-19 + assigning authority as the natural key; close the extra as entered-in-error     |
| Late A03                                        | Census still shows in-progress after discharge                                                | Idempotent A03: set status and period.end; do not require the original A01 in the same process |
| A08 applied as A01                              | Extra visits on demographic noise                                                             | Filter MSH-9.2 before insert; A08 is PATCH                                                     |
| Loop with a FHIR subscription that re-emits ADT | Message volume cliff                                                                          | One authoritative write path; provenance on the projected resources; suppress echo by MSH-3    |

Keep v2 as the source of truth until the FHIR Patient/Encounter pair has a named owner, an ACK policy, and a replay from the engine's message store. The [migration playbook](https://alabenaicha.me/insights/hl7v2-to-fhir-migration-strategy) covers shadow, dual-write and cutover as architecture. This mapper must still be able to disable the FHIR writer without touching the HIS ADT feed.

## Related service

If you need the mapping implemented on an engine (NextGen Connect / Mirth, or a FHIR facade in front of a v2 feed), that is [HL7 FHIR integration](https://alabenaicha.me/services/hl7-fhir-integration). For a scoped ADT→Patient/Encounter spike, use [contact with project intent](https://alabenaicha.me/contact?intent=project).

This page is not a conformance certificate against the v2-to-FHIR IG, not an MPI product, and not clinical advice about admission or discharge.
