Outcomes

Every verification carries two independent things, on two fields, and it is worth being precise about which is which:

Field Answers Values
status Has it finished? pending, complete
result.outcome What did we conclude? deliverable, invalid, catch-all, unknown

result (and recovery) are null while status is pending — a check that has not finished has reached no conclusion, and unknown is a conclusion rather than the absence of one.

This page is about the second column. It applies identically everywhere a verdict is written down: single-verify responses, poll results, list rows, webhook payloads, the batch CSV's Outcome column, and the dashboard.

On this page

result.outcome values

Value Meaning
deliverable Mailbox confirmed deliverable via SMTP
invalid Mailbox does not exist or hard-rejected
catch-all Domain accepts all addresses; deliverability unconfirmable
unknown Could not determine — DNS failure, transient error, or no SMTP probe data

sub_status values

sub_status provides additional signal within a top-level status. It is an empty string ("") when none of the conditions below apply.

Value Meaning
disposable Address belongs to a known disposable / temporary email provider
role_based Address is a role alias (e.g. admin@, support@, noreply@)
accept_all Domain is a catch-all — every address appears to succeed
no_dns_entries No MX records found and DNS did not return a transient error
mailbox_not_found SMTP server returned 550 or 551 (user unknown)
greylisted SMTP server returned a 4xx temporary rejection — may be greylisting
mail_server_did_not_respond DNS resolved but SMTP connection timed out or was refused

Sub-status derivation is first-match: disposable > role_based > accept_all > no_dns_entries > mailbox_not_found > greylisted > mail_server_did_not_respond.

risk

risk is a single planning signal for when you want one field rather than a decision table. It is assessed at probe time, from everything the check observed — including things this response does not otherwise report to you.

Value What it means
none A confirmed mailbox with no adverse signal
low A free-mail domain, or an address we could not verify conclusively
medium A catch-all domain, a role address, a non-ASCII local part, or a mailbox whose probe was deferred by the server
high A definitive rejection, a disposable domain, a domain with no mail server, or a structurally suspect address (a quoted or commented local part)

It is not a function of outcome. Several of its inputs — the SMTP conversation's exact status, the domain's mail-server state, DNS transience, and the address's own syntax — appear nowhere else on the response, so risk will sometimes say more than outcome and details together do. The clearest case is a deferred mailbox: the server answered with a 4xx temporary failure, so outcome is unknown and sub_status is greylisted, and risk is medium because the mailbox is genuinely uncertain rather than merely unchecked. Read the four values as an ordered scale, not as a formula over the other fields.

It is present on every completed result, live and test-mode alike. It is omitted whenever the check carries no recorded assessment. That rule is wider than the one for free_email and details, which are withheld only when the stored probe detail cannot be read: risk is also absent on a healthy row that has no stored detail at all — a check completed before risk became a recorded value, until it is backfilled — as well as while a check is still pending. An absent risk means unknown, never none.

Deliverability guidance

result.outcome Use in mailing list?
deliverable Yes
invalid No — remove from list
catch-all Use with caution; domain accepts all mail but individual mailboxes may not exist
unknown Treat with caution; retry later if possible

recovery

When the address you sent is dead, we sometimes find a live alternative for the same contact. When we do, we probe it before telling you about it — so recovery is never a spelling guess, and you never have to decide whether to trust it.

{
  "object":     "verification",
  "id":         "1c9d2f80-4f3a-4c19-9a51-52a1a4e0c001",
  "created_at": "2025-06-10T14:07:44.010Z",
  "livemode":   true,
  "status":     "complete",
  "email":      "alice@gmial.com",
  "result": {
    "outcome":    "invalid",
    "sub_status": "no_dns_entries",
    "risk":       "high",
    "free_email": false,
    "account":    "alice",
    "domain":     "gmial.com",
    "details":    { "...": "..." }
  },
  "recovery": {
    "email":   "alice@gmail.com",
    "outcome": "deliverable"
  },
  "completed_at":      "2025-06-10T14:07:48.220Z",
  "credits_remaining": 4819
}

recovery.email is always a complete address — both the local part and the domain are considered, and whichever did not change is carried through verbatim — and it is always an addition. email still holds exactly what you submitted; nothing is ever silently substituted.

recovery.outcome uses the same vocabulary as result.outcome, so there is nothing new to learn:

Value Meaning
deliverable A probe confirmed a mailbox at the recovered address
catch-all The fix landed on a domain that accepts everything; the mailbox itself is unconfirmable

There is deliberately no confidence field. The only decision you have about a recovered address is whether to mail it, and outcome is the field that answers it — a separate certainty tier would be our language about our own workings, mapping to nothing you can do.

recovery is null unless a probe resolved something unambiguous. In particular it is null when several different candidates came back deliverable, or several different domains came back catch-all: a guess between them is exactly the "wrong person" risk this feature exists to avoid.

A recovery is billable work. A definitive outcome or a recovery is what you pay for. So an unknown result carrying a recovery is charged, and an unknown with no recovery is refunded — if you see a charge against a non-definitive result, recovery in that same response is the reason.

The batch CSV carries the same two facts, in the same words, as the RecoveredEmail and RecoveredOutcome columns.

catch-all vs catch_all — the naming convention

Both spellings appear in a result, and the difference is not an inconsistency. It is the API's naming rule:

Spelling What it is
catch-all (hyphen) A value — the result.outcome enum member, sharing one vocabulary with the CSV export, the dashboard and this documentation
catch_all (underscore) A field namedetails.catch_all, and catch_all_count in a batch summary

The rule: values are hyphenated, field names are snake_case. Every field on the wire is snake_case (sub_status, free_email, created_at, mx_record), and the outcome vocabulary is a single shared set of words used identically everywhere a verdict is written down.

Unifying the two would break one of those two guarantees, so both spellings are permanent. When you write a comparison, match result.outcome == "catch-all" and read result.details.catch_all.

The full result object

{
  "object":     "verification",
  "id":         "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "created_at": "2025-06-10T14:02:09.180Z",
  "livemode":   true,
  "status":     "complete",
  "email":      "alice@example.com",
  "result": {
    "outcome":    "deliverable",
    "sub_status": "",
    "risk":       "none",
    "free_email": false,
    "account":    "alice",
    "domain":     "example.com",
    "details": {
      "role":          false,
      "disposable":    false,
      "catch_all":     false,
      "mx_found":      true,
      "mx_record":     "mail.example.com",
      "smtp_provider": "Google",
      "smtp_code":     250
    }
  },
  "recovery":          null,
  "completed_at":      "2025-06-10T14:02:11.432Z",
  "credits_remaining": 4820
}

credits_remaining is null for system and demo accounts, and also on list rows — a balance is a per-request figure, so read it from GET /api/v1/credits.