new-site/scripts/_email_exclusions.py
justin b973c6c132 exclusions: block Google consumer mailboxes (gmail) from cold/warmup sends
Warmup audit (2026-06-08) found the main sending pool was eating a 37% bounce
rate, and 556 of those were Google 550-5.7.1 'likely unsolicited mail' spam
blocks -- of which gmail.com alone was 427 (77%). Google's cold-IP filter is the
strictest of the big providers and consumer gmail has the highest complaint
sensitivity, so mailing it from a warming IP is pure reputation damage.

Added GOOGLE_CONSUMER_DOMAINS (gmail.com, googlemail.com) to BLOCKED_EMAIL_DOMAINS,
which the daily trucking builder already enforces in its recipient SQL
(lower(domain) <> ALL(blocked)). Takes effect on the next nightly build.

Custom domains silently on Google Workspace are a smaller (~5%) MX-only signal,
already handled in the healthcare builder via the mx_provider flag; can be ported
to the main pool later if the residual warrants it.
2026-06-08 13:50:27 -05:00

62 lines
2.8 KiB
Python

"""Shared recipient-domain exclusions for outbound cold-email campaigns.
We self-host our MTA (transactional relays like SES forbid cold email), so we
must protect our sending-IP reputation manually. The two biggest levers:
1. NOT mailing the Yahoo/Verizon-Media family: those providers aggressively
defer cold senders with "unexpected volume / user complaints" 421
responses, which poisons the IP for every other provider too.
2. NOT mailing Google CONSUMER mailboxes (gmail.com etc.) from a cold/warming
IP: Google hard-rejects them with 550-5.7.1 "this message is likely
unsolicited mail", and those rejections are reputation-damaging. (On
2026-06-08 a warmup audit found gmail.com alone was 77% of our 550-5.7.1
blocks -- 427 of 556.) Custom domains hosted on Google Workspace are a
smaller, MX-only signal handled separately in the per-vertical builders.
Keep this list authoritative and import it everywhere we build audiences.
"""
from __future__ import annotations
# Yahoo / Verizon Media operates ALL of these consumer domains. Legacy AT&T and
# Frontier consumer mail was handed off to Yahoo's infrastructure as well.
YAHOO_FAMILY_DOMAINS: frozenset[str] = frozenset({
# Yahoo / AOL core
"yahoo.com", "yahoo.com.mx", "yahoo.es", "yahoo.it", "yahoo.ca",
"myyahoo.com", "ymail.com", "rocketmail.com",
"aol.com", "aol.com.mx", "aim.com", "love.com", "games.com", "wow.com",
"netscape.net", "netscape.com", "cs.com", "compuserve.com",
# AT&T family (Yahoo-hosted)
"att.net", "sbcglobal.net", "bellsouth.net", "pacbell.net",
"ameritech.net", "swbell.net", "snet.net", "flash.net", "prodigy.net",
"wans.net", "nvbell.net",
# Verizon family (Yahoo-hosted)
"verizon.net", "verizongni.com", "bellatlantic.net",
# Frontier (Yahoo-hosted)
"frontier.com", "frontiernet.net",
})
# Google consumer mailboxes. Google's cold-IP spam filter (550-5.7.1) is the
# strictest of the big providers; consumer gmail accounts have the highest
# complaint sensitivity. We hold these out of cold/warmup sends. (This is the
# domain-string layer; custom domains silently on Google Workspace need an MX
# lookup and are handled in the per-vertical builders, e.g. the healthcare
# mx_provider flag.)
GOOGLE_CONSUMER_DOMAINS: frozenset[str] = frozenset({
"gmail.com", "googlemail.com",
})
# The full set of consumer domains we refuse to cold-mail. Extend here as we
# discover other reputation-sensitive providers.
BLOCKED_EMAIL_DOMAINS: frozenset[str] = YAHOO_FAMILY_DOMAINS | GOOGLE_CONSUMER_DOMAINS
def domain_of(email: str) -> str:
"""Return the lowercased domain part of an email, or '' if malformed."""
if "@" not in email:
return ""
return email.rsplit("@", 1)[-1].strip().lower()
def is_blocked(email: str) -> bool:
return domain_of(email) in BLOCKED_EMAIL_DOMAINS