agentleFS
press-passpress-pass/journalism-skills/skills/verify-email/SKILL.md

verify-email

Skill2 starsChanged 7 months agoMITView on GitHub

Verify whether email addresses are reachable without sending an email. Checks syntax, DNS/MX records, disposable domains, and SMTP mailbox existence.

  • Sends data out

The file

Show its rules and text

We split this skill into the rules it gives an agent

30 rules. About 0 are common, 17 are distinctive, and 13 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈17

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 13

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Verify the email matches a valid format: one @, valid local part (no leading/trailing dots, no consecutive dots), and a valid domain (valid …

    line 10
  2. Distinctive:

    If the syntax is invalid, mark the email as Invalid (bad syntax) and skip remaining steps.

    line 13
  3. Very specific:

    Run dig +short <domain> MX via Bash.

    line 17
  4. Very specific:

    If no MX records are returned, try dig +short <domain> A as a fallback (some domains accept mail on their A record).

    line 19
  5. Distinctive:

    If neither returns results, mark as Invalid (no mail server) and skip remaining steps.

    line 20
  6. Distinctive:

    If MX records exist, note the lowest-preference (primary) MX server for Step 4.

    line 21
  7. Distinctive:

    Check whether the domain is a known disposable/temporary email provider.

    line 25
  8. Distinctive:

    If disposable, flag it as Warning: disposable email provider but continue to Step 4.

    line 29
  9. Distinctive:

    Run this Bash script, substituting the email and MX server:

    line 35
  10. Very specific:

    Connection refused/timeout: check (once per run) whether outbound port 25 works at all: timeout 8 bash -c 'exec …

    line 79
  11. Distinctive:

    If that fails, mark as Indeterminate (port 25 blocked locally).

    line 81
  12. Very specific:

    After the RCPT TO check, if the result was 250, test whether the domain is catch-all by checking a clearly fake address: …

    line 88
  13. Very specific:

    Call browser_navigate to https://account.live.com/password/reset

    line 99
  14. Very specific:

    Call browser_snapshot to find the email input field.

    line 100
  15. Very specific:

    Call browser_fill_form to enter the email address into the email input.

    line 101
  16. Very specific:

    Call browser_click on the "Next" button.

    line 102
  17. Very specific:

    Wait 3 seconds, then call browser_snapshot and check:

    line 103
  18. Distinctive:

    If the page contains text like "that Microsoft account doesn't exist" or "No account found with that email" → Unreachable (account not …

    line 104
  19. Distinctive:

    If the page shows identity verification options ("verify your identity", "send a code", "authenticator app") → Reachable (account exists)

    line 105
  20. Distinctive:

    If a CAPTCHA appears, note it and mark as Indeterminate (CAPTCHA blocked)

    line 106
  21. Very specific:

    Call browser_snapshot to find the email/phone input field.

    line 111
  22. Very specific:

    Call browser_fill_form to enter the email address.

    line 112
  23. Very specific:

    Call browser_click on "Continue".

    line 113
  24. Distinctive:

    If the page shows an error message like "Sorry, we don't recognize that email" → Unreachable (account not found)

    line 115
  25. Distinctive:

    If the page shows verification options (send a code, security key, etc.) → Reachable (account exists)

    line 116
  26. Distinctive:

    Before using the headless browser, try the faster API check:

    line 121
  27. Distinctive:

    Use the SMTP RCPT TO check from Step 4.

    line 141
  28. Distinctive:

    After the table, briefly explain any indeterminate results and suggest next steps (e.g., "send a confirmation email to verify" for …

    line 158
  29. Distinctive:

    Before presenting results, inform the user of the following:

    line 163
  30. Distinctive:

    No technique — free or paid — can definitively verify a specific mailbox on a catch-all domain without sending.

    line 171

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

You will be given one or more email addresses (comma-separated). For each email address, run the verification pipeline below. Report results in a summary table at the end.

Step 1: Syntax check

Verify the email matches a valid format: one @, valid local part (no leading/trailing dots, no consecutive dots), and a valid domain (valid hostname characters, at least one dot).

If the syntax is invalid, mark the email as Invalid (bad syntax) and skip remaining steps.

Step 2: DNS/MX record check

Run dig +short <domain> MX via Bash. Inspect the results:

  • If no MX records are returned, try dig +short <domain> A as a fallback (some domains accept mail on their A record).
  • If neither returns results, mark as Invalid (no mail server) and skip remaining steps.
  • If MX records exist, note the lowest-preference (primary) MX server for Step 4.

Step 3: Disposable email check

Check whether the domain is a known disposable/temporary email provider. Search the web for "is <domain> a disposable email" or check against well-known disposable domains (mailinator.com, guerrillamail.com, tempmail.com, throwaway.email, yopmail.com, sharklasers.com, 10minutemail.com, trashmail.com, etc.).

If disposable, flag it as Warning: disposable email provider but continue to Step 4.

Step 4: SMTP RCPT TO verification

This step connects to the mail server and asks if the mailbox exists without sending an email.

Run this Bash script, substituting the email and MX server:

{
  exec 3<>/dev/tcp/<mx_server>/25
  timeout=5

  # Read greeting
  read -t $timeout -u 3 greeting
  echo "GREETING: $greeting"

  # HELO
  echo -e "HELO verify.local\r" >&3
  read -t $timeout -u 3 helo_resp
  echo "HELO: $helo_resp"

  # MAIL FROM
  echo -e "MAIL FROM:<verify@verify.local>\r" >&3
  read -t $timeout -u 3 mail_resp
  echo "MAIL FROM: $mail_resp"

  # RCPT TO - the actual verification
  echo -e "RCPT TO:<EMAIL_ADDRESS>\r" >&3
  read -t $timeout -u 3 rcpt_resp
  echo "RCPT TO: $rcpt_resp"

  # QUIT
  echo -e "QUIT\r" >&3

  exec 3<&-
  exec 3>&-
} 2>&1

Interpret the RCPT TO response — match the response text as well as the code:

  • 250: Mailbox exists (or domain is catch-all — see below)
  • Text mentions "full", "quota", "blocks limit", or "inode limit" (any code, typically 452/552): the mailbox exists but is over quota — mark as Risky (mailbox full)
  • Text mentions "relay", "relaying", "not local", or "authentication required" (any code, e.g. 551 User not local: authentication required for relaying): the server rejected the probe, not the mailbox — this says nothing about whether the address exists — mark as Indeterminate (relay denied)
  • 550/551/553 (no relay/quota wording): Mailbox does not exist — mark as Invalid (mailbox not found)
  • 450/451/452 (no quota wording): Temporary rejection (greylisting) — mark as Indeterminate (greylisted)
  • Connection refused/timeout: check (once per run) whether outbound port 25 works at all: timeout 8 bash -c 'exec 3<>/dev/tcp/gmail-smtp-in.l.google.com/25 && read -t 5 -u 3 greeting && echo "$greeting"'. If that fails, mark as Indeterminate (port 25 blocked locally). If it succeeds and the domain was A-record-only (no MX — the A record usually points to a web server, not a mail server), mark as Invalid (no mail server); otherwise mark as Indeterminate (server unreachable)
  • Any other error: Note the code and mark as Indeterminate

Catch-all detection

After the RCPT TO check, if the result was 250, test whether the domain is catch-all by checking a clearly fake address: does-not-exist-xyz-99@<domain>. If that also returns 250, the domain accepts all addresses and the result is Indeterminate (catch-all domain).

Step 5: Provider-specific headless browser verification

For providers that defeat SMTP verification, use Playwright to check account existence via their password recovery flows. Skip the SMTP check (Step 4) for these providers and use this step instead.

Microsoft (outlook.com, hotmail.com, live.com, msn.com)

  1. Call browser_navigate to https://account.live.com/password/reset
  2. Call browser_snapshot to find the email input field.
  3. Call browser_fill_form to enter the email address into the email input.
  4. Call browser_click on the "Next" button.
  5. Wait 3 seconds, then call browser_snapshot and check:
    • If the page contains text like "that Microsoft account doesn't exist" or "No account found with that email" → Unreachable (account not found)
    • If the page shows identity verification options ("verify your identity", "send a code", "authenticator app") → Reachable (account exists)
    • If a CAPTCHA appears, note it and mark as Indeterminate (CAPTCHA blocked)

Yahoo (yahoo.com, ymail.com, aol.com, yahoo.co.uk, yahoo.ca, etc.)

  1. Call browser_navigate to https://login.yahoo.com/forgot
  2. Call browser_snapshot to find the email/phone input field.
  3. Call browser_fill_form to enter the email address.
  4. Call browser_click on "Continue".
  5. Wait 3 seconds, then call browser_snapshot and check:
    • If the page shows an error message like "Sorry, we don't recognize that email" → Unreachable (account not found)
    • If the page shows verification options (send a code, security key, etc.) → Reachable (account exists)
    • If a CAPTCHA appears, note it and mark as Indeterminate (CAPTCHA blocked)

Microsoft 365 (business domains with MX pointing to outlook.com or *.mail.protection.outlook.com)

Before using the headless browser, try the faster API check:

Run via Bash:

curl -s -X POST 'https://login.microsoftonline.com/common/GetCredentialType' \
  -H 'Content-Type: application/json' \
  -d '{"Username": "EMAIL_ADDRESS"}' | python3 -c "import sys,json; r=json.load(sys.stdin); print('EXISTS' if r.get('IfExistsResult')==0 else 'NOT_FOUND' if r.get('IfExistsResult')==1 else f'UNKNOWN({r.get(\"IfExistsResult\")})')"
  • EXISTS → Reachable (Microsoft 365 account exists)
  • NOT_FOUND → Unreachable (account not found)
  • UNKNOWN → Fall back to the Microsoft headless browser check above

iCloud (icloud.com, me.com, mac.com)

Apple does not expose account enumeration in password recovery flows. For iCloud addresses, SMTP verification is unreliable (catch-all behavior) and no headless workaround exists. Mark as Indeterminate (Apple — cannot verify).

All other providers

Use the SMTP RCPT TO check from Step 4.

Output

Present a summary table:

EmailSyntaxMX RecordDisposableVerification MethodResultVerdict

Verification Method should indicate what was used: SMTP, Headless (Microsoft), Headless (Yahoo), API (M365), or N/A.

Verdicts:

  • Reachable: Account confirmed to exist via SMTP 250 (non-catch-all) or headless browser / API check
  • Unreachable: Failed syntax, MX, SMTP returned 550, or headless browser confirmed account does not exist
  • Risky: Disposable email provider (may still be reachable)
  • Indeterminate: Could not confirm (catch-all, greylisted, CAPTCHA blocked, or Apple iCloud)

After the table, briefly explain any indeterminate results and suggest next steps (e.g., "send a confirmation email to verify" for indeterminate addresses).

Limitations — tell the user about these

Before presenting results, inform the user of the following:

  • This is not 100% reliable. Even commercial email verification services (ZeroBounce, NeverBounce, etc.) only achieve ~95-99% accuracy, and they use rotating IP pools, ML models trained on billions of bounce logs, and proprietary ESP data partnerships that we don't have.
  • iCloud addresses (icloud.com, me.com, mac.com) cannot be verified. Apple blocks all enumeration techniques. The only way to confirm an iCloud address is to send an actual email.
  • Catch-all domains are fundamentally unverifiable. ~20-30% of business domains accept mail for any address. No technique — free or paid — can definitively verify a specific mailbox on a catch-all domain without sending.
  • SMTP checks may fail from this machine. Port 25 is blocked by many ISPs and cloud providers. If the SMTP step times out or is refused, it may be a network issue rather than an invalid address.
  • Headless browser checks depend on provider UI. Microsoft and Yahoo can change their password recovery pages at any time, which would break the headless verification. If the page structure looks unexpected, note this.
  • CAPTCHAs may block headless checks. Repeated use from the same IP will trigger CAPTCHAs on Microsoft and Yahoo, degrading results to Indeterminate.
  • A "Reachable" result means the account exists, not that someone reads it. The mailbox could be abandoned, full, or filtered. The only true confirmation is a reply from the recipient.

Email addresses to verify: $ARGUMENTS

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.