@Mail Domain Check

SMTP 250 | Asynchronous bounce | Per-recipient status

SMTP 250 accepted, but the email later bounced

A final 250 after DATA transfers responsibility to that SMTP server. It does not prove that every recipient reached a mailbox or inbox. Record relay acceptance honestly, return every synchronous recipient result, and reconcile later DSNs or authenticated provider events.

What SMTP 250 proves

RFC 5321 says that a receiver sending 250 OK after the terminating DATA sequence accepts responsibility to deliver or relay the message. A failure discovered after that acceptance is reported separately to the envelope return path. The application therefore has evidence of one completed SMTP handoff, not final inbox placement.

Proved
The receiving SMTP hop accepted responsibility for this message transaction.
Not proved
The destination mailbox exists, every recipient succeeded, the final recipient server accepted the message, or a user can see it in an inbox.
Immediate failure
A 4yz or 5yz response before final acceptance remains a synchronous submission result for the affected recipient or transaction.
Later failure
A DSN, NDR, or verified provider event can change an accepted attempt to delayed or bounced after the application request has returned.

Use an evidence-based state model

Avoid a single boolean named sent or delivered. Keep transport evidence and user-visible evidence separate, with timestamps and the source of each transition.

  1. pending: the application stored a stable notification and recipient-attempt ID before submission.
  2. submission_failed: the local client or SMTP hop rejected the attempt synchronously.
  3. accepted_by_relay: the SMTP transaction received final positive completion. This is the honest meaning of the initial success.
  4. delivery_delayed or bounced: an authenticated asynchronous event reports a temporary or permanent downstream outcome.
  5. recipient_server_accepted: a provider event or success DSN explicitly supports that narrower claim. It still does not prove inbox placement.
  6. inbox_visible: separate recipient-side evidence, when the product can obtain it lawfully and safely.

Do not present an MX lookup as mailbox verification. MX proves that a domain advertises mail routing. It does not prove that a particular local part exists or accepts mail.

Return every synchronous recipient result

In a batch loop, do not let the first send error discard the outcomes for later recipients. Store each recipient attempt first, submit independently, and return a bounded result for every attempted recipient. Do not expose full addresses in logs or public responses.

Stable key
Use an opaque notification ID plus recipient-attempt ID generated before sending.
Success value
Return accepted_by_relay with the SMTP or provider transaction identifier, not "email delivered."
Failure value
Return a classified synchronous error per recipient while continuing the batch according to an explicit policy.
Retry rule
Make transitions idempotent and retry only transient failures. A duplicate webhook or DSN must not duplicate notifications or reverse a terminal state.

Correlate asynchronous outcomes

Use an opaque token in the envelope return path when VERP-style correlation is available. If the relay advertises the SMTP DSN extension, ENVID identifies the transaction and ORCPT preserves the original recipient for the DSN. RFC 3464 explicitly separates Original-Envelope-Id from the message header's Message-ID, so Message-ID should not be the only correlation key.

  • For provider webhooks, verify the signature before parsing, map the provider message ID to the stored attempt, and deduplicate by a stable provider event ID.
  • For mailbox-delivered DSNs, parse message/delivery-status with bounded MIME depth, body size, field count, and UTF-8 handling.
  • Reject forged, unmatched, stale, or invalid transitions. Record the evidence source and observed time without retaining unnecessary message content.
  • Keep recipient addresses, message bodies, verification links, OTPs, credentials, and raw provider payloads out of analytics and public diagnostics.

A safe implementation sequence

  1. Rename the current synchronous success to accepted_by_relay.
  2. Persist one stable attempt per recipient before calling the mailer.
  3. Collect all per-recipient send results instead of aborting the batch at the first error.
  4. Add a monitored envelope return path or authenticated provider webhook and correlate it to the attempt.
  5. Apply idempotent delayed, bounced, and recipient-server-accepted transitions.
  6. Notify the original application actor with bounded status text; never include another recipient's result.
  7. Test immediate 4yz/5yz failures, delayed DSNs, permanent DSNs, duplicate events, forged events, partial batches, and retry races.

See the provider-event boundary map when a delivery service supplies queued, delayed, bounced, or delivered events. For a later NDR containing a three-digit or enhanced status code, decode the bounded diagnostic locally.

Primary references