# Delivery Status Notifications — DSN Format (RFC 3464)

> How bounce messages are structured: the multipart/report DSN format, per-message and per-recipient fields (Action, Status, Diagnostic-Code), and how senders should parse them.

Source: emailmarketing.net — https://emailmarketing.net/learn/bounce-handling/delivery-status-notifications

To process bounces automatically, you need to read the machine-readable bounce message that mail servers send back. When a message cannot be delivered (or its delivery is delayed, or a positive notification you requested is triggered), the reporting MTA sends a **Delivery Status Notification (DSN)** back to the envelope sender.

RFC 3464 defines the standard format of that message. Every automated bounce processor is, at its core, a parser of this format plus heuristics for the bounces that do not follow it.

## Envelope rules

- A DSN sent over SMTP **MUST use a null return path**: `MAIL FROM:<>`. This prevents mail loops, because no DSN is ever generated for a DSN.
- It follows that bounces arrive at whatever address you put in `MAIL FROM` on the original message. A return path that encodes each recipient (**VERP**, for example `bounces+user=example.com@sender.com`) lets you identify the failed recipient even when the body of the DSN is malformed. It also lets a [feedback loop processor](https://emailmarketing.net/learn/list-management/complaint-feedback-loops) reuse the same suppression pipeline.
- Each recipient the sender specified SHOULD produce **at most one** "delivered" or "failed" DSN. When a message reaches a mailing list alias, the forwarding MTA SHOULD issue an "expanded" DSN for the original recipient, and should not pass the DSN request on to the expanded addresses.

## Overall structure: multipart/report

A DSN is a MIME message with `Content-Type: multipart/report; report-type=delivery-status`. It contains:

| Part | Content-Type | Purpose |
|---|---|---|
| 1 | `text/plain` (typically) | An explanation of what happened, for people to read |
| 2 | `message/delivery-status` | The status in a form machines can parse. This is the part bounce processors read |
| 3 | `message/rfc822` or `text/rfc822-headers` | The returned original message (or its headers). Optional |

The `message/delivery-status` part is a set of groups of fields written like headers: one **per-message** group, then one **per-recipient** group for each recipient reported. A blank line separates the groups.

## Per-message fields

| Field | Required? | Syntax and meaning |
|---|---|---|
| `Reporting-MTA` | **Required** | `mta-name-type; mta-name`: the MTA reporting the result, for example `dns; mail.example.net` |
| `Original-Envelope-Id` | Optional | Transaction ID supplied at submission, for matching the DSN with the original send |
| `DSN-Gateway` | Conditional | Present only when the DSN was translated from a foreign (non-Internet) reporting system |
| `Received-From-MTA` | Optional | Name of the MTA the message was received from |
| `Arrival-Date` | Optional | RFC 822 date-time the message arrived at the reporting MTA |

MTA names are case-sensitive, and their spelling must be preserved exactly.

## Per-recipient fields

| Field | Required? | Syntax and meaning |
|---|---|---|
| `Final-Recipient` | **Required** | `address-type; generic-address`: the recipient address as the reporting MTA saw it |
| `Action` | **Required** | One of five values, below |
| `Status` | **Required** | Enhanced status code, `DIGIT.1*3DIGIT.1*3DIGIT` (for example `5.1.1`) |
| `Original-Recipient` | Optional | The address as the sender originally specified it (before forwarding or rewriting) |
| `Remote-MTA` | Optional | The next-hop MTA involved in the reported delivery attempt |
| `Diagnostic-Code` | Optional | `diagnostic-type; text`: the actual transport-level error, for example the remote server's full SMTP reply |
| `Last-Attempt-Date` | Optional | RFC 822 date-time of the final delivery attempt |
| `Final-Log-ID` | Optional | Index into the reporting MTA's delivery log |
| `Will-Retry-Until` | Optional | For `delayed` only: when the MTA will give up |

### Action values (exactly five, case-insensitive)

| Action | Meaning | How a bounce processor handles it |
|---|---|---|
| `failed` | Could not be delivered, and delivery has been abandoned for good | This is the bounce. Classify it using `Status` and `Diagnostic-Code` |
| `delayed` | Not yet delivered; retries continue (check `Will-Retry-Until`) | Informational. Do not suppress, because a `failed` DSN may or may not follow |
| `delivered` | Successfully delivered to the recipient address | A positive DSN (sent only if requested); not a bounce |
| `relayed` | Forwarded into an environment that does not take responsibility for DSNs | Final as far as DSNs are concerned, but not proof of delivery |
| `expanded` | Delivered to the address the sender specified, which was an alias or list with several recipients that sent the message on | Not final; used only for aliases with several recipients |

### Status vs Diagnostic-Code

- `Status` carries the [enhanced status code](https://emailmarketing.net/learn/bounce-handling/smtp-enhanced-status-codes), which does not depend on the transport. It is the main key for classification (`4.x.x` soft, `5.x.x` hard).
- `Diagnostic-Code` keeps the raw transport error. When its type is `smtp`, the text is the remote server's SMTP reply. The RFC notes that it is "somewhat redundant" with `Status`, but is provided to keep the original information. When `Remote-MTA` is present, the diagnostic came from that MTA; otherwise it came from the reporting MTA. In practice, the diagnostic text often carries details specific to the provider (blocklist names, policy URLs, "user unknown") that the numeric `Status` lacks, so parse both.

### Type conventions

- The values of `address-type`, `mta-name-type` and `diagnostic-type` are case-insensitive atoms. The standard values are `rfc822` (addresses), `dns` (MTA names) and `smtp` (diagnostics). `unknown` is used when the type cannot be determined, the `X-` prefix marks experimental types, and new types are registered with IANA.
- The content after the type may be case-sensitive (the local parts of mailboxes, for example) and must be preserved exactly.

## Example (from the RFC)

```
Reporting-MTA: dns; cs.utk.edu

Original-Recipient: rfc822;louisl@larry.slip.umd.edu
Final-Recipient: rfc822;louisl@larry.slip.umd.edu
Action: failed
Status: 4.0.0
Diagnostic-Code: smtp; 426 connection timed out
Last-Attempt-Date: Thu, 7 Jul 1994 17:15:49 -0400
```

## Parsing guidance for senders

1. **Identify the recipient.** Prefer `Original-Recipient`, and fall back to `Final-Recipient`. Strip the `rfc822;` type prefix. If the body cannot be parsed, fall back to the return path encoded with VERP.
2. **Filter on `Action`.** Only `failed` should lead to suppression. Ignore `delayed` for list hygiene, but log it for delivery monitoring. Treat `delivered`, `relayed` and `expanded` as not being bounces.
3. **Classify on `Status`.** Send `5.x.x` to your hard-bounce logic and `4.x.x` to your soft-bounce counters. Use the [table of detail codes](https://emailmarketing.net/learn/bounce-handling/smtp-enhanced-status-codes) to separate codes for bad addresses (suppress them) from codes for policy or reputation (investigate them). A wave of `5.7.1` is a problem with the sender, not with the list.
4. **Refine on `Diagnostic-Code`.** Rules based on regular expressions or keywords, applied to the raw SMTP text, catch conditions specific to a provider that the numeric code hides (named blocklists, "spam content", notices of rate limits).
5. **Expect several recipient groups in one DSN.** One bounce message can report several recipients, so process each group separately.
6. **Expect bounces that do not follow the format.** Plenty of real-world bounces are free-form text with no `message/delivery-status` part at all. Parsing RFC 3464 is the fast path; keep a heuristic fallback, with VERP as the safety net.
7. **Never auto-reply to a DSN**, and never send bounces of your own with a return path that is not null. The `MAIL FROM:<>` rule is what keeps the email ecosystem free of loops.
