cd /news/ai-agents/i-ran-50-emails-through-ai-agents-12… · home topics ai-agents article
[ARTICLE · art-98331] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

I Ran 50 Emails Through AI Agents. 12 SMTP Bounces Hit.

A developer tested an AI agent's ability to send outreach emails and found that 12 of 50 addresses bounced, including a known test address. The agent relied solely on SMTP verification, which accepted the address, but a composite trust check flagged it as risky due to three data breaches. The developer recommends using an email validation API that combines SMTP, breach, and disposable checks to gate agent actions.

read8 min views1 publishedAug 15, 2026

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?

── more in #ai-agents 4 stories · sorted by recency
── more on @muse glimmer 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-ran-50-emails-thro…] indexed:0 read:8min 2026-08-15 ·