Source: https://cli.nylas.com/guides/smtp-reply-codes-explained

# SMTP Reply Codes Explained

An SMTP server answers every command with a three-digit reply code. The first digit tells you everything that matters: 2xx means it worked, 4xx means try again later, and 5xx means stop and fix something. This reference decodes the codes you actually see in bounces, shows how to read the enhanced status code inside a delivery status notification, and how to confirm what landed using the Nylas CLI from the terminal.

Written by [Pouya Sanooei](https://cli.nylas.com/authors/pouya-sanooei) Software Engineer

Updated June 9, 2026

> **TL;DR:** Read the first digit of an SMTP reply code first: `2xx` means the server accepted the command, `4xx` is a temporary failure you should retry, and `5xx` is permanent — the message will not go through until you change something. A bounce carries a second, finer signal you can act on without reading raw SMTP logs at all. The CLI lets you confirm delivery from the terminal with `nylas email list` and `nylas email search`.

Command references used in this guide: [`nylas email list`](https://cli.nylas.com/docs/commands/email-list), [`nylas email search`](https://cli.nylas.com/docs/commands/email-search), and [`nylas email read`](https://cli.nylas.com/docs/commands/email-read).

## What are SMTP reply codes?

An SMTP reply code is a three-digit number a mail server returns after every command, defined in [RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321). The first digit is the verdict: `2` means accepted, `4` means a temporary failure, and `5` means a permanent failure. Read that digit before anything else.

The standard has used this structure since RFC 821 in 1982, so the same five families work across every provider. The second and third digits add detail, but the leading digit alone tells you whether to celebrate, retry, or stop. A `250` after the final `DATA` command is the only code that means the receiving server took responsibility for the message. Everything from a `354` prompt to a `421` shutdown is a step along the way, and a single failed step is what produces the bounce that lands back in your inbox minutes later.

```bash
# The five SMTP reply families (first digit = verdict)
# 2yz  Positive completion   accepted (e.g. 250 OK)
# 3yz  Positive intermediate server wants more (e.g. 354 start mail input)
# 4yz  Transient failure     retry later (e.g. 421, 451, 452)
# 5yz  Permanent failure     fix something (e.g. 550, 552, 554)
```

## What do 2xx and 3xx SMTP codes mean?

A `2xx` SMTP reply code is a success: the server accepted the command and is ready for the next one. A `3xx` code is intermediate — the server understood you and is waiting for more input before it can finish. Together they form the normal path of a healthy delivery, and you rarely see them unless you watch a live session.

The two codes that matter are `250` and `354`. After your client issues `MAIL FROM`, `RCPT TO`, and then `DATA`, the server answers `354` to ask for the message body, and a final `250` once it accepts custody. That final `250` is the moment delivery is the receiving server's problem, not yours. RFC 5321 reserves `220` for the greeting and `221` for a clean disconnect, so a full conversation touches four or five success codes before it ends. None of these reach the recipient as a bounce, which is why a sender almost never reads them directly.

```bash
# A successful SMTP exchange (server replies on the left)
220 mx.example.com ESMTP ready
250 mx.example.com Hello
250 2.1.0 Sender OK            # MAIL FROM accepted
250 2.1.5 Recipient OK         # RCPT TO accepted
354 Start mail input           # send the message body now
250 2.0.0 Message accepted     # custody transferred
221 2.0.0 Bye
```

## Why does a 4xx SMTP code mean retry but 5xx mean fix it?

A `4xx` SMTP reply code is a transient failure: the server could not accept the message right now but might in a few minutes. A `5xx` code is permanent — retrying changes nothing because the problem is the request itself, like a recipient address that does not exist. The leading digit decides whether your retry queue should hold the message or drop it.

This split is why a well-behaved sender treats the two families differently. A `421` (service unavailable) or `451` (local processing error) should sit in a retry queue with exponential backoff; most senders retry for up to 5 days before giving up, the default Postfix bounce window (its maximal_queue_lifetime defaults to 5 days). A `550` (mailbox unavailable) or `554` (transaction failed) should never be retried — resending the same message to the same dead address just damages your sender reputation. Greylisting deliberately returns a `4xx` on first contact and accepts the retry, so a sender that drops on the first `4xx` loses legitimate mail. Read the digit, then decide retry versus fix.

```bash
# Common transient (4xx) codes — keep retrying
# 421  Service not available, closing channel (or greylisting / rate limit)
# 450  Mailbox unavailable, busy — try later
# 451  Local error in processing
# 452  Insufficient system storage (often a quota or rate cap)

# Common permanent (5xx) codes — stop and fix
# 550  Mailbox unavailable / address does not exist
# 552  Message exceeds size limit
# 553  Mailbox name not allowed (bad address syntax)
# 554  Transaction failed (often spam rejection or policy block)
```

## How do I read a bounce DSN and its enhanced status code?

A bounce arrives as a delivery status notification (DSN), a structured report defined in [RFC 3464](https://datatracker.ietf.org/doc/html/rfc3464). Inside it sits an enhanced status code — three numbers like `5.1.1` — defined in [RFC 3463](https://datatracker.ietf.org/doc/html/rfc3463). It tells you the exact reason far more precisely than the bare `550` does.

The enhanced code has three parts: class, subject, and detail. The class digit mirrors SMTP — `2` success, `4` transient, `5` permanent — so `5.1.1` is a permanent address failure (bad mailbox) and `4.2.2` is a transient over-quota condition. The middle digit names the subject: `1` for addressing, `2` for the mailbox, `7` for security or policy. IANA maintains the full registry of these codes in its [enhanced status code assignments](https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xhtml). A bounce with `5.7.1` is a policy rejection (often anti-spam) you fix by authentication, while `5.1.1` means the address is wrong and no fix on your side will deliver it. The DSN's `Action: failed` field confirms it was a hard bounce, not a delay.

```bash
# The machine-readable part of a bounce DSN (message/delivery-status)
Reporting-MTA: dns; mx.example.com
Final-Recipient: rfc822; nobody@example.com
Action: failed
Status: 5.1.1                       # class.subject.detail
Diagnostic-Code: smtp; 550 5.1.1 The email account that you tried to
 reach does not exist.

# Read it as: permanent (5) . addressing (1) . bad mailbox (1)
```

## How do I debug delivery with the Nylas CLI?

You debug delivery by checking two things: did the message leave, and did a bounce come back. The Nylas CLI confirms both from the terminal without an SMTP client or raw log access. The CLI sends over OAuth across Gmail and Outlook, so most `5xx` auth-and-relay failures that plague hand-configured SMTP never happen in the first place.

The `nylas email send` command transmits the message; a non-zero exit code surfaces a hard failure immediately instead of a silent drop. To confirm what landed, list the Sent folder with `nylas email list --folder SENT`, then catch a bounce with `nylas email search` — mailer-daemon notifications almost always carry the subject prefix `Undelivered` or arrive from a `mailer-daemon` address. Open the matched message with `nylas email read` to see the DSN body and its enhanced status code. The search auto-paginates past 200 results and accepts `--after` to scope a window to the minutes around a send, so a bounce from a batch of 500 is one command away.

```bash
# 1. Send the message (OAuth handles auth; no SMTP config)
nylas email send --to user@example.com --subject "Build report" \
  --body "Nightly build passed." --yes

# 2. Confirm it left — list the Sent folder
nylas email list --folder SENT --limit 10 --json

# 3. Catch a bounce — mailer-daemon DSNs land in the inbox
nylas email search "Undelivered" --after 2026-06-09 --json

# 4. Read the DSN body to see the enhanced status code (e.g. 5.1.1)
nylas email read <message-id> --json
```

## Next steps

- [Soft Bounce vs Hard Bounce Explained](https://cli.nylas.com/guides/soft-bounce-vs-hard-bounce) — Soft bounces (4xx) are transient and retried for up to 5 days;…
- [Send Email in Go: net/smtp, APIs, and CLI](https://cli.nylas.com/guides/send-email-go) — Four ways to send email from Go
- [Send Email in Java: Jakarta Mail and APIs](https://cli.nylas.com/guides/send-email-java) — Four ways to send email in Java
- [Send Email in Ruby: Net::SMTP, Mail Gem, CLI](https://cli.nylas.com/guides/send-email-ruby) — Three ways to send email in Ruby
- [Send Email in C#: MailKit, Graph API, CLI](https://cli.nylas.com/guides/send-email-csharp) — SmtpClient is no longer recommended.
- [Send Email in Rust: lettre and Email APIs](https://cli.nylas.com/guides/send-email-rust) — Three ways to send email in Rust
- [Validate Email Addresses from the CLI](https://cli.nylas.com/guides/email-address-validation-cli) — Validate email addresses from the terminal with an RFC 5322 syntax…
- [Handle email bounces](https://cli.nylas.com/guides/email-bounce-handling-cli) — parse DSNs and route hard bounces from the terminal
- [Debug email delivery](https://cli.nylas.com/guides/debug-email-delivery-cli) — when a sent message never arrives
- [Improve email deliverability](https://cli.nylas.com/guides/email-deliverability-cli) — SPF, DKIM, and DMARC to avoid 5.7.x rejections
- [Turn bounces into Zendesk tickets](https://cli.nylas.com/guides/email-to-zendesk-tickets) — route failed deliveries to support
- [Turn emails into Jira issues](https://cli.nylas.com/guides/email-to-jira-issues) — file a ticket when a delivery fails
- [Getting started](https://cli.nylas.com/guides/getting-started) — install the CLI and connect an account
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
- Standards: [RFC 5321 (SMTP)](https://datatracker.ietf.org/doc/html/rfc5321), [RFC 3463 (enhanced codes)](https://datatracker.ietf.org/doc/html/rfc3463), and the [IANA enhanced status code registry](https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xhtml)
