Skip to content

The five verdicts

status is the technical conclusion. There are five values and there is deliberately no sixth, and no boolean underneath them.

status What it means Typical action
deliverable Mail is routable and a destination accepted this recipient, with no overriding risk signal. allow
undeliverable A deterministic failure: invalid syntax, a domain that does not exist, a domain that refuses all mail, or a mailbox-not-found from a provider whose signal we have reason to believe. reject
risky The recipient may be accepted, but the evidence is weak or the business risk is elevated — a disposable domain, or a domain that accepts everything. allow_with_email_confirmation
unknown No safe determination was possible. allow_with_email_confirmation
pending Recipient evidence is queued or in flight and you did not wait for it. Not a verdict — poll for one.

A 250 to RCPT TO does not prove a mailbox exists. It can come from a domain that accepts every recipient, a gateway that validates after acceptance, a provider that hides mailbox existence as a matter of policy, or a system that accepts now and bounces later. A 550 does not prove a mailbox does not exist either — 5.7.x is usually a decision about the sender.

Collapsing that into exists: true|false would be a claim the protocol cannot support. Everything else on this page follows from refusing to make it.

status is what the evidence says. action is what a registration flow should probably do about it. They are separate fields because different products weigh the same evidence differently: a disposable domain is a hard no for a paid trial and completely fine for a newsletter.

There are three actions and there is deliberately no fourth:

  • allow — proceed.
  • allow_with_email_confirmation — proceed, behind a confirmation message. This is the answer to almost everything uncertain, and it is not a hedge: confirmation is the only thing that establishes control of a mailbox anyway.
  • reject — refuse.

Nothing more interactive than that is recommended, because anything more is your layer rather than ours.

unknown is the correct answer far more often than it feels like it should be. Policy blocks, timeouts, greylisting, TLS failures and providers that decline to answer all produce it, and every one of them is a statement about the conversation rather than about the person signing up.

The service is built to fail open: when a dependency is unavailable, it degrades to unknown and keeps answering rather than failing. An integration that treats unknown as undeliverable inverts that — it converts our bad afternoon into your lost signups, and it does so silently, because a refused registration leaves no trace on either side.

Use reason_codes if you want to distinguish kinds of unknown. Every code is tagged with what it is evidence about, and four of the groups say plainly that they are about us and not about the address: see Reason codes.

The verdict is a summary. checks reports each question separately:

Field Values
syntax valid, invalid
domain valid, invalid, unknown
mail_routing valid, invalid, unknown
smtp accepted, rejected, skipped, unknown
accept_all yes, no, unknown
disposable boolean
role_account boolean
smtp_utf8_required boolean

skipped and unknown under smtp are not the same thing, and the difference is worth branching on: skipped means no probe was sent — we declined, or you bought the shallower depth — while unknown means a conversation happened and established nothing.

id names one conclusion. A terminal result never changes for a given identifier: re-checking an address that does new work produces a new identifier or a new generation, so you are never contradicted about something you were already told. A re-check answered from cache keeps the original identifier, which is the same rule from the other side — the same conclusion keeps the same name.