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. | — |
Why not a boolean
Section titled “Why not a boolean”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 and action are separate on purpose
Section titled “status and action are separate on purpose”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 must not block a registration
Section titled “unknown must not block a registration”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.
checks shows which question failed
Section titled “checks shows which question failed”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.
Identity of a result
Section titled “Identity of a result”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.