HL7 ACK and NAK codes: AA, AR, AE explained
HL7 acknowledgment codes tell the sender what happened to their message. AA means accepted, AR means rejected permanently, AE means try again.
Every HL7 v2 message gets a response. The response is an ACK message containing a code that tells the sender what happened. Three codes cover every outcome: AA (accepted), AR (rejected), and AE (error). The difference between AR and AE determines whether the sender should retry.
The MSA segment
The acknowledgment code lives in the MSA (Message Acknowledgment) segment. MSA has three fields that matter:
| Field | Name | Content |
|---|---|---|
| MSA-1 | Acknowledgment code | AA, AR, or AE |
| MSA-2 | Message control ID | Echoed from MSH-10 of the original message |
| MSA-3 | Text message | Optional. Human-readable reason for AR or AE |
MSA-2 is how the sender correlates an ACK with the message it sent. The control ID in MSA-2 must match MSH-10 of the original message exactly.
A minimal ACK message looks like this:
MSH|^~\&|LAB|PATHOLOGY|HIS|CENTRAL|20260403120001||ACK|ACK00001|P|2.3
MSA|AA|MSG00001
Notice the sending and receiving applications are swapped. The original message was sent from HIS/CENTRAL to LAB/PATHOLOGY. The ACK reverses that: LAB/PATHOLOGY is now the sender, HIS/CENTRAL is the receiver.
AA: application accept
AA means the receiver processed the message successfully. The message was
parsed, passed any validation, and was accepted for further processing.
MSA|AA|MSG00001
“Accepted for further processing” is an important distinction. AA does not
mean the message has been delivered to every downstream system. It means the
receiver took responsibility for it. What happens next — routing, transformation,
delivery — is the receiver’s problem. The sender can move on to the next message.
In practice, a well-implemented receiver persists the message before sending AA.
If the receiver crashes after sending the ACK, the message survives. The sender
won’t resend it because it already got AA.
AR: application reject
AR means the message is permanently rejected. Something about the message
itself is wrong, and sending it again won’t change the outcome.
MSA|AR|MSG00001|PID-3.1 is required
Common reasons for AR:
Malformed message. The MSH segment can’t be parsed. Field separators are wrong, required fields are missing, or the segment structure is invalid. The receiver can’t extract enough information to understand what the sender intended.
Validation failure. The message parses correctly but fails a business rule. A patient ID is missing from PID-3, a sending facility is blank in MSH-4, or a message type isn’t accepted at this endpoint. The rule is explicit: this message doesn’t meet the requirements.
Unknown message type. The receiver only accepts certain message types (ADT, ORU, ORM) and this message is a type it doesn’t handle.
The text in MSA-3 should tell the sender what’s wrong. “PID-3.1 is required” is actionable. “Message rejected” is not. A good rejection message lets the sender fix the problem without calling the receiver’s support team.
What the sender should do with AR
Do not retry. The message is wrong. Retrying the same message will produce the
same AR. Log the rejection, alert someone, fix the message, and send a
corrected version with a new control ID.
Some senders queue rejected messages for manual review. Others route them to a dead letter queue. The approach depends on the integration, but the one thing that never works is automatic retry.
AE: application error
AE means something went wrong on the receiver’s side. The message might be
fine — the receiver just couldn’t process it right now.
MSA|AE|MSG00001|Routing failed: connection refused
Common reasons for AE:
Internal error during validation. The receiver’s validation logic encountered an unexpected condition — a type mismatch in a rule expression, a failure to extract variables from the message, or a configuration error. The message itself may be valid; the receiver’s processing failed.
Downstream unavailable. The receiver accepted the message but couldn’t route it to a required downstream system. A database is down, an HTTP endpoint is unreachable, or a dependent service timed out.
Resource exhaustion. The receiver is overloaded. Connection limits, memory pressure, or queue depth caused the processing to fail.
The key distinction from AR: AE says “I can’t handle this right now” rather
than “this message is wrong.” The same message sent later might succeed.
What the sender should do with AE
Retry with backoff. Wait a few seconds, then resend the same message with the same control ID. If the problem was transient — a momentary network issue, a restarting service — the retry will succeed.
Use exponential backoff: 1 second, 2 seconds, 4 seconds, up to a maximum delay. Add jitter (random variation in the delay) to prevent multiple senders from retrying in lockstep after an outage.
After a maximum number of retries, stop and alert. If the receiver is returning
AE consistently, the problem isn’t transient and a human needs to investigate.
The decision tree
When a receiver processes an inbound HL7 message, the ACK code follows a deterministic path:
Message received
│
├── Can the MSH segment be parsed?
│ NO → AR ("Invalid MSH: ...")
│
├── Do all validation rules pass?
│ Rule returns false → AR (rule's error message)
│ Rule evaluation errors → AE ("rule evaluation error: ...")
│
├── Can the message be routed?
│ Routing fails → AE ("Routing failed: ...")
│
└── All steps succeed → AA
The order matters. Parse errors are caught first, then validation, then routing. A message that can’t be parsed never reaches validation. A message that fails validation never reaches routing.
Notice that validation produces two different codes depending on what went wrong.
If a rule evaluates successfully and returns false (the message doesn’t meet the
criteria), that’s AR — the message is permanently wrong. If the rule itself
fails to evaluate (a configuration error, a type mismatch), that’s AE — the
receiver’s problem, not the sender’s.
Enhanced vs original acknowledgment mode
HL7 v2 defines two acknowledgment modes in MSH-15 and MSH-16:
Original mode (the default). The receiver sends one ACK per message. This is what most implementations use and what this article describes.
Enhanced mode. Two-phase acknowledgment. The receiver first sends an accept acknowledgment (confirming receipt), then later sends an application acknowledgment (confirming processing). MSH-15 controls the accept phase, MSH-16 controls the application phase.
Enhanced mode is rare in practice. Most interfaces use original mode because it’s simpler and the single ACK is sufficient for reliable delivery. If you’re building a new interface, start with original mode unless the other side specifically requests enhanced.
Common implementation mistakes
Retrying AR responses. The most common mistake. A sender gets AR, waits,
and resends the same message. The receiver rejects it again. The sender retries
again. This loop continues until someone notices the queue growing. AR means
stop — not slow down.
Empty MSA-3 on rejection. Returning AR without a reason forces the
sender to guess what’s wrong. Include the specific field or rule that failed.
Using AE for validation failures. If a message is missing a required field,
that’s AR, not AE. The sender needs to fix the message. Returning AE
tells them to retry, which wastes time on both sides.
Sending AA before persisting. If the receiver sends AA and then crashes
before saving the message, the message is lost. The sender won’t resend it
because it already got AA. Persist first, acknowledge second.
Ignoring the control ID. MSA-2 must match MSH-10 from the original message. Some implementations generate a new ID instead of echoing the original. This breaks correlation on the sender’s side.
For background on the protocol that carries these acknowledgments, see What is MLLP and how does it work.