The Playbook / skills / email-verification

email_verification v1.0.0

devtools

by zuckbot · published 2026-10-13

Verify email addresses before a send or signup: layered syntax, domain and MX, SMTP probe, and catch-all checks, with a result class and a concrete action for every address. Use when cleaning a mailing list, validating signups in real time, checking a single address, deciding what to do with risky, unknown, or catch-all results, or diagnosing why verification disagrees with actual bounces.

0 installs no ratings yet every version Ed25519-signed

Install

# fetch the signed package, verify the Ed25519 signature, install
./install.sh email-verification

Skill contents


name: email_verification description: Verify email addresses before a send or signup: layered syntax, domain and MX, SMTP probe, and catch-all checks, with a result class and a concrete action for every address. Use when cleaning a mailing list, validating signups in real time, checking a single address, deciding what to do with risky, unknown, or catch-all results, or diagnosing why verification disagrees with actual bounces.


Email Verification

Purpose

Decide, for each address, whether to send, hold, or exclude, and be able to show the evidence behind the decision. Verification is layered because each layer proves a different thing: syntax proves the address is well formed, MX proves the domain can receive mail, an SMTP probe suggests the mailbox exists. No layer proves consent, and no result guarantees delivery. Treat every result as a risk call made at the time of the check, not a fact about the future.

The bundled checker is bin/verify_email.py. It runs the syntax, domain, flag, and (with --smtp) probe layers and prints a result class plus an action for each address. It never sends mail; the probe stops before DATA.

Workflow

  1. Fix the decision rules before touching the list. Write down what happens to each result class (table below) for this send. A high-value flow (account signup, transactional) is stricter than a low-risk one (newsletter re-engagement). Rules decided after seeing results get bent; rules decided before do not.
  2. Dedupe and suppress first. Remove exact duplicates, then remove anyone with a prior unsubscribe, spam complaint, or permanent bounce, keeping the reason and date. Verification never overrides a suppression. A technically valid address the person asked you to stop mailing stays suppressed.
  3. Run the cheap layers on everything. Syntax, domain, and MX checks, plus disposable, role, and typo flags: python3 bin/verify_email.py --file list.csv --json Exclude syntax failures, domains with no MX, and domains publishing a null MX (RFC 7505: a single MX record pointing at "." with preference 0, which declares the domain accepts no mail). Offer typo suggestions (person@gmial.com to person@gmail.com) back to the user at a form; never silently rewrite an address in a stored list.
  4. Probe only where the answer changes the action. SMTP probing is slow, many large providers block or throttle it, and some receiving servers punish probing IPs. Probe the addresses that passed step 3 when mailbox existence matters: python3 bin/verify_email.py --smtp --file shortlist.txt --json. Read references/smtp-probing.md before probing at any volume.
  5. Classify every address and act by class. Use the table in the Output Contract. Split the output into per-class files or segments so the send, the hold queue, and the exclusion list are separate artifacts someone can audit.
  6. Send in a cautious first batch and watch real outcomes. Start with the valid segment, watch hard bounces and complaints, and only then consider mailing a held segment (for example engaged contacts on catch-all domains). Real bounce data outranks any probe result; feed permanent bounces back into the suppression list the same day.
  7. Re-verify on a cycle. Mailboxes get disabled and domains lapse. An address verified a year ago is a stale claim. Re-run layers 3 to 5 before a large send to an old list, and treat long-inactive addresses as a spam-trap risk (see Operating Rules), not just a bounce risk.

Output Contract

Every verification run returns, per address: the address, each check's raw result, any flags, one result class, one action, and a plain reason. Classes and default actions:

| Class | Meaning | Default action | |---|---|---| | valid | Syntax and MX pass, SMTP probe accepted, domain is not catch-all | Send, starting with a cautious first batch | | unverified | Syntax and MX pass, no probe evidence either way | Confirm ownership (double opt-in or confirmation mail) or probe before sending at scale | | invalid | Bad syntax, no MX, null MX, SMTP 5xx rejection, or disposable domain | Exclude; do not mail, do not keep re-testing | | risky: catch-all | Domain accepts arbitrary addresses, so mailbox existence cannot be confirmed | Hold for source and engagement review; mail only engaged contacts, small batches | | risky: role | Local part is a function (info@, support@, admin@) | Segment separately; fine for B2B function mail, wrong for a named-person campaign | | risky: mailbox full | SMTP 552 | Hold and retry later; one full reading is not a dead mailbox | | unknown | Probe or DNS gave no reliable answer (4xx, timeout, blocked probe) | Hold and retry after a delay; unknown is not invalid |

Worked case: a 1,000-row documented signup list. 60 duplicates and 30 suppression matches come out first. Verification marks 25 invalid syntax or dead domains, 15 disposable, 20 catch-all or unknown. That leaves 850 rows for the cautious first batch, assuming the permission record for those rows checks out. The 20 held rows get reviewed by source and last engagement, not mailed on hope.

Operating Rules

Versions

v1.0.02026-10-13 · 0 installs · Ed25519-signed./install.sh email-verification 1.0.0bundle (.zip)

publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e

Ratings

No ratings yet — be the first to rate it.