Handling results
Four verdicts, one switch. Every response carries suggested_action with this exact mapping, so the first integration is already correct; the reasons are there when you want finer policy.
| verdict | suggested_action | Why |
|---|---|---|
| deliverable | accept | The mailbox answered. Let the user through. |
| undeliverable | reject | No mail server, or the server rejected the mailbox permanently, or the address is malformed. Show the reason in plain words: "that domain can't receive email", "that mailbox does not exist". |
| risky | accept_and_confirm | A fact you cannot act on alone: the domain accepts every address, the mailbox is full, or it is disposable. Let the user through and confirm by email. The disposable case is the exception: suggested_action says reject, because the mailbox is real and the user is not staying. |
| unknown | accept_and_confirm | The server would not say (a "come back later", a block on our IP, a tarpit) or the time ran out. Not billed. Let the user through and confirm by email, or retry later; never reject a real person on a maybe. |
The switch
switch (data.verdict) {
case 'deliverable': return accept(email);
case 'undeliverable': return reject(email, data.reason); // e.g. "no_mail_server"
case 'risky': // catch-all, full mailbox, disposable
case 'unknown': // the server would not say; not billed
return data.suggested_action === 'reject' ? reject(email, data.reason) : acceptAndConfirm(email);
}Typos
When did_you_mean is not empty, offer it as a one-click correction before anything else: Did you mean sara@gmail.com? It is only ever a free-provider domain within two characters of what was typed, so it is never wrong by much.
Flags for your own policy
flags.disposable, flags.role_account and flags.free_provider are facts, not verdicts. A B2B lead form may reject free providers; a newsletter may not care. A free-trial signup rejects disposable addresses; a support form should not. Decide once, in your code.
When you cannot wait
Pass timeout (milliseconds, 1000–45000). If it runs out you get unknown with the reason timeout, unbilled, and the switch above already handles it. Two seconds is a reasonable ceiling for a signup form; a lead form that posts in the background can afford the full 45.
Explaining a rejection
Add evidence=true when you log verifications. When a user writes in, the rcpt_to reply in the evidence is what their mail server said, verbatim, and it settles the question in seconds.