AI agents are starting to use email. Not metaphorically. They sign up for services, receive verification codes, get alerts, and act on them. At broodnet we give each agent its own email address and a CLI to manage its inbox. We recently scored 160+ transactional emails (verification codes, welcome messages, notifications, security alerts) collected from the team's professional and personal inboxes over the last 5 to 10 years, covering SaaS, games, crypto, dev tools, news, and more. We scored each one on both human UX quality and agent parseability, and the results were rough: over 40% had fully opaque tracking URLs, 66% of welcome emails contained zero usable links, and only 16% of the dataset was clean enough for both audiences.
That analysis taught us what's broken. This is what to do about it.
1. Strip tracking from your transactional links
This is the single highest-impact change. Every tracking redirect turns a readable URL into an opaque blob that tells a non-browser client nothing about where it leads.
Most people overlook this: transactional emails with a clear call to action don't need click tracking because the CTA is the metric. If you send a "verify your email" message, you already know whether the email got verified. You can measure that on the backend. You don't need a Salesforce Marketing Cloud redirect between the user and the endpoint.
Your marketing emails can have all the tracking they want. But verify-email, confirm-account, and manage-webhooks links should be raw destination URLs. Companies like GitLab ship every transactional link as a raw URL and still understand their funnel just fine.
2. Put the verification code in the subject line
847291 is your Acme code
Five senders in our dataset did this (Canva, Slack, LinkedIn, Gravatar, Instagram) and all five scored a perfect 5/5 on subject quality. Emails without the code in the subject averaged 3.6/5.
One template change, every client benefits. iOS and Android detect OTP codes in notifications and surface a "copy code" button on the lock screen. Having it in the subject makes it trivially detectable. An agent scanning its inbox through a CLI extracts the code without opening the message. Humans grab the code without opening the email, sometimes without even unlocking their phone.
3. State the expiry in plain text
"This code expires in 15 minutes."
Six words. Emails that included this scored 7.4/10 on agent metrics versus 5.7/10 for those that didn't. Partly because expiry matters for prioritization (an agent needs to know if it should drop everything), partly because the kind of team that states expiry tends to write cleaner emails overall.
GitLab states it on every OTP. Hetzner includes the exact datetime. Docker says 15 minutes. Gravatar says 5.
4. Use semantic URL paths
Compare:
/verify?token=eyJhbGciOiJIUzI1NiJ9.eyJtZXNzYWdl...
/email_verify/?email=user@example.com&hash=a336a019-f7cf-4454-b3bd-8aa1e134cd29
Both work. Both are secure. But the second one tells you what it does and who it's for just by reading it. UUID4 tokens, short hex hashes, or alphanumeric codes with readable path segments all beat opaque JWTs.
Some patterns from the dataset:
app.pulsetic.com/email_verify/?email=<email>&hash=<uuid4>-
raider.io/verify?user=<username>&token=<token>(note the semantic prefix) signup.mailgun.com/activate/<hex>accounts.random.org/create/confirm?secret=<uuid4>
5. Hand-write the plain-text MIME part
Every email is a MIME multipart with HTML and plain-text versions. Most ESPs auto-generate the plain-text from the HTML, and the result is usually garbage: stripped tags, broken links, spacer characters left in.
This matters because terminal clients, CLI tools, agents, screen readers, and LLMs all consume the plain-text part. If yours is auto-generated trash, that entire audience sees trash.
Put links on their own lines. Use dashes as section dividers. Include the same critical info as the HTML. The best plain-text emails in our dataset (ngrok's verify, GitLab, RANDOM.ORG) were written as plain text first and it shows.
6. Kill the spacer characters
ESPs inject zero-width Unicode characters after the preheader to prevent email clients from pulling body text into the preview. In raw text:
Click the button to verify
We found 7 distinct zero-width character types in the dataset. 29% had moderate pollution. Nearly 12% were severely polluted. Google Workspace's welcome email had hundreds of combined U+034F and soft hyphens before the first real sentence.
These characters are a hack that directly poisons every non-HTML reader. Terminal clients render garbled whitespace. Agents waste processing on invisible noise. Screen readers may choke.
The alternative: display:none or mso-hide:all on a hidden span after your preheader. You avoid poisoning the plain-text part entirely. For transactional emails you don't need pixel-perfect preheader control.
7. Provide a raw URL fallback for every button
If the button doesn't work, copy this link:
https://app.example.com/verify?token=abc123
Even if your button CTA goes through a tracker, the fallback gives every non-browser client something it can actually use. Mobile users with broken rendering get a working link. CLI users get an extractable URL. It costs one extra line in the template.
Buffer did this on exactly one of their five emails (the login security alert) and it was the only Buffer email that scored above worst-in-class.
8. Add schema.org JSON-LD
Gmail supports embedded structured data through JSON-LD. A ConfirmAction in the email HTML surfaces one-click actions in the Gmail UI and provides a machine-readable, typed representation of what the email wants the reader to do.
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ConfirmAction",
"name": "Verify Email",
"handler": {
"@type": "HttpActionHandler",
"url": "https://app.example.com/verify?token=abc123"
}
},
"description": "Verify your email address for Acme"
}
Embed in a <script type="application/ld+json"> tag. Any parser that knows to look for JSON-LD gets a structured action without heuristic text matching. Gmail also supports ViewAction, SaveAction, and TrackAction (for shipments). Almost nobody uses this for transactional emails, but the infrastructure has been there for years.
9. Set meaningful email headers
X-Entity-Ref-ID: unique value per email. Prevents Gmail from threading your OTP under a week-old verification thread.
List-Unsubscribe + List-Unsubscribe-Post: RFC 8058. Machine-readable unsubscribe entirely through headers. Gmail and Yahoo require this for bulk marketing senders, and while transactional emails are technically exempt, adding it improves deliverability and gives agents a structured way to manage subscriptions. List-Unsubscribe: <https://app.example.com/unsubscribe?id=<token>> plus List-Unsubscribe-Post: List-Unsubscribe=One-Click.
Message-ID, In-Reply-To, References: if you send follow-up emails (password reset after failed login), proper threading headers let agents understand email B relates to email A.
10. Keep your sender identity consistent
If your verification comes from noreply@mail.accounts.acme.com, your welcome from no-reply@notify.acme.com, and marketing from hello@youraccount.acme.com, an agent has to learn a different sender for each template.
Not a made-up example. Buffer sent from both hello@youraccount.buffer.com and hello@buffer.com depending on the template. An agent searching by sender misses half the emails.
Pick one address for transactional, one for marketing. Be consistent.
11. Separate transactional from marketing infrastructure
Our data showed this already happens accidentally at most companies. Product team sends clean transactional emails. Marketing sends tracked welcome emails through their ESP. The quality gap between templates from the same sender was as high as 5 points on a 10-point scale.
Make it intentional. ESP for marketing. Transactional sender (Postmark, AWS SES direct, your own SMTP) for verifications, OTPs, and security notifications. Different domain, different templates, different tracking config.
12. Design verification for every client
Most verify-link flows assume a human clicking in a browser. An agent can't "click" anything. A phone user might prefer typing a code. A text-only reader can follow a raw URL but can't render a button.
Provide the clickable link for browsers, provide the code in the body (and ideally the subject) for everything else.
The emails that offered both a link and a standalone code (NiceHash, Telltale Games, Gravatar) scored well on both human and agent metrics. The link works for desktop. The code works for agents, mobile, screen readers, and anyone who'd rather type 6 digits than tap a tiny button. This dual-path approach is already standard in SMS verification. No reason email can't do the same.
TLDR; The checklist
- [ ] Transactional CTA links are raw destination URLs, not tracker redirects
- [ ] OTP code appears in the subject line
- [ ] Expiry is stated explicitly in the body
- [ ] URL paths are human-readable (semantic paths, UUID tokens, not opaque JWTs)
- [ ] Plain-text MIME part is hand-written or at least reviewed
- [ ] No zero-width spacer character injection in the plain-text part
- [ ] Raw URL fallback provided alongside tracked buttons
- [ ] JSON-LD structured data in the HTML part for key actions
- [ ] Meaningful headers set (
X-Entity-Ref-ID,List-Unsubscribe, threading) - [ ] Consistent sender address across all transactional email types
- [ ] Transactional email sent through separate infrastructure from marketing
- [ ] Verification flow supports both link-click and code-entry paths
None of these hurt human readability. Most of them improve it. The data from 179 scored emails backs this up: the emails that scored highest for agents also scored highest for humans. Writing for machines and writing for people is the same job.
broodnet gives AI agents their own email addresses. CLI-native, built for agent-to-owner communication. Free tier available.
Top comments (0)