Core commands
Five commands complete a transaction.
EHLO opens the session and identifies the client by hostname. The server responds with the list of extensions it supports, which is how a client discovers whether STARTTLS, authentication mechanisms, size limits and pipelining are available. HELO is the older form that returns no extension list and exists for backward compatibility.
MAIL FROM sets the envelope sender, meaning the return path bounces go to. This is a different address from the From header the recipient sees, and conflating the two is behind a large share of misrouted bounce handling.
RCPT TO names one recipient and is repeated once per recipient. It produces the most informative rejection in the protocol: a 550 here usually means the mailbox does not exist, which is how a server reports an invalid address without anything being sent.
DATA begins message transmission. The server answers 354, the client sends headers, a blank line and the body, then terminates with a single dot on a line by itself.
QUIT closes the session cleanly. Abandoning the connection instead holds a slot on the server until it times out.
Extended commands
STARTTLS upgrades a plaintext connection to TLS, specified in RFC 3207. On port 587 this must complete before authentication, because the credential would otherwise cross the network in the clear.
AUTH authenticates the client, defined in RFC 4954. The mechanism is chosen from those the server advertised in its EHLO response, commonly PLAIN, LOGIN or XOAUTH2.
How strictly a server enforces these two commands is the whole game. Our own forwarding relay accepts nothing on a plaintext connection and nothing on loose credentials: TLS and exact SMTP authentication are mandatory, TCP 2525 is firewalled to our API host, every one-recipient route snapshot is HMAC-signed, and a replayed or unsigned request is refused. The relay's host sends no marketing campaigns and is not an open relay, which is precisely what a well-run AUTH policy is meant to guarantee.
RSET aborts the current transaction without closing the connection, which lets a client reuse an open session after a failure rather than reconnecting.
VRFY and EXPN were designed to verify an address and expand a mailing list. Both are disabled almost everywhere, because they let anyone enumerate valid addresses on a server.
Enumeration abuse did not stop at the protocol layer, it moved up to signups. Our own gate triggers on disposable and blocked email domains with full-domain and dot-boundary subdomain matching, backed by a curated bundled blocklist and an environment-driven override that ships without a deploy. Same instinct that killed VRFY, applied one layer higher.
NOOP does nothing and elicits a 250, which makes it useful for keeping a connection alive or confirming the server is still responsive.
Reading the responses
Every command returns a three-digit code. A 2xx means success, 3xx means the server is waiting for more input, 4xx is a temporary failure worth retrying, and 5xx is permanent.
The text after the code carries more information than the code does. Providers put the actual reason there, frequently including a URL explaining a block or a listing, and client libraries often discard it before the developer sees it.
Enhanced status codes add precision, defined in RFC 3463. A 5.1.1 identifies a nonexistent mailbox specifically, where the bare 550 could mean several things.
Issuing commands manually
A manual session is the lowest-level diagnostic available and strips away every abstraction between you and the server's actual response.
Use openssl s_client -starttls smtp rather than telnet for anything encrypted, since telnet cannot negotiate TLS and submission ports require it. The openssl form additionally shows the certificate chain and the advertised capability list.
Never type credentials into an unencrypted session. Anything sent before STARTTLS completes is readable on the path.
A manual session proves the server accepted a message. It does not prove placement, which is decided afterward by the receiving mailbox provider on reputation and authentication that a raw transaction does not exercise.
MAIL FROM and the From header are different fields
The command sequence is fixed and worth knowing, but the part that causes real confusion is that two different things both get called the sender. MAIL FROM is the envelope, given as a command. The From header is content, carried inside DATA. They are set separately and are not required to match.
That separation is what SPF actually evaluates, which is why a message can pass SPF while displaying a From address on a completely different domain. Anyone debugging an alignment failure by staring at the visible From address is looking at the wrong field.
Where a custom MAIL FROM has not been registered, our sender falls the visible From address back to the sending subdomain rather than failing. That is a deliberate choice, and knowing it exists explains an otherwise baffling symptom: mail that authenticates perfectly and displays a sender the customer did not configure.
Worth separating presentation problems from delivery problems when reading the conversation. We have had a visible From header issue where the mail was delivering and authenticating correctly throughout, which made it a presentation defect rather than a deliverability one, and the server's own responses said as much.
The reply codes are the honest output of the whole exchange. A 5xx is a permanent refusal and a 4xx is a transient one, and a client that collapses both into a generic failure discards the single most useful piece of information the server gave it.