On August 12, I handed an AI agent a CSV of fifty email addresses and an SMTP tool. Thirty-eight minutes later, twelve of them had bounced.
I wasn't trying to break anything. I was testing whether a local agent could handle a simple outreach task end-to-end: read a list, draft a short note, send it. The model I used was Muse Glimmer, Meta's 30-billion-parameter open-weights agent model that dropped on August 10, 2026. It's small enough to run on a single consumer GPU, which means agents like this are about to be everywhere—on your laptop, inside a Docker Sandbox, or wired into a multiplayer harness like qm. More tools, more autonomy, more chances for a bad send.
The agent saw send_email
as just another function call. It didn't ask whether the addresses were real, reachable, or trustworthy. It pinged SMTP, got a handful of 250 OK
greetings, and fired. One of those addresses was test@gmail.com
. SMTP said yes. The gatekeeper I built afterward said no.
Here's the gatekeeper I wish I'd put in front of the mailer first:
import requests
import json
import sys
RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY"
URL = "https://email-validator112.p.rapidapi.com/validate"
def gatekeep(email):
r = requests.get(
URL,
headers={
"X-RapidAPI-Key": RAPIDAPI_KEY,
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
},
params={"email": email},
timeout=10
)
r.raise_for_status()
data = r.json()
verdict = "SEND" if data.get("is_trusted_identity") else "BLOCK"
print(json.dumps(data, indent=2))
print(f"\nverdict: {verdict}")
return verdict
if __name__ == "__main__":
gatekeep(sys.argv[1] if len(sys.argv) > 1 else "test@gmail.com")
When I ran that against test@gmail.com
, SMTP came back verified. The composite trust score came back false. The agent would have sent. The gatekeeper didn't.
The response I got back is worth reading in full:
{
"email": "test@gmail.com",
"is_valid": true,
"is_disposable": false,
"is_free_email": true,
"provider": "Google",
"mx_record": "gmail.com",
"smtp_verified": true,
"is_catch_all": false,
"breached": true,
"breach_count": 3,
"first_breach": "2014-01-01",
"last_breach": "2023-05-15",
"is_trusted_identity": false,
"syntax_suggestion": null
}
That's a lot of signal for one HTTP call. The first thing that jumps out is the contradiction: smtp_verified
is true
, but is_trusted_identity
is false
. If your agent is only checking SMTP, it treats this address as deliverable and moves on. The gatekeeper treats it as risky, because the same address has appeared in three separate breach dumps spanning from January 1, 2014 to May 15, 2023.
Three breaches. Nine years between the first and last. A free Gmail account on Google's MX. Not disposable, not catch-all, syntactically valid. The kind of address an agent would happily email all day.
The is_trusted_identity
composite is the detail that makes this API interesting as a gatekeeper. It doesn't just verify reachability; it folds in breach status and disposable detection. You can't easily reconstruct that from public docs alone because the weights matter: is one breach enough to block? Two? Three? The API made a call here and said no. That's a policy decision wrapped in a field, and it's exactly the kind of thing I want between an agent and my mailer.
provider
and mx_record
give you routing identity. For test@gmail.com
, the provider ID is Google
and the MX resolves to gmail.com
. That's useful beyond validation. In a B2B lead-scoring flow, is_free_email: true
is a negative signal; in a consumer signup flow, it's neutral or positive. The API gives you the provider bucket so you can decide per workflow instead of hard-coding a list of domains.
is_catch_all
was false
here, but when it's true
the SMTP check is basically lying to you. A catch-all server accepts every recipient, so smtp_verified
becomes meaningless. An agent that trusts SMTP alone will send to not-a-real-user@catchalldomain.com
and think it succeeded. The gatekeeper needs to downgrade SMTP when catch-all is true.
syntax_suggestion
was null
for this address, which makes sense—test@gmail.com
is spelled correctly. But the feature matters for agent workflows because agents don't fix typos. A human sees gmial.com
and corrects it; an agent sees a valid-looking string and sends. The API would return gmail.com
as a suggestion for that typo. That's a second chance before the bounce.
Greylisting detection didn't fire on Gmail either, but it's another layer I want in the loop. Some mail servers temporarily reject the first delivery attempt to slow down spammers. An impatient agent might interpret the deferral as a hard failure, or worse, retry aggressively and get rate-limited. Knowing a domain greylists lets you schedule the send instead of hammering it.
So the data tells a clear story: test@gmail.com
is reachable, but not trustworthy. The agent saw reachable. The gatekeeper saw trustworthy. Twelve bounces later, I'm on the gatekeeper's side.
Here's where I stop hedging: SMTP verification alone is overrated for agentic email workflows. It's a useful signal. It is not a send permission.
The problem isn't the protocol. The problem is what an agent does with the signal. Local agent models like Muse Glimmer are built for always-on, function-calling workflows. Docker Sandboxes give coding agents disposable, isolated environments to run tools. qm is building a multiplayer harness so multiple agents can collaborate on work. The common thread is more autonomy, less human in the loop. That's great until the agent picks up a mailer and starts spraying.
The recent report on document-borne AI worms is what made me nervous. Håkon Måløy's research, disclosed after a 144-day coordination period with Microsoft, showed how attacker-controlled instructions in one Word document can propagate through Copilot-generated documents across trusted workflows. The vulnerability isn't just a single bad prompt; it's a chain of trusted actions that amplifies a mistake. Email is the same shape. One bad send doesn't just bounce. It dings your sender reputation, pollutes your list hygiene, triggers ESP rate limits, and in some jurisdictions creates a compliance event. The agent doesn't see any of that second-order damage.
That's why I separated "can send" from "should send." The validator's is_trusted_identity
field is a "should send" signal. It says: even if the mailbox exists, this address has been in three breach dumps, so maybe don't hand it sensitive content without a second look. An agent that only knows SMTP will never make that distinction.
I'm still not sure if blocking every breached address is the right call. Plenty of real humans have old accounts in breach dumps. A hard block could exclude legitimate users. But for an autonomous agent with no human review, I'd rather err on the side of false negatives than explain a reputation crash to my ESP. The tradeoff is messy, and I'm leaving it messy.
This is also why I linked the gatekeeper to my broader tool-audit work. In a previous post about building an MCP server for domain investigation, I hit five security gotchas around giving agents network tools. The pattern is the same: the agent doesn't need to know less; it needs a separate layer that knows when to say no. I've also been comparing APIs lately—my evaluation of twelve domain WHOIS APIs taught me that composite scores usually beat raw field dumps. The same logic applies here.
The gatekeeper is now mandatory before any agent-triggered email leaves my infrastructure. Not optional. Not "nice to have." Mandatory.
Here's what that means in practice:
smtp_verified
alone.is_trusted_identity
is false, the agent gets a refusal.syntax_suggestion
back to the user or agent before the address enters the workflow.is_free_email
and provider
feed B2B vs B2C segmentation. A @gmail.com
lead gets a different score than a @company.com
lead.The implementation is small. A Python wrapper around the API. A refusal response the agent can parse. A log line. That's it. The hard part was deciding that the agent isn't allowed to skip it.
If you want to build the same thing, the code and docs are on GitHub, and the hosted endpoint is on RapidAPI. I keep the RapidAPI subscription on a pay-as-you-go plan because validation volume spikes around product launches.
A minimal call looks like this with curl:
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/validate?email=test@gmail.com' \
--header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY' \
--header 'X-RapidAPI-Host: email-validator112.p.rapidapi.com'
And the Python version I run in my gatekeeper:
import requests
url = "https://email-validator112.p.rapidapi.com/validate"
headers = {
"X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
}
def validate(email):
r = requests.get(url, headers=headers, params={"email": email}, timeout=10)
r.raise_for_status()
return r.json()
data = validate("test@gmail.com")
print(data["is_trusted_identity"]) # False for test@gmail.com
Both return the full JSON I quoted above. Swap the email parameter for whatever your agent is about to contact.
On August 12, the agent also sent to a disposable domain that the validator had flagged. I saw the flag in the log after the fact. It cost me three hours of scrubbing the list and a temporary rate limit from my ESP. I don't have a tidy lesson from that one. Sometimes you ship the guardrail after the crash.
The bigger unresolved question is who owns the trust decision in an agent stack. If every tool vendor ships its own validator, we end up with fragmented policy: the email tool says one thing, the database tool says another, the browser tool says a third. Maybe what we need isn't a dozen gatekeepers but a single "tool-use risk score" that an agent consults before any external action. Nobody has built that yet. Or if they have, I haven't found the open-source version.
What I do know is that I won't run another agent with a mailer until something like this sits in front of it. The 12 bounces were cheap. The next mistake might not be.
If you had a free weekend, what would you build with an email-validation gatekeeper: a self-healing newsletter list, a disposable-email firewall for your signups, or something weirder?