<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yopie Suryadi</title>
    <description>The latest articles on DEV Community by Yopie Suryadi (@yopiesuryadi).</description>
    <link>https://dev.to/yopiesuryadi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3847929%2Fd24f2652-9552-49e6-a153-a8a9b25097e4.jpeg</url>
      <title>DEV Community: Yopie Suryadi</title>
      <link>https://dev.to/yopiesuryadi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yopiesuryadi"/>
    <language>en</language>
    <item>
      <title>Email API for AI Agents: What to Evaluate Before You Pick One</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Mon, 30 Mar 2026 08:32:18 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/email-api-for-ai-agents-what-to-evaluate-before-you-pick-one-2lki</link>
      <guid>https://dev.to/yopiesuryadi/email-api-for-ai-agents-what-to-evaluate-before-you-pick-one-2lki</guid>
      <description>&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;Most developers discover the limits of their email API only after something breaks in production. An agent sends a follow-up reply that lands outside the original thread. An inbound message arrives at a webhook endpoint and disappears with no way to replay it. A compliance audit asks for an audit log that was never generated. By then, migrating to a different provider is painful.&lt;/p&gt;

&lt;p&gt;Choosing an email API for an AI agent is a different problem from choosing one for transactional email. A welcome email does not need to receive replies. An AI agent does. The decision criteria are not the same, and most comparison guides available today were written with transactional use cases in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The standard comparison framework for email APIs focuses on deliverability, latency, and price per message. Those things matter, but they answer the wrong question for agents. When an agent manages an ongoing support conversation, a sales sequence, or an approval workflow, the relevant questions are: can the agent receive the reply, does the reply arrive in the right thread context, and what happens if the event notification fails?&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://dev.to/knocklabs/we-analyzed-a-billion-email-api-requests-heres-what-we-learned-j39"&gt;billion-request benchmark by Knock&lt;/a&gt; found that SendGrid's median API response time is 22ms, Postmark's is 33ms, and Resend's is 79ms. Those numbers matter for transactional throughput. But an agent waiting on a human reply is not measuring latency in milliseconds. It is measuring reliability over minutes and hours.&lt;/p&gt;

&lt;p&gt;The industry has also converged on a comparison model that treats inbound email as a secondary feature, something bolted on via webhook rather than designed as a core primitive. &lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;AgentMail's 2026 comparison&lt;/a&gt; of the top providers found that most handle inbound email through stateless webhook routing with no persistent storage or threading. For transactional email, that is fine. For an agent that needs to read a reply, correlate it with a prior message, and continue a conversation, it is a significant gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Insight
&lt;/h2&gt;

&lt;p&gt;The right question to ask before picking an email API for an agent is not "how fast is the send?" It is "can this API handle the full conversation loop?" That loop has seven distinct requirements, and providers differ on almost every one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evaluation Framework
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Two-Way vs. Send-Only
&lt;/h3&gt;

&lt;p&gt;The most fundamental distinction. Send-only providers (AWS SES at its core, older SendGrid configurations) give you an endpoint for outbound email and nothing more. Two-way providers give you both a send path and a receive path.&lt;/p&gt;

&lt;p&gt;The difference in architecture is significant. &lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;AgentMail's comparison&lt;/a&gt; found that SendGrid's inbound parse is stateless (no persistent storage, no threading) and Mailgun routes inbound email via webhook with no persistent inbox. Resend added inbound webhook support in 2025. AWS SES requires additional AWS infrastructure (S3, Lambda, SNS) to do anything useful with a received message.&lt;/p&gt;

&lt;p&gt;For agents, the question is whether you want to build and maintain that additional layer yourself or use an API that treats two-way communication as a first-class primitive.&lt;/p&gt;

&lt;p&gt;An API designed for the full conversation loop looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// An email API for agents should handle the full cycle: send, receive, reply in thread&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;support-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timeoutMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thanks for reaching out.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;waitFor&lt;/code&gt; method blocks until a reply arrives, which is exactly the pattern an agent running a turn needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. MTA Ownership vs. Rented Infrastructure
&lt;/h3&gt;

&lt;p&gt;Who controls the mail transfer agent matters for deliverability configuration and high-volume cost. AWS SES runs its own MTA and prices per message ($0.10 per 1,000 emails sent and $0.10 per 1,000 received, per &lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;AgentMail's pricing table&lt;/a&gt;). SendGrid and Postmark operate their own infrastructure. Resend routes through established MTAs.&lt;/p&gt;

&lt;p&gt;Providers that own their MTA can offer dedicated IPs, custom warm-up, and more direct control over reputation. Providers that abstract the MTA away trade that control for easier onboarding. For agents sending at modest volume (under 50,000 messages per month), MTA ownership is less important than the API surface above it. In regulated industries, dedicated IP configuration and reputation isolation become meaningful.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Thread Handling
&lt;/h3&gt;

&lt;p&gt;Email threading is governed by three headers: &lt;code&gt;Message-ID&lt;/code&gt;, &lt;code&gt;In-Reply-To&lt;/code&gt;, and &lt;code&gt;References&lt;/code&gt;. &lt;a href="https://datatracker.ietf.org/doc/html/rfc2822" rel="noopener noreferrer"&gt;RFC 2822&lt;/a&gt; specifies that a reply's &lt;code&gt;References&lt;/code&gt; field should include the parent's &lt;code&gt;References&lt;/code&gt; plus the parent's &lt;code&gt;Message-ID&lt;/code&gt;. When this chain is managed correctly, every major email client preserves the thread. When it breaks, replies land as new conversations.&lt;/p&gt;

&lt;p&gt;Managing this manually in application code is straightforward for the first reply. It becomes error-prone after three or four turns when the &lt;code&gt;References&lt;/code&gt; header needs to carry the full ancestry. An API that handles threading automatically removes this class of bug entirely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;AgentMail&lt;/a&gt; handles threading via built-in API support with automatic header management. SendGrid and SES do not manage thread state; the application is responsible for passing the correct headers on every reply. Resend's threading behavior is not documented as an automatic feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Event Notification Reliability
&lt;/h3&gt;

&lt;p&gt;When an inbound message triggers an event, what happens if your endpoint is down? This is where providers diverge significantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mailtrap.io/blog/email-api-flexibility/" rel="noopener noreferrer"&gt;Mailtrap's flexibility comparison&lt;/a&gt; found that retry windows vary considerably across providers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Retry window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SendGrid&lt;/td&gt;
&lt;td&gt;72 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailtrap&lt;/td&gt;
&lt;td&gt;24 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postmark&lt;/td&gt;
&lt;td&gt;12 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailgun&lt;/td&gt;
&lt;td&gt;8 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;td&gt;User-managed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Beyond retry duration, the question is whether you can replay a specific event after the window expires. For agents that need to recover from a missed notification without re-triggering the full workflow, event replay is a meaningful capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Custom Domains with Full DNS Setup
&lt;/h3&gt;

&lt;p&gt;An AI agent sending from &lt;code&gt;agent@support.yourcompany.com&lt;/code&gt; requires a custom domain with SPF, DKIM, and ideally DMARC records configured. The question is how much of that setup the provider automates.&lt;/p&gt;

&lt;p&gt;All major providers support custom domains. The differences are in the verification flow, the time required, and whether the provider guides you through the full DNS record set or leaves gaps. &lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;AgentMail&lt;/a&gt; notes that SendGrid's time to first email is 10 to 15 minutes, Mailgun's is similar, and AWS SES requires sandbox approval that takes 24 to 48 hours for new users.&lt;/p&gt;

&lt;p&gt;For agents deployed in enterprise environments, the ability to verify multiple domains and issue separate credentials per domain matters for tenant isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Compliance Readiness
&lt;/h3&gt;

&lt;p&gt;Enterprise buyers commonly ask for SOC 2 Type II, ISO 27001, or equivalent certifications. For agents handling customer communication, audit logs (who sent what, when, and to whom) are also relevant.&lt;/p&gt;

&lt;p&gt;This criterion is often invisible until a procurement process or security review surfaces it. Checking compliance posture before you build is faster than retrofitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Pricing Model
&lt;/h3&gt;

&lt;p&gt;Three pricing models exist: per-message, per-inbox (flat rate), and hybrid.&lt;/p&gt;

&lt;p&gt;Per-message pricing (AWS SES at $0.10/1,000) is economical at high outbound volume but can become expensive if agents are also receiving large volumes. Per-inbox pricing (AgentMail's model, starting at $20/month for 10 inboxes and 10,000 messages) is predictable for deployments with a fixed number of agent inboxes. Flat-rate models (Postmark, Resend's Pro tier) are predictable up to a message ceiling, then require a tier upgrade.&lt;/p&gt;

&lt;p&gt;For agents, the relevant calculation is the ratio of inbound to outbound messages. A support agent that receives 1,000 messages and sends 1,000 replies is doing 2,000 message operations. A per-message model bills both directions if the provider supports inbound; a per-inbox model does not change with message volume within the tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison Table
&lt;/h2&gt;

&lt;p&gt;The table below summarizes each provider across the seven criteria. "Native" means the feature is a first-class API primitive. "Webhook" means the feature requires your application to handle state and persistence.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;SendGrid&lt;/th&gt;
&lt;th&gt;AWS SES&lt;/th&gt;
&lt;th&gt;Resend&lt;/th&gt;
&lt;th&gt;Postmark&lt;/th&gt;
&lt;th&gt;AgentMail&lt;/th&gt;
&lt;th&gt;mailbot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Two-way (inbound)&lt;/td&gt;
&lt;td&gt;Webhook, stateless&lt;/td&gt;
&lt;td&gt;Via S3/Lambda/SNS&lt;/td&gt;
&lt;td&gt;Webhook (since 2025)&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Native inbox&lt;/td&gt;
&lt;td&gt;Native inbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MTA ownership&lt;/td&gt;
&lt;td&gt;Yes (Twilio)&lt;/td&gt;
&lt;td&gt;Yes (AWS)&lt;/td&gt;
&lt;td&gt;Abstracted&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Abstracted&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto thread handling&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Not documented&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event retry window&lt;/td&gt;
&lt;td&gt;72 hours&lt;/td&gt;
&lt;td&gt;Not specified&lt;/td&gt;
&lt;td&gt;User-managed&lt;/td&gt;
&lt;td&gt;12 hours&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom domains&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance docs&lt;/td&gt;
&lt;td&gt;SOC 2&lt;/td&gt;
&lt;td&gt;SOC 2, ISO 27001&lt;/td&gt;
&lt;td&gt;SOC 2&lt;/td&gt;
&lt;td&gt;SOC 2&lt;/td&gt;
&lt;td&gt;Not published&lt;/td&gt;
&lt;td&gt;In progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing model&lt;/td&gt;
&lt;td&gt;Per-message&lt;/td&gt;
&lt;td&gt;Per-message&lt;/td&gt;
&lt;td&gt;Per-message tiers&lt;/td&gt;
&lt;td&gt;Per-message tiers&lt;/td&gt;
&lt;td&gt;Per-inbox tiers&lt;/td&gt;
&lt;td&gt;Per-inbox tiers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few notes on reading this table honestly. SendGrid's &lt;a href="https://dev.to/knocklabs/we-analyzed-a-billion-email-api-requests-heres-what-we-learned-j39"&gt;22ms p50 latency&lt;/a&gt; is the best measured across any provider in the Knock benchmark, and its 72-hour retry window is the longest available for event notifications. AWS SES has &lt;a href="https://dev.to/knocklabs/we-analyzed-a-billion-email-api-requests-heres-what-we-learned-j39"&gt;the most consistent error rates&lt;/a&gt; of any provider measured, with most days below 0.01%. These are real advantages for high-volume transactional use cases.&lt;/p&gt;

&lt;p&gt;The providers that score highest on the agent-specific criteria (two-way, auto threading, event replay) are the newer ones: AgentMail and mailbot. Both are earlier-stage than SendGrid or SES, which means a tradeoff: more agent-native features, less operational history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where mailbot Stands
&lt;/h2&gt;

&lt;p&gt;mailbot is designed around the agent use case. Inboxes are programmable resources. Threads are tracked automatically with correct &lt;code&gt;In-Reply-To&lt;/code&gt; and &lt;code&gt;References&lt;/code&gt; headers on every reply. Event notifications include replay via &lt;code&gt;client.events.replay(eventId)&lt;/code&gt;. Compliance tooling is available via &lt;code&gt;client.compliance.readiness()&lt;/code&gt; and &lt;code&gt;client.auditLog.list()&lt;/code&gt;. Pricing is per-inbox, not per-message.&lt;/p&gt;

&lt;p&gt;The honest caveat: mailbot is younger than SendGrid or Postmark, which means less operational track record at the top end of volume. If you are migrating an existing high-volume transactional email pipeline, that history matters. If you are building a new agent workflow from scratch, the agent-native API surface is a meaningful starting point advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Close
&lt;/h2&gt;

&lt;p&gt;Not every agent needs all seven criteria. A simple outbound notification agent only needs criteria 2 and 5. An agent managing multi-turn customer conversations needs all seven, and a gap in any one of them will surface as a bug in production.&lt;/p&gt;

&lt;p&gt;The providers that dominated email in 2015 were built for a world where email was a one-way notification channel. Agent workflows are a different problem, and the evaluation should reflect that. The &lt;a href="https://getmail.bot/compare" rel="noopener noreferrer"&gt;mailbot comparison page&lt;/a&gt; maps each criterion to a working implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;AgentMail, "5 Best Email API For Developers Compared [2026]" (2026-01-27): &lt;a href="https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026" rel="noopener noreferrer"&gt;https://www.agentmail.to/blog/5-best-email-api-for-developers-compared-2026&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jeff Everhart / Knock via Dev.to, "We analyzed a billion email API requests: here's what we learned" (2026-03-12): &lt;a href="https://dev.to/knocklabs/we-analyzed-a-billion-email-api-requests-heres-what-we-learned-j39"&gt;https://dev.to/knocklabs/we-analyzed-a-billion-email-api-requests-heres-what-we-learned-j39&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ivan Djuric / Mailtrap, "5 Best Email APIs: Flexibility Comparison [2026]" (2026-03-13): &lt;a href="https://mailtrap.io/blog/email-api-flexibility/" rel="noopener noreferrer"&gt;https://mailtrap.io/blog/email-api-flexibility/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Postmark, "Best Email API" (2026-01-12): &lt;a href="https://postmarkapp.com/blog/best-email-api" rel="noopener noreferrer"&gt;https://postmarkapp.com/blog/best-email-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reddit r/webdev, "Email API benchmarks for SendGrid, Amazon SES...": &lt;a href="https://www.reddit.com/r/webdev/comments/1rrxxs5/email_api_benchmarks_for_sendgrid_amazon_ses/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/webdev/comments/1rrxxs5/email_api_benchmarks_for_sendgrid_amazon_ses/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;IETF RFC 2822, "Internet Message Format": &lt;a href="https://datatracker.ietf.org/doc/html/rfc2822" rel="noopener noreferrer"&gt;https://datatracker.ietf.org/doc/html/rfc2822&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>email</category>
      <category>api</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Your AI Agent Loses Context After Three Email Replies</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Mon, 30 Mar 2026 08:32:13 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/why-your-ai-agent-loses-context-after-three-email-replies-2c99</link>
      <guid>https://dev.to/yopiesuryadi/why-your-ai-agent-loses-context-after-three-email-replies-2c99</guid>
      <description>&lt;p&gt;Your AI email agent handles the first two replies flawlessly. It reads the original message, drafts a thoughtful response, and sends it on time. Then reply three arrives and something quietly goes wrong. The agent responds to the wrong topic. It asks a question the customer already answered. It loses the thread entirely.&lt;/p&gt;

&lt;p&gt;This is not a reasoning failure. The underlying language model did not forget anything. The problem is infrastructure: the email headers that stitch a conversation together are either missing, truncated, or misread by the receiving client. By the time a thread hits three or more messages deep, the structural glue that email relies on has often already snapped.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Email Threads Are Actually Built
&lt;/h2&gt;

&lt;p&gt;Every email message carries a unique identifier called a &lt;code&gt;Message-ID&lt;/code&gt;. When you reply to that message, your client adds two headers: &lt;code&gt;In-Reply-To&lt;/code&gt;, which holds the parent message's &lt;code&gt;Message-ID&lt;/code&gt;, and &lt;code&gt;References&lt;/code&gt;, which holds the entire ancestry chain.&lt;/p&gt;

&lt;p&gt;As defined by &lt;a href="https://datatracker.ietf.org/doc/html/rfc2822" rel="noopener noreferrer"&gt;RFC 2822 (IETF)&lt;/a&gt;, the &lt;code&gt;References&lt;/code&gt; header must equal the parent message's own &lt;code&gt;References&lt;/code&gt; field plus the parent's &lt;code&gt;Message-ID&lt;/code&gt; appended at the end. Each new reply extends this chain. So by message four, the &lt;code&gt;References&lt;/code&gt; header contains three &lt;code&gt;Message-ID&lt;/code&gt; values, each pointing one step further back.&lt;/p&gt;

&lt;p&gt;This chain is how email clients group messages into a visible thread. Without it, each reply appears as a disconnected new conversation. Different email clients handle threading differently: Gmail uses &lt;code&gt;References&lt;/code&gt; together with &lt;code&gt;In-Reply-To&lt;/code&gt; and subject matching, Outlook leans primarily on &lt;code&gt;References&lt;/code&gt;, and Apple Mail follows RFC 2822 most closely. Omitting the &lt;code&gt;References&lt;/code&gt; header outright breaks threads after three or more messages in every major client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Break Happens at Reply Three (Not Reply One or Two)
&lt;/h2&gt;

&lt;p&gt;The first reply only needs &lt;code&gt;In-Reply-To&lt;/code&gt;. Many email clients and APIs get that right automatically. The second reply needs both &lt;code&gt;In-Reply-To&lt;/code&gt; and a short &lt;code&gt;References&lt;/code&gt; chain. Most still manage this correctly. But the third reply requires your sending code to correctly read and forward the entire &lt;code&gt;References&lt;/code&gt; chain from the previous message, append the previous message's &lt;code&gt;Message-ID&lt;/code&gt;, and then set the new &lt;code&gt;In-Reply-To&lt;/code&gt; to point at that same previous message.&lt;/p&gt;

&lt;p&gt;If any step in that chain is wrong, not just the most recent one, clients diverge. According to &lt;a href="https://lifetips.alibaba.com/tech-efficiency/make-outlook-thread-conversations-like-gmail" rel="noopener noreferrer"&gt;Alibaba LifeTips research on Outlook threading&lt;/a&gt;, Outlook splits 31.4% of threads that Gmail preserves intact. Outlook's threading is folder-bound and more sensitive to subject-line changes, while Gmail treats subject-matching as a fallback rather than a primary signal. A thread that looks continuous in Gmail may already be fragmented in Outlook before your agent sees it.&lt;/p&gt;

&lt;p&gt;This is the compound problem. Your agent does not receive a clean, continuous conversation object. It receives fragments: some messages grouped, some orphaned, some duplicated across quoted footers. As one developer put it in a &lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;discussion on r/AI_Agents&lt;/a&gt;, roughly 80% of the tokens in a real email thread are duplicate quotes and footers, not new information. The same thread "evolves into a substantial infrastructure project" once you try to use it reliably in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Token Waste and Context Collapse Problem
&lt;/h2&gt;

&lt;p&gt;Before the headers even matter, there is a tokenization problem. When your agent fetches a thread naively, it is likely reading the same content four or five times because each reply quotes the previous one in full. The new information in message five may be two sentences. The payload you are feeding your agent may be four thousand tokens of redundant history.&lt;/p&gt;

&lt;p&gt;This is a practical, measurable problem. The developer thread on &lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;r/AI_Agents&lt;/a&gt; describes direct measurements: roughly 80% of tokens in a real email thread are duplicate quotes, signatures, and footers. Your agent is spending most of its context window processing information it has already seen.&lt;/p&gt;

&lt;p&gt;Then, when the &lt;code&gt;References&lt;/code&gt; chain is broken, your agent cannot reliably determine which message is the true root of the conversation, which messages have already been handled, or what the correct &lt;code&gt;In-Reply-To&lt;/code&gt; value for its outgoing reply should be. At that point, even a perfectly capable reasoning engine will produce incoherent replies because the input it received was incoherent. The agent did not fail. The input pipeline failed.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://composio.dev/blog/why-ai-agent-pilots-fail-2026-integration-roadmap" rel="noopener noreferrer"&gt;Composio 2025 AI Agent Report&lt;/a&gt;, integration failure rather than language model failure is the primary cause of AI agent production failures. Email threading is a textbook example of this pattern. The model is fine. The plumbing is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Wrong Way: Manual Header Construction
&lt;/h2&gt;

&lt;p&gt;Many developers, when building email reply logic, reach for &lt;code&gt;client.messages.send()&lt;/code&gt; with manually constructed headers. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// WRONG: manually constructing threading headers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbox_abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Re: Your support request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thank you for your message...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;In-Reply-To&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;original-message-id@mail.example.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;References&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;original-message-id@mail.example.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// You are now responsible for reading and appending the full References chain&lt;/span&gt;
    &lt;span class="c1"&gt;// Get this wrong once and Outlook splits the thread. Forever.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this approach is not that it is hard to write. It is that it requires you to correctly read the full &lt;code&gt;References&lt;/code&gt; chain from the previous message, append the new &lt;code&gt;Message-ID&lt;/code&gt;, and keep this logic accurate across every environment that may format or truncate headers differently. Miss it once on reply three, and your agent is now operating on a broken thread for every reply that follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Right Way: client.messages.reply()
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;client.messages.reply()&lt;/code&gt; method exists specifically to handle this. It reads the correct &lt;code&gt;Message-ID&lt;/code&gt; from the parent message, builds the full &lt;code&gt;References&lt;/code&gt; chain by reading the parent's own &lt;code&gt;References&lt;/code&gt; header, and sets both &lt;code&gt;In-Reply-To&lt;/code&gt; and &lt;code&gt;References&lt;/code&gt; correctly before sending. You do not touch headers at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// RIGHT: let the SDK handle threading headers automatically&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbox_abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msg_xyz789&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the specific message you are replying to&lt;/span&gt;
  &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Thank you for following up. Here is what we found...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single call handles RFC 2822 compliance, correct &lt;code&gt;References&lt;/code&gt; chain construction, and &lt;code&gt;In-Reply-To&lt;/code&gt; assignment. No manual header management, no risk of chain truncation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetching Full Thread Context Before You Reply
&lt;/h2&gt;

&lt;p&gt;The other half of the problem is making sure your agent actually reads the full thread before composing a reply. The correct pattern is to call &lt;code&gt;client.threads.get()&lt;/code&gt; first, which returns the complete message history for the thread as a structured object, then pass that context to your agent, and only then call &lt;code&gt;client.messages.reply()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Step 1: Get the full thread so your agent has complete context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;threadId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: Extract message bodies in order (skip duplicate quoted sections)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// Step 3: Feed structured thread to your agent, get a reply draft&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;replyDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;yourAgentLogic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 4: Reply using the SDK so headers are handled correctly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// reply to the most recent message&lt;/span&gt;
  &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;replyDraft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern gives your agent structured, deduplicated context rather than a raw chain of quoted bodies. It also ensures the reply is attached to the correct message in the thread, which is what determines whether Gmail, Outlook, and Apple Mail all show it in the right place. The deduplication step in particular matters: by passing only the unique message bodies in chronological order instead of four thousand tokens of repeated quoted text, your reasoning engine operates on clean input. Your agent processes less and produces more accurate output.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Consistent Threading Actually Unlocks
&lt;/h2&gt;

&lt;p&gt;When your agent maintains thread continuity reliably, several things improve at once. The conversation history your agent reads is accurate instead of fragmented. Reply rates from customers tend to rise because responses feel contextually aware rather than generic. Thread-level event history through &lt;code&gt;client.events.list(threadId)&lt;/code&gt; becomes useful for auditing what the agent did and when.&lt;/p&gt;

&lt;p&gt;More importantly, you stop debugging ghost threads in Outlook and wondering why a customer says "I already answered that." This much is clear: the clients that handle threading well do so because they construct headers by the spec. The ones that break threads do so because they take shortcuts. Your agent should not take shortcuts either.&lt;/p&gt;

&lt;p&gt;Email is still the dominant communication channel for business workflows. An AI agent that loses thread context after three replies is not a production agent. It is a prototype that creates cleanup work for your team. Getting the infrastructure right is not optional, and as the &lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;r/AI_Agents community noted&lt;/a&gt;, most teams learn this the hard way after shipping.&lt;/p&gt;

&lt;p&gt;You do not have to.&lt;/p&gt;

&lt;p&gt;Start building thread-aware email agents at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc2822" rel="noopener noreferrer"&gt;RFC 2822: Internet Message Format (IETF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;Reddit r/AI_Agents: Email Context for AI Agents Is Way Harder Than It Looks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://composio.dev/blog/why-ai-agent-pilots-fail-2026-integration-roadmap" rel="noopener noreferrer"&gt;Composio: Why AI Agent Pilots Fail, 2026 Integration Roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lifetips.alibaba.com/tech-efficiency/make-outlook-thread-conversations-like-gmail" rel="noopener noreferrer"&gt;Alibaba LifeTips: Make Outlook Thread Conversations Like Gmail&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>email</category>
      <category>ai</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Give Your AI Agent a Real Email Inbox with MCP</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 16:55:58 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/how-to-give-your-ai-agent-a-real-email-inbox-with-mcp-1jm1</link>
      <guid>https://dev.to/yopiesuryadi/how-to-give-your-ai-agent-a-real-email-inbox-with-mcp-1jm1</guid>
      <description>&lt;p&gt;Most email MCP servers let your AI client send email. That is the easy half. The harder half is letting it receive replies, track delivery events, and maintain conversation context across a thread. This tutorial shows you how to wire both halves together using the mailbot MCP server.&lt;/p&gt;

&lt;p&gt;If you have searched for "MCP email server" or "email MCP server" and landed on tutorials that only cover outbound, you already know the gap. &lt;a href="https://www.mailercheck.com/articles/email-mcp-server" rel="noopener noreferrer"&gt;MailerCheck's roundup of 6 email MCP servers&lt;/a&gt; confirms that the only two-way option in the list is a Gmail relay through Zapier. For developers who want a purpose-built inbox that their AI agent can own end to end, that is a meaningful gap.&lt;/p&gt;

&lt;p&gt;This tutorial fills it. By the end, your MCP-compatible AI client will be able to create an inbox, send email from it, read replies, and check delivery events.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Will Build
&lt;/h2&gt;

&lt;p&gt;An AI agent workflow backed by a real mailbot inbox. Your AI client exposes 13 MCP tools that map directly to mailbot's API surface: inbox management, message sending, reply handling, thread reading, and delivery event inspection. You type a natural language instruction, and the client calls the right tool.&lt;/p&gt;

&lt;p&gt;This is useful for agentic tasks like: send a follow-up to anyone who replied to yesterday's campaign, check whether my outbound message was delivered, or create a throwaway inbox for this test scenario and clean it up when done.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 18 or later&lt;/strong&gt; installed on your machine (the MCP server runs via &lt;code&gt;npx&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An MCP-compatible AI desktop client&lt;/strong&gt; that supports external MCP servers via a JSON config file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A mailbot account and API key&lt;/strong&gt; from &lt;a href="https://getmail.bot" rel="noopener noreferrer"&gt;getmail.bot&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No local build step required. The package ships prebuilt to npm.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Understand How MCP Servers Work
&lt;/h2&gt;

&lt;p&gt;MCP (Model Context Protocol) lets an AI client call external tools in the same way a developer calls an API. &lt;a href="https://modelcontextprotocol.io/docs/develop/build-server" rel="noopener noreferrer"&gt;According to the official MCP documentation&lt;/a&gt;, servers expose tools as typed functions. When you send a message to your AI client, it inspects the available tools, decides which one matches your intent, and executes it. The result comes back as context for the next response.&lt;/p&gt;

&lt;p&gt;For email, this means your AI client becomes a first-class email actor rather than a text generator that happens to mention email addresses. It can actually create inboxes, send messages, and read what comes back.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install the mailbot MCP Server
&lt;/h2&gt;

&lt;p&gt;No manual install is required. The package runs on demand via &lt;code&gt;npx&lt;/code&gt;, so your AI client fetches and executes it automatically on first launch.&lt;/p&gt;

&lt;p&gt;The package is published at &lt;code&gt;@yopiesuryadi/mailbot-mcp&lt;/code&gt; on npm. If you want to inspect the package before running it, you can pull it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @yopiesuryadi/mailbot-mcp &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the package resolves and prints the available tool list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configure the MCP Server in Your AI Client
&lt;/h2&gt;

&lt;p&gt;Your MCP-compatible AI client reads a JSON config file to discover external servers. The exact file location varies by client. Common locations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;Typical config path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/Library/Application Support/&amp;lt;ClientName&amp;gt;/config.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%APPDATA%\&amp;lt;ClientName&amp;gt;\config.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add the following block to your client's MCP servers config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mailbot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@yopiesuryadi/mailbot-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"MAILBOT_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mb_test_xxx"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;mb_test_xxx&lt;/code&gt; with your actual mailbot API key from your account dashboard.&lt;/p&gt;

&lt;p&gt;Save the file and restart your AI client. If the client has a tools or connectors panel, you should see "mailbot" listed with its 13 available tools. That confirms the server is running and connected.&lt;/p&gt;

&lt;p&gt;Note: the MCP server is at v1 and has not been tested across every AI client configuration. If your client does not surface the tools after restart, check that the config JSON is valid and that Node.js is accessible on your system PATH.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Create an Inbox via MCP
&lt;/h2&gt;

&lt;p&gt;Once the server is connected, you can talk to your AI client in plain language. To create a new inbox, try a prompt like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a new mailbot inbox named "support-test"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your AI client will call the &lt;code&gt;create_inbox&lt;/code&gt; tool, which maps to &lt;code&gt;client.inboxes.create&lt;/code&gt; in the mailbot SDK. The tool returns the inbox details including its assigned email address.&lt;/p&gt;

&lt;p&gt;You can list existing inboxes with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;List my mailbot inboxes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And retrieve details for a specific one with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the inbox with ID inbox_abc123&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Send Email via MCP
&lt;/h2&gt;

&lt;p&gt;With an inbox created, sending is one instruction away:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Send an email from my support-test inbox to &lt;a href="mailto:recipient@example.com"&gt;recipient@example.com&lt;/a&gt; with the subject "Hello from mailbot MCP" and a plain text body saying "This was sent by my AI agent."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The client calls the &lt;code&gt;send_message&lt;/code&gt; tool under the hood. This is meaningfully different from send-only email MCP servers like &lt;a href="https://mailtrap.io/blog/claude-desktop-send-email/" rel="noopener noreferrer"&gt;Mailtrap's MCP integration&lt;/a&gt;, which only expose a single outbound send tool. With mailbot, the same session that sends can also receive and inspect.&lt;/p&gt;

&lt;p&gt;You can also send HTML:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Send an HTML email from support-test to &lt;a href="mailto:recipient@example.com"&gt;recipient@example.com&lt;/a&gt;. Subject: "Welcome". Body: a simple HTML welcome message with a bold heading.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 6: Receive and Read Email via MCP
&lt;/h2&gt;

&lt;p&gt;When a reply arrives at your mailbot inbox, your AI client can read it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;List the latest messages in my support-test inbox&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This calls &lt;code&gt;list_messages&lt;/code&gt; and returns subject, sender, snippet, and thread ID for each message. To read a full message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the full content of message msg_xyz789&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To search across messages:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Search my support-test inbox for messages from &lt;a href="mailto:sender@example.com"&gt;sender@example.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;search_messages&lt;/code&gt; tool accepts sender, subject keywords, date ranges, and label filters, so your agent can do targeted retrieval without reading the entire inbox.&lt;/p&gt;

&lt;p&gt;If you are building an automated flow and need to wait for a reply before proceeding, the &lt;code&gt;wait_for_message&lt;/code&gt; tool (backed by &lt;code&gt;client.messages.waitFor&lt;/code&gt;) polls until a matching message arrives or a timeout is reached. This is useful for test flows where you send a message and need to assert on the reply.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Check Delivery Events via MCP
&lt;/h2&gt;

&lt;p&gt;Sending a message is the start, not the end. Your AI client can also inspect what happened to each message after delivery.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check the delivery events for thread thread_abc123&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This calls &lt;code&gt;list_events&lt;/code&gt; for the thread, returning a timeline of events (queued, delivered, opened, bounced, and so on). You can also retrieve a single event:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get event details for event evt_123&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is useful for agentic tasks like: "Send a follow-up only if the first message was delivered but not opened." Your agent can check the event timeline, make a conditional decision, and act without you writing any conditional logic manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Organize with Labels and Threads via MCP
&lt;/h2&gt;

&lt;p&gt;The 13 mailbot MCP tools also cover thread reading and label management. To view a full conversation thread:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me the full thread for thread_abc123&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To label a message for downstream filtering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add the label "needs-followup" to message msg_xyz789&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Labels work as lightweight state markers that persist on the message, so other tools or agents in your workflow can filter by them later.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;This tutorial covered the core loop: create inbox, send, receive, inspect events. The mailbot MCP server exposes the same API surface as the SDK, so everything in the &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;mailbot documentation&lt;/a&gt; applies to what your AI client can do.&lt;/p&gt;

&lt;p&gt;A few directions to explore from here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event notifications&lt;/strong&gt;: Set up a webhook to push inbound messages to your own endpoint, so your agent reacts in real time rather than polling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain verification&lt;/strong&gt;: Verify a custom sending domain so outbound messages use your own address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance checks&lt;/strong&gt;: Use the compliance tools to run readiness checks before sending to a new list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP integration is v1. Feedback from real usage is how it improves. If you run into edge cases with your specific AI client configuration, the documentation is the right place to start: &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mailtrap.io/blog/claude-desktop-send-email/" rel="noopener noreferrer"&gt;Mailtrap MCP Tutorial: Sending Email from an AI Desktop Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mailercheck.com/articles/email-mcp-server" rel="noopener noreferrer"&gt;MailerCheck: Guide to Email MCP Servers (Plus, 6 You Can Use Today)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/develop/build-server" rel="noopener noreferrer"&gt;Model Context Protocol: Build an MCP Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>email</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building an AI Support Agent That Sends Real Email (Not Just Chat)</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 16:55:54 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/building-an-ai-support-agent-that-sends-real-email-not-just-chat-59oc</link>
      <guid>https://dev.to/yopiesuryadi/building-an-ai-support-agent-that-sends-real-email-not-just-chat-59oc</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Is Not the AI
&lt;/h2&gt;

&lt;p&gt;Most teams building AI support agents hit the same wall. The AI classification works fine in testing. The prompt responses look reasonable. But when they try to connect it to actual email, things fall apart fast. The inbox is shared with marketing sends. There is no way to listen for inbound messages without polling. Replies break the thread. Nobody knows whether the automated response was actually delivered.&lt;/p&gt;

&lt;p&gt;As one developer put it in a thread on &lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;r/AI_Agents&lt;/a&gt;: "What begins as simple email context evolves into a substantial infrastructure project." That quote describes the experience of most teams within the first week of building a real support agent, not a demo.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://composio.dev/blog/why-ai-agent-pilots-fail-2026-integration-roadmap" rel="noopener noreferrer"&gt;Composio AI Agent Report&lt;/a&gt; is direct about the root cause: integration failure, not model failure, is the number one reason AI agent pilots fail in production. The report identifies "brittle connectors" as a specific trap, where one-off integrations work in isolation but break the moment real email volume hits, or when email clients format messages differently than expected.&lt;/p&gt;

&lt;p&gt;This post is a comprehensive walkthrough for building a support agent that avoids those failure modes. It covers everything from creating a dedicated inbox, to listening for inbound messages, to classifying intent, to confirming delivery, to escalating uncertain cases to a human reviewer. If you want the 30-minute quickstart version, the existing &lt;a href="https://getmail.bot/blog" rel="noopener noreferrer"&gt;Build an Email AI Agent in 30 Minutes&lt;/a&gt; post covers the basics. This post is for teams who want something production-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Dedicated Infrastructure Matters
&lt;/h2&gt;

&lt;p&gt;A support agent needs its own inbox, its own event notification listener, and a reliable threading model. Sharing an inbox with other email processes introduces noise that defeats classification before the AI ever sees a message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://instantly.ai/blog/automate-email-triage-classification-ai/" rel="noopener noreferrer"&gt;Instantly's email triage research&lt;/a&gt; found that 70 to 80 percent of routine support emails can be classified and responded to automatically, but only when the classification system has clean, well-scoped input. Routing all company email through one inbox and asking an agent to sort it out is not a clean input.&lt;/p&gt;

&lt;p&gt;It is worth noting that we run mailbot's own support inbox this way. The architecture described in this post is not hypothetical. You can read about it in the &lt;a href="https://getmail.bot/blog/we-run-our-own-support-on-our-own-api" rel="noopener noreferrer"&gt;mailbot dogfooding post&lt;/a&gt;, which covers how we use our own API to handle support at the company level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Dedicated Inbox
&lt;/h2&gt;

&lt;p&gt;Start by initializing the SDK and creating an inbox specifically for support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;support-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Inbox created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you an isolated address (something like &lt;code&gt;support-agent@yourdomain.getmail.bot&lt;/code&gt;) that receives only inbound support email. No newsletter noise, no transactional sends from other systems. Your classifier gets a clean channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Register an Event Notification Listener
&lt;/h2&gt;

&lt;p&gt;Polling an inbox on an interval is the third failure trap identified in the &lt;a href="https://composio.dev/blog/why-ai-agent-pilots-fail-2026-integration-roadmap" rel="noopener noreferrer"&gt;Composio report&lt;/a&gt;, labeled the "Polling Tax." It wastes resources, introduces latency, and adds another surface where things can fail silently.&lt;/p&gt;

&lt;p&gt;Register an event notification endpoint instead. The SDK makes this a single call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-agent.example.com/inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message.inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Note: Webhooks fire for all inboxes. Filter by inboxId in your /inbound handler.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Listener registered:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your endpoint at &lt;code&gt;/inbound&lt;/code&gt; will now receive a POST payload every time a new message arrives in the support inbox. No polling required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Receive and Read the Inbound Message
&lt;/h2&gt;

&lt;p&gt;When your endpoint receives a notification, it includes the &lt;code&gt;inboxId&lt;/code&gt; and &lt;code&gt;messageId&lt;/code&gt;. Use those to fetch the full message and the thread context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;threadId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch the individual message&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch the full thread for context&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;threadId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Pass to your classifier&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;classifyIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetching the full thread via &lt;code&gt;client.threads.get()&lt;/code&gt; is important for repeat customers or ongoing issues. A support ticket about a billing error in the third reply looks very different without the first two messages. Thread context prevents your classifier from treating it as a fresh, unrelated inquiry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Classify Intent and Reply
&lt;/h2&gt;

&lt;p&gt;Your AI classifier receives the message text and thread context and returns an intent label plus a confidence score. The exact implementation of your classifier is up to you. The important part is that this function returns something structured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;classifyIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Call your AI classification layer here&lt;/span&gt;
  &lt;span class="c1"&gt;// Return: { intent: string, confidence: number, suggestedReply: string }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://instantly.ai/blog/automate-email-triage-classification-ai/" rel="noopener noreferrer"&gt;Instantly's research&lt;/a&gt; shows that 70 to 80 percent of routine support emails fall into a small set of intent categories: order status, refund request, account access, and general inquiry. A well-tuned classifier handles the bulk of volume without human review.&lt;/p&gt;

&lt;p&gt;When confidence is above your threshold, reply in the same thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suggestedReply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;escalateToHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;client.messages.reply()&lt;/code&gt; keeps the response inside the original thread. The customer's email client shows it as a continuation of the same conversation, not a new message. This matters both for the customer experience and for the threading chain that future AI classification will need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Verify Delivery with the Event Timeline
&lt;/h2&gt;

&lt;p&gt;Sending a reply is not the same as delivering it. Network issues, misconfigured DNS, and provider-side throttling can all cause a message to leave your system without reaching the recipient.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;client.engagement.messageTimeline()&lt;/code&gt; to confirm the delivery path after sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engagement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messageTimeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delivered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opened&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;delivered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reply not confirmed delivered. Flagging for review.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Trigger retry or alert here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of operational check that separates a demo agent from a production one. If a customer does not receive the reply, the next message they send will be an escalation in frustration. Catching delivery failures early gives you time to intervene before that happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Escalate to a Human When Confidence Is Low
&lt;/h2&gt;

&lt;p&gt;When the classifier's confidence falls below your threshold, the message should go to a human reviewer rather than being sent an automated reply that may be wrong or tone-deaf.&lt;/p&gt;

&lt;p&gt;The pattern has two parts: label the message so it appears in the escalation queue, then notify a human agent via a separate inbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escalateToHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Label the message in the support inbox&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateLabels&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escalated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Send notification to human agent inbox&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HUMAN_AGENT_INBOX_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;support-team@yourcompany.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escalation Required: Low Confidence Classification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Message ID &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was classified as "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" with confidence &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Please review and respond manually.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is consistent with findings from &lt;a href="https://www.eesel.ai/blog/best-practices-for-human-handoff-in-chat-support" rel="noopener noreferrer"&gt;Eesel AI's analysis of human handoff best practices&lt;/a&gt;, which identifies confidence thresholds and intent-specific triggers as the most reliable escalation signals. Keywords like "refund," "cancel," or "legal" warrant a lower threshold regardless of overall confidence.&lt;/p&gt;

&lt;p&gt;The label approach keeps your support inbox organized. Messages labeled &lt;code&gt;escalated&lt;/code&gt; appear separately from those the agent handled autonomously. You get a natural audit trail without building a separate database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Check Compliance Readiness Before Going Live
&lt;/h2&gt;

&lt;p&gt;Before routing real customer email through the agent, run a compliance readiness check on the inbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readiness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compliance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readiness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Compliance status:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readiness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks that the inbox has proper configuration for unsubscribe handling, opt-out tracking, and other requirements that apply to automated email senders. Running this before go-live avoids situations where a compliance gap surfaces only after you have been sending at volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It Together
&lt;/h2&gt;

&lt;p&gt;The full architecture looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A dedicated support inbox receives inbound email cleanly.&lt;/li&gt;
&lt;li&gt;An event notification listener fires your handler on each new message.&lt;/li&gt;
&lt;li&gt;Your handler fetches the message and full thread context.&lt;/li&gt;
&lt;li&gt;Your AI classifier returns an intent and confidence score.&lt;/li&gt;
&lt;li&gt;High-confidence intents trigger an automated reply via &lt;code&gt;client.messages.reply()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The event timeline confirms delivery after each send.&lt;/li&gt;
&lt;li&gt;Low-confidence intents are labeled &lt;code&gt;escalated&lt;/code&gt; and routed to a human agent via a second inbox.&lt;/li&gt;
&lt;li&gt;Compliance readiness is verified before production launch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We built and run this exact pattern for mailbot's own support. The &lt;a href="https://getmail.bot/blog/we-run-our-own-support-on-our-own-api" rel="noopener noreferrer"&gt;dogfooding post&lt;/a&gt; goes into detail on how the live system handles real volume and where we had to adjust our confidence thresholds over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Infrastructure Is the Product
&lt;/h2&gt;

&lt;p&gt;The AI classifier is the part that gets the most attention in conversations about AI support agents. But as the &lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;r/AI_Agents community has found directly&lt;/a&gt;, the classifier is rarely where things break. The email infrastructure underneath it is where fragility lives: brittle polling loops, lost thread context, unconfirmed delivery, no human fallback.&lt;/p&gt;

&lt;p&gt;The steps in this guide address each of those failure points specifically. A dedicated inbox eliminates noise. Event notifications replace polling. &lt;code&gt;client.threads.get()&lt;/code&gt; preserves context. &lt;code&gt;client.engagement.messageTimeline()&lt;/code&gt; confirms delivery. Labels and a second inbox create a human escalation path. Compliance readiness checks prevent surprises at go-live.&lt;/p&gt;

&lt;p&gt;Ready to start building? The full SDK reference is at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/AI_Agents/comments/1rkhpqu/email_context_for_ai_agents_is_way_harder_than_it/" rel="noopener noreferrer"&gt;r/AI_Agents: Email context for AI agents is way harder than it looks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://composio.dev/blog/why-ai-agent-pilots-fail-2026-integration-roadmap" rel="noopener noreferrer"&gt;Composio: Why AI Agent Pilots Fail in 2026 (Integration Roadmap)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://instantly.ai/blog/automate-email-triage-classification-ai/" rel="noopener noreferrer"&gt;Instantly: Automate Email Triage Classification with AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.eesel.ai/blog/best-practices-for-human-handoff-in-chat-support" rel="noopener noreferrer"&gt;Eesel AI: Best Practices for Human Handoff in Chat Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://getmail.bot/blog/we-run-our-own-support-on-our-own-api" rel="noopener noreferrer"&gt;mailbot: We Run Our Own Support on Our Own API&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>email</category>
      <category>ai</category>
      <category>typescript</category>
      <category>automation</category>
    </item>
    <item>
      <title>Why Your Agent Needs Its Own Email Address, Not a Shared Mailbox</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 09:43:15 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/why-your-agent-needs-its-own-email-address-not-a-shared-mailbox-6go</link>
      <guid>https://dev.to/yopiesuryadi/why-your-agent-needs-its-own-email-address-not-a-shared-mailbox-6go</guid>
      <description>&lt;p&gt;You have three agents running different workflows. Support, onboarding, and billing. All three send email from the same address: &lt;a href="mailto:team@yourcompany.com"&gt;team@yourcompany.com&lt;/a&gt;. A customer replies to a billing notification. The support agent picks it up. The onboarding agent ignores it. Nobody knows which agent should own the conversation.&lt;/p&gt;

&lt;p&gt;This is what happens when agents share a mailbox. It works in demos. It breaks in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shared Mailbox Problem
&lt;/h2&gt;

&lt;p&gt;Shared mailboxes were designed for human teams. A group of people monitoring a single address, manually triaging incoming messages, and collectively managing responses. The assumption: someone is reading everything and routing it to the right person.&lt;/p&gt;

&lt;p&gt;Agents don't work this way. An agent processing inbound email needs to know that every message arriving in its inbox is relevant to its workflow. When agents share an inbox, every agent receives every message. Filtering becomes your problem. Thread ownership becomes ambiguous. And when two agents both try to respond to the same email, the customer gets duplicate replies from the same address with no way to tell them apart.&lt;/p&gt;

&lt;p&gt;The core issue: assigning a single email address to all agents makes it difficult to track which agent is responsible for specific communications. Forwarding rules can fail. Routing logic adds complexity that grows with every new agent. And the challenge isn't just having an inbox per agent — it's maintaining a reliable and searchable state for those inboxes that the entire system can trust.&lt;/p&gt;

&lt;p&gt;These are infrastructure problems, not application logic problems. And they don't go away by writing better routing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Gmail and Outlook Don't Solve This
&lt;/h2&gt;

&lt;p&gt;The first instinct for most developers is to use Gmail or Outlook. Create a Google Workspace account per agent, connect via OAuth, and start sending.&lt;/p&gt;

&lt;p&gt;This breaks down fast.&lt;/p&gt;

&lt;p&gt;Gmail's OAuth 2.0 requirements &lt;a href="https://www.getmailbird.com/gmail-oauth-authentication-changes-user-guide/" rel="noopener noreferrer"&gt;reached full enforcement in March 2025&lt;/a&gt;, replacing password-based authentication entirely. Every agent now needs a proper OAuth flow with token refresh handling. For a single agent, that's manageable. For twenty agents, you're maintaining twenty separate OAuth sessions, each with its own token lifecycle, scope configuration, and failure modes.&lt;/p&gt;

&lt;p&gt;Gmail also wasn't priced for this. As &lt;a href="https://www.agentmail.to/blog/why-ai-agents-need-email" rel="noopener noreferrer"&gt;AgentMail's analysis notes&lt;/a&gt;, Google Workspace costs $12 per inbox per month. At 100 agents, that's $14,400 per year just for email addresses, before you've sent a single message.&lt;/p&gt;

&lt;p&gt;Then there's the rate limit problem. Gmail imposes sending limits per account. &lt;a href="https://www.computing.co.uk/news/2026/microsoft-drops-exchange-changes" rel="noopener noreferrer"&gt;Microsoft recently attempted to cap Exchange Online at 2,000 external recipients per day&lt;/a&gt; and had to reverse the change after customer backlash about broken automated workflows. These limits assume a human sending cadence. Agents operate at machine speed, and consumer email platforms weren't built for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Dedicated Agent Inbox Looks Like
&lt;/h2&gt;

&lt;p&gt;The alternative is programmable inboxes: email addresses created via API, one per agent or one per workflow, each with its own identity and its own message stream.&lt;/p&gt;

&lt;p&gt;With mailbot, creating an inbox is a single API call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a dedicated inbox for your support agent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supportInbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Support Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email_prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;support-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a separate inbox for your billing agent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingInbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Billing Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email_prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;billing-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each inbox gets its own email address. Messages sent to &lt;code&gt;support-agent@yourdomain.mailbot.id&lt;/code&gt; arrive only in the support agent's inbox. The billing agent never sees them. No routing logic. No filtering. The isolation is structural, not application-level.&lt;/p&gt;

&lt;p&gt;When an agent needs to reply, it uses its own identity. The customer sees a consistent sender address. Thread headers are handled automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Support agent replies to a customer message&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;supportInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msg_inbound_customer_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We found the issue. Your subscription has been corrected.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// In-Reply-To and References headers are set automatically&lt;/span&gt;
&lt;span class="c1"&gt;// The customer sees a threaded reply from support-agent@yourdomain.mailbot.id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No shared address. No ambiguity about which agent sent the response. No threading collisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reputation Isolation
&lt;/h2&gt;

&lt;p&gt;There's a deeper infrastructure reason to give each agent its own identity: sender reputation.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://www.twilio.com/en-us/blog/insights/email-reputation-101-ip-reputation-vs-domain-reputation" rel="noopener noreferrer"&gt;Twilio's guide on email reputation&lt;/a&gt;, domain reputation follows you everywhere. IP reputation can be rebuilt in 2 to 4 weeks, but domain reputation takes 6 to 12 weeks to recover. And Gmail now prioritizes domain-based filtering over IP reputation alone.&lt;/p&gt;

&lt;p&gt;When all your agents share a single sending address, a reputation problem caused by one agent affects every agent. If your outreach agent triggers spam complaints, your support agent's replies start landing in spam too. The workflows are unrelated, but the reputation is shared.&lt;/p&gt;

&lt;p&gt;With dedicated inboxes, reputation risk is contained. Each agent operates under its own address. If one workflow has deliverability problems, you can investigate and fix it without disrupting every other agent's email.&lt;/p&gt;

&lt;p&gt;This is the same principle behind subdomain isolation in traditional email infrastructure, applied at the agent level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thread Ownership and Conversation Continuity
&lt;/h2&gt;

&lt;p&gt;When agents share a mailbox, thread ownership becomes a constant source of bugs. Common failures include aligning message threads with the right workflow — outreach sequences, support escalations, deal stages — and ensuring replies aren't lost when agents restart or hand off conversations.&lt;/p&gt;

&lt;p&gt;Dedicated inboxes solve this structurally. Each inbox has its own thread list. You can retrieve all threads for a specific agent without filtering across a shared pool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// List all threads for the support agent's inbox&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supportInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Get a specific thread with full message history&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;supportInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;thread_abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a customer replies, the message arrives in the inbox that owns the conversation. The agent picks it up with full context. No race conditions with other agents. No need to build your own thread routing layer on top of a shared mailbox.&lt;/p&gt;

&lt;p&gt;And when you need to trace what happened, events are scoped to the thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check delivery events for a specific thread&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;thread_abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: delivered, opened, bounced events with timestamps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each agent's conversation history is self-contained and traceable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compliance Angle
&lt;/h2&gt;

&lt;p&gt;For organizations handling sensitive data, shared mailboxes create audit problems. When three agents share an address, who sent what? Which agent accessed which customer data? Audit trails become tangled because the identity layer is shared.&lt;/p&gt;

&lt;p&gt;Dedicated inboxes produce clean audit logs. Every message is tied to a specific inbox, a specific agent, a specific workflow. mailbot's audit log tracks actions per inbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Review audit trail&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auditEntries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auditLog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;supportInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For teams working toward compliance frameworks like ISO 27001, this separation of concerns matters. It's easier to demonstrate access control and data isolation when each agent operates within its own boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Need More Than One Inbox
&lt;/h2&gt;

&lt;p&gt;The real power of programmable inboxes shows up in multi-agent architectures. Common patterns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-workflow inboxes.&lt;/strong&gt; A support agent, a billing agent, and a notifications agent each get their own address. Customers know who they're talking to. Each agent manages its own conversation history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-customer inboxes.&lt;/strong&gt; For multi-tenant applications, create an inbox per customer or per account. Inbound messages are automatically isolated. No risk of data leaking between tenants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ephemeral inboxes.&lt;/strong&gt; Need a temporary email for a verification flow or a one-off test? Create an inbox, use it, delete it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a temporary inbox for a verification flow&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tempInbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verification Flow - User 8847&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Wait for the verification email to arrive&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tempInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subjectContains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verify your email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeoutMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Process the verification, then clean up&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tempInbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each pattern is possible because inboxes are API primitives, not manually provisioned resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Sharing, Start Isolating
&lt;/h2&gt;

&lt;p&gt;Shared mailboxes were built for human teams passing messages around. Agent workflows need structural isolation: one identity per agent, one message stream per workflow, one reputation boundary per sending address.&lt;/p&gt;

&lt;p&gt;mailbot gives you programmable inboxes via API. Create them with a single API call. Each one gets its own address, its own threads, its own event history. No OAuth complexity, no per-seat pricing, no manual setup.&lt;/p&gt;

&lt;p&gt;Start building at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.getmailbird.com/gmail-oauth-authentication-changes-user-guide/" rel="noopener noreferrer"&gt;Mailbird: Gmail OAuth 2.0 Changes 2026&lt;/a&gt;. Timeline of Google's OAuth enforcement reaching full rollout in March 2025.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.agentmail.to/blog/why-ai-agents-need-email" rel="noopener noreferrer"&gt;AgentMail: Why AI Agents Need Email&lt;/a&gt;. Gmail pricing at $12/inbox/month, scaling cost analysis for multi-agent setups.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.computing.co.uk/news/2026/microsoft-drops-exchange-changes" rel="noopener noreferrer"&gt;Computing.co.uk: Microsoft drops Exchange changes after customer backlash&lt;/a&gt;. Microsoft's reversed 2,000 external recipient daily cap on Exchange Online.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.twilio.com/en-us/blog/insights/email-reputation-101-ip-reputation-vs-domain-reputation" rel="noopener noreferrer"&gt;Twilio: IP Reputation vs. Domain Reputation&lt;/a&gt;. Domain reputation recovery takes 6 to 12 weeks; Gmail prioritizes domain reputation over IP.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Suggested tags for Dev.to / Hashnode: &lt;code&gt;email-api&lt;/code&gt;, &lt;code&gt;ai-agents&lt;/code&gt;, &lt;code&gt;developer-tools&lt;/code&gt;, &lt;code&gt;programmable-inbox&lt;/code&gt;, &lt;code&gt;email-infrastructure&lt;/code&gt;, &lt;code&gt;devops&lt;/code&gt;&lt;/p&gt;

</description>
      <category>emailapi</category>
      <category>aiagents</category>
      <category>programmableinbox</category>
      <category>developertools</category>
    </item>
    <item>
      <title>The Case for Email-Native Observability</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 09:42:27 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/the-case-for-email-native-observability-20b5</link>
      <guid>https://dev.to/yopiesuryadi/the-case-for-email-native-observability-20b5</guid>
      <description>&lt;p&gt;Your email API says "delivered." Your user says they never got it. You check the dashboard. It shows a green checkmark. The user checks their spam folder. Nothing there either.&lt;/p&gt;

&lt;p&gt;Somewhere between your API call and the recipient's mailbox, the message vanished. And you have no way to trace where it went.&lt;/p&gt;

&lt;p&gt;This is the default experience with most email APIs. They confirm acceptance, not arrival. And for developers building agent workflows where email is the interface, that gap is where failures hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap Between "Sent" and "Seen"
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://unspam.email/articles/email-deliverability-report/" rel="noopener noreferrer"&gt;Unspam's 2025 deliverability report&lt;/a&gt;, only 60% of emails reach a visible mailbox location. 36% land in spam. 4% are blocked or disappear entirely. These numbers come from millions of email tests across consumer and enterprise providers.&lt;/p&gt;

&lt;p&gt;The key insight: technical delivery success now overstates actual inbox reach by roughly 40%. Your API says "delivered" because the receiving server accepted the message. But acceptance and inbox placement are not the same thing.&lt;/p&gt;

&lt;p&gt;For a marketing team, a 60% inbox rate is a conversion problem. For a developer running an AI support agent, it's an operational failure. If four out of ten replies your agent sends don't reach the customer, your agent isn't slow. It's broken. And without per-message visibility, you won't know which four.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Observability Means in Infrastructure (and Why Email Doesn't Have It)
&lt;/h2&gt;

&lt;p&gt;In modern infrastructure, observability rests on &lt;a href="https://www.ibm.com/think/insights/observability-pillars" rel="noopener noreferrer"&gt;three pillars: logs, metrics, and traces&lt;/a&gt;. Logs tell you what happened. Metrics tell you how much. Traces show you the path a request took from start to finish.&lt;/p&gt;

&lt;p&gt;Every serious backend service has all three. Your database has query traces. Your API gateway has request logs. Your CDN has latency metrics. When something breaks, you open a dashboard and follow the breadcrumbs.&lt;/p&gt;

&lt;p&gt;Email doesn't work this way. Most email APIs give you one of three things: a delivery status ("sent," "delivered," "bounced"), an aggregate dashboard (open rates, bounce rates across a campaign), or raw SMTP logs that require you to parse headers manually.&lt;/p&gt;

&lt;p&gt;None of these are observability. A delivery status is a single data point, not a trace. An aggregate dashboard hides the individual failures that matter most. And SMTP logs are what you read when everything else has failed and you're three hours into an incident.&lt;/p&gt;

&lt;p&gt;The debugging challenge compounds when you consider how many layers are involved: email transport, agent logic, and delivery mechanics. A bug can live in any of those layers, and the symptoms rarely point to the actual source. Without per-message tracing, developers often waste hours investigating agent logic when the root cause is an infrastructure failure in the inbound or outbound email layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Email-Native Observability Looks Like
&lt;/h2&gt;

&lt;p&gt;Email-native observability means treating every message as a traceable event with a clear input, a set of processing steps, and a measurable outcome.&lt;/p&gt;

&lt;p&gt;Not aggregate stats. Not "check the logs." A timeline per message.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in mailbot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event timeline.&lt;/strong&gt; Every message gets a chronological sequence of events: &lt;code&gt;message.delivered&lt;/code&gt;, &lt;code&gt;message.opened&lt;/code&gt;, &lt;code&gt;message.clicked&lt;/code&gt;, &lt;code&gt;message.bounced&lt;/code&gt;. Not batched into a daily report. Updated in real time, visible per message, per recipient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event replay.&lt;/strong&gt; When a delivery fails, you don't just see that it failed. You can replay the event sequence to understand what happened. Was it a temporary deferral that resolved on retry? A permanent 5xx rejection? A DMARC alignment failure? The replay shows the full sequence, not just the final status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inbound visibility.&lt;/strong&gt; Observability isn't just about outbound. When your agent receives an email, you need to see the raw message, the parsed content, and the event notification that triggered your workflow. If classification went wrong, was it because the email body was garbled, quoted text wasn't separated, or the message arrived with unexpected headers?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Send a message and get a trackable handle&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbox_support_01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Re: Ticket #2847&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;ve&lt;/span&gt; &lt;span class="nx"&gt;identified&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;fix&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;rolling&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
});

// message.id ties to a full event timeline in your dashboard:
// message.delivered → message.opened → message.clicked
// or: message.bounced (with bounce code and reason)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard shows this as a visual timeline. One message, one trace. The same model that modern infrastructure uses for request tracing, applied to email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Agent Workflows
&lt;/h2&gt;

&lt;p&gt;Agent workflows multiply the observability problem. A human sending one email can check their sent folder and follow up if there's no response. An agent sending fifty replies an hour needs infrastructure-level visibility.&lt;/p&gt;

&lt;p&gt;Consider a support agent that processes inbound tickets. A customer emails in. The agent classifies the ticket, drafts a reply, and sends it. Without per-message observability, the developer has no way to answer basic questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the reply reach the customer's inbox?&lt;/li&gt;
&lt;li&gt;Did the customer open it?&lt;/li&gt;
&lt;li&gt;If it bounced, what was the bounce code?&lt;/li&gt;
&lt;li&gt;If the original email was misclassified, was it because of a parsing problem or a classification problem?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not edge cases. They are the normal debugging surface of any email-based agent workflow. And most email APIs leave developers blind to all of them.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://unspam.email/articles/email-deliverability-report/" rel="noopener noreferrer"&gt;Unspam report&lt;/a&gt; reinforces this: "Technical delivery success now overstates real inbox reach by approximately 40%." If you're relying on your API's "delivered" status to confirm that your agent's reply reached the customer, you're trusting a metric that's wrong four out of ten times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build With Visibility, Not Hope
&lt;/h2&gt;

&lt;p&gt;Email is infrastructure. And infrastructure without observability is a black box you're choosing to trust.&lt;/p&gt;

&lt;p&gt;mailbot treats every message as a first-class observable event. Event timeline, event replay, per-message delivery tracking, inbound and outbound. The same visibility model that developers expect from every other part of their stack, applied to email.&lt;/p&gt;

&lt;p&gt;Start building at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emailapi</category>
      <category>observability</category>
      <category>aiagents</category>
      <category>developertools</category>
    </item>
    <item>
      <title>We Run Our Own Support on Our Own API</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 09:37:27 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/we-run-our-own-support-on-our-own-api-38kb</link>
      <guid>https://dev.to/yopiesuryadi/we-run-our-own-support-on-our-own-api-38kb</guid>
      <description>&lt;p&gt;A support ticket comes in. An AI agent reads it, classifies it, drafts a reply with full context, and sends it back. The entire loop runs on the same API we ship to developers.&lt;/p&gt;

&lt;p&gt;This is not a demo. This is how we handle support at mailbot, every day, on our own infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best Test Is the One You Can't Skip
&lt;/h2&gt;

&lt;p&gt;Dogfooding is not a new idea. &lt;a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food" rel="noopener noreferrer"&gt;Microsoft coined the term in the 1980s&lt;/a&gt; when a manager sent an internal memo titled "Eating our own Dogfood." Slack built its product by &lt;a href="https://www.theverge.com/2014/8/12/5991005/slack-is-killing-email-maybe" rel="noopener noreferrer"&gt;using it internally for months&lt;/a&gt; before anyone else could.&lt;/p&gt;

&lt;p&gt;The pattern is clear: the products that hold up in production are the ones their own teams depend on. Not in a quarterly review. Not in a staged demo. In the moment when something breaks and you need the tool to work.&lt;/p&gt;

&lt;p&gt;For mailbot, that moment is support. When a developer emails us with a problem, the clock starts. If our own infrastructure can't receive that email, classify it, and help us respond quickly, we have no business selling it to anyone else.&lt;/p&gt;

&lt;p&gt;So we put our entire support workflow on our own API. Not as a marketing exercise. As an operational decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The flow is straightforward.&lt;/p&gt;

&lt;p&gt;A customer sends an email to our support inbox. That inbox is a programmable mailbot inbox, the same kind any developer can create through our API. The message arrives, triggers an event notification, and gets picked up by our agent workflow.&lt;/p&gt;

&lt;p&gt;The AI agent does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Classifies the ticket.&lt;/strong&gt; Is this a bug report, a billing question, a feature request, or something else? The agent reads the full message body and assigns a category and priority.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Drafts a reply.&lt;/strong&gt; Using the classification and the original message as context, the agent generates a response. Not a template. A contextual reply that addresses what the customer actually wrote.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sends the reply through the mailbot API.&lt;/strong&gt; The same &lt;code&gt;messages.send&lt;/code&gt; endpoint our users call. Same SDK. Same authentication. Same event timeline tracking whether the reply was delivered, opened, or bounced.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Receive inbound via event notification handler&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhooks/mailbot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message.inbound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;classifyTicket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateReply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thread stays intact because mailbot handles &lt;code&gt;In-Reply-To&lt;/code&gt; and &lt;code&gt;References&lt;/code&gt; headers automatically. The customer sees a normal email reply, threaded under their original message.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;Running support on our own API exposed things that testing alone never would.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event notifications need to be fast and reliable.&lt;/strong&gt; When a customer writes in, the event notification has to fire immediately. Early on, we found edge cases where retry timing wasn't optimal for real-time support workflows. We fixed them. Those fixes shipped to every mailbot user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thread continuity is harder than it looks.&lt;/strong&gt; Customers forward emails, CC other people, and reply from different addresses. If your threading logic only handles the clean case, production will embarrass you. We tightened our threading engine because our own agent kept losing context in messy threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-message visibility is not optional.&lt;/strong&gt; When a support reply bounces, we need to know within seconds, not the next day. Our event timeline, the same one available in the mailbot dashboard, became our primary debugging tool. If a message shows "delivered" but the customer says they didn't get it, we can trace the full delivery path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI classification is only as good as the data it gets.&lt;/strong&gt; The agent receives the raw email, headers and all. Clean parsing matters. If the message body comes through garbled or if quoted text isn't properly separated, classification degrades. We improved our inbound parsing because we were eating our own output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Developers
&lt;/h2&gt;

&lt;p&gt;Every email infrastructure provider claims reliability. Fewer can show you that they depend on it themselves.&lt;/p&gt;

&lt;p&gt;When we tell developers that mailbot handles two-way email conversations, thread continuity, and per-message observability, we're describing our own production workflow. Not a feature spec. Not a roadmap item. The system we wake up to every morning.&lt;/p&gt;

&lt;p&gt;This is also why we catch issues fast. We're not waiting for a customer to report that event notifications are delayed or that threading broke on a forwarded message. We hit those problems first because we're running the same workflows our users run.&lt;/p&gt;

&lt;p&gt;The best developer tools get built this way: used internally until they're genuinely useful to the team, then shipped externally. If you want to build infrastructure that works under pressure, put yourself under that pressure first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Loop
&lt;/h2&gt;

&lt;p&gt;mailbot is email infrastructure that gives AI agents the ability to email. We don't just say that. We run our own operations on it.&lt;/p&gt;

&lt;p&gt;Every support ticket that comes in gets classified by AI, drafted with context, and sent back through the same API we ship to developers. One stack. Same endpoints. Same event timeline. Same deliverability.&lt;/p&gt;

&lt;p&gt;If you're building an agent workflow that needs to send, receive, and act on email, start with the infrastructure that its own team trusts enough to run support on.&lt;/p&gt;

&lt;p&gt;Start building at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emailapi</category>
      <category>aiagents</category>
      <category>developertools</category>
      <category>dogfooding</category>
    </item>
    <item>
      <title>Your Email API Doesn't Have an Opinion About Deliverability</title>
      <dc:creator>Yopie Suryadi</dc:creator>
      <pubDate>Sun, 29 Mar 2026 09:37:26 +0000</pubDate>
      <link>https://dev.to/yopiesuryadi/your-email-api-doesnt-have-an-opinion-about-deliverability-1500</link>
      <guid>https://dev.to/yopiesuryadi/your-email-api-doesnt-have-an-opinion-about-deliverability-1500</guid>
      <description>&lt;p&gt;Your agent sends a password reset. The user never gets it. You check the logs. "Sent," they say. That's it. No bounce reason. No retry trace. No idea whether the message hit the inbox, landed in spam, or disappeared into the void.&lt;/p&gt;

&lt;p&gt;You didn't have a sending problem. You had a deliverability problem. And your email API had nothing to say about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2026 Deliverability Landscape Changed Everything
&lt;/h2&gt;

&lt;p&gt;Email deliverability used to be forgiving. Gmail would route questionable messages to spam. Outlook would give you a second chance. The system had tolerance for "almost compliant."&lt;/p&gt;

&lt;p&gt;That era is over.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.valimail.com/blog/google-email-compliance-enforcement/" rel="noopener noreferrer"&gt;November 2025, Gmail escalated from warnings to outright SMTP rejection&lt;/a&gt; for non-compliant bulk senders. Messages that fail authentication don't go to spam. They never arrive. &lt;a href="https://techcommunity.microsoft.com/blog/microsoftdefenderforoffice365blog/strengthening-email-ecosystem-outlook%E2%80%99s-new-requirements-for-high%E2%80%90volume-senders/4399730" rel="noopener noreferrer"&gt;Microsoft followed in May 2025&lt;/a&gt; with its own enforcement for senders above 5,000 messages per day — returning &lt;code&gt;550; 5.7.515 Access denied&lt;/code&gt; for non-compliant domains. &lt;a href="https://senders.yahooinc.com/best-practices/" rel="noopener noreferrer"&gt;Yahoo had already tightened rules&lt;/a&gt; across all its consumer domains starting February 2024.&lt;/p&gt;

&lt;p&gt;The result: a binary pass or fail model. Your SPF, DKIM, and DMARC either align perfectly, or your messages get rejected at the protocol level. No gradation. No grace period.&lt;/p&gt;

&lt;p&gt;For developers building agent workflows that depend on email, this isn't a marketing concern. It's an infrastructure concern. A support agent that sends a reply into the void is worse than one that doesn't reply at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most Email APIs Don't Own the Delivery Path
&lt;/h2&gt;

&lt;p&gt;Here's what happens when you send through a typical email API. Your application calls the API. The API accepts the message. Then it hands it off to a third-party MTA, one you don't control, can't configure, and probably can't even see.&lt;/p&gt;

&lt;p&gt;That MTA handles authentication, queue management, retry logic, and reputation. If something goes wrong, your API provider might surface a "bounced" status. Or it might just say "sent" and leave you guessing.&lt;/p&gt;

&lt;p&gt;This is the default experience with most email APIs on the market today. They're routing layers, not delivery infrastructure. They accept your message and pass it to someone else's stack.&lt;/p&gt;

&lt;p&gt;The problem compounds for agent workflows. When an AI agent sends a follow-up, a ticket update, or a transactional notification, the developer needs to know: did it land? Was it opened? Did it bounce? Why?&lt;/p&gt;

&lt;p&gt;A "sent" status and a shrug is not observability. It's a liability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MTA Ownership Matters
&lt;/h2&gt;

&lt;p&gt;The Mail Transfer Agent is where deliverability lives. It's the system that resolves MX records, negotiates TLS connections, enforces authentication, manages queues, and retries failed deliveries. Every decision that determines whether your message reaches the inbox happens at the MTA layer.&lt;/p&gt;

&lt;p&gt;When an email provider owns its MTA, it controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication enforcement.&lt;/strong&gt; SPF, DKIM, and DMARC configuration happens at the source, not delegated to a vendor you can't audit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reputation management.&lt;/strong&gt; IP warm-up, sending rates, and domain reputation are tuned directly, not inherited from a shared pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue visibility.&lt;/strong&gt; When a message is deferred, you can see why. When it retries, you can trace each attempt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bounce diagnostics.&lt;/strong&gt; A 5xx rejection from Gmail tells you something specific. That information should reach the developer, not get swallowed by middleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When an email provider doesn't own its MTA, it's renting all of this from someone else. And renters don't get to set the thermostat.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;mailbot runs its own proprietary MTA. That means when you send a message, we handle every step from API call to inbox placement. No hand-off. No third-party relay sitting between your code and the recipient's mail server.&lt;/p&gt;

&lt;p&gt;The practical difference shows up in three places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-message visibility.&lt;/strong&gt; Every message gets a timeline: delivered, opened, clicked, bounced. Not aggregate stats across a campaign. Per-message, per-recipient, in chronological order. When an agent sends a reply, you can trace exactly what happened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailbotClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@yopiesuryadi/mailbot-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailbotClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mb_test_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inbox_support_01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Re: Order #4821&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your replacement has shipped. Tracking number attached.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Later: check what happened on the thread&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threadId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { data: [{ type: 'message.delivered', timestamp: '...' }, { type: 'message.opened', timestamp: '...' }] }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Event replay for debugging.&lt;/strong&gt; When something fails, you don't just see that it failed. You can replay the event to understand the sequence. What was the bounce code? When did the retry happen? Was it a temporary deferral or a permanent rejection?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unified deliverability stack.&lt;/strong&gt; Because mailbot controls the MTA, authentication alignment, IP reputation, and sending behavior are tuned as a single system. Not stitched together across vendors hoping their configurations don't conflict. For context, &lt;a href="https://www.mailreach.co/blog/email-deliverability-statistics" rel="noopener noreferrer"&gt;top-performing email programs achieve inbox placement rates above 95%&lt;/a&gt;, while shared-infrastructure providers often average closer to 75–80%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Agent Workflow Problem Gets Worse Without This
&lt;/h2&gt;

&lt;p&gt;Consider a support automation workflow. An inbound email arrives. Your AI agent classifies the ticket, drafts a reply, and sends it back through your email API.&lt;/p&gt;

&lt;p&gt;If that reply bounces and your API can't tell you why, your agent is flying blind. Worse, if the bounce was caused by a misconfigured PTR record or a DMARC alignment failure on your API provider's side, you have zero ability to fix it. You're dependent on someone else's infrastructure team to resolve a problem that's blocking your customers.&lt;/p&gt;

&lt;p&gt;This isn't hypothetical. Studies show that &lt;a href="https://www.infraforge.ai/blog/reverse-dns-email-deliverability-impact" rel="noopener noreferrer"&gt;20–30% of emails with misconfigured reverse DNS settings are either rejected or sent to spam&lt;/a&gt;. Since Gmail, Microsoft, and Yahoo now &lt;a href="https://support.google.com/a/answer/81126?hl=en" rel="noopener noreferrer"&gt;enforce FCrDNS (Forward-Confirmed reverse DNS) for bulk senders&lt;/a&gt;, many mail servers outright reject connections from IPs without valid PTR records — before SPF or DKIM are even checked. If your email provider owns that infrastructure, they fix it. If they're renting it, you file a ticket and wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build on Infrastructure You Can See Through
&lt;/h2&gt;

&lt;p&gt;Email deliverability in 2026 is binary. Your messages either pass authentication and reach the inbox, or they get rejected at the gate.&lt;/p&gt;

&lt;p&gt;If your email API doesn't own the delivery path, you're trusting a black box to handle the most critical part of your workflow. For developers building AI agents, automated support, or transactional systems that depend on email actually arriving, that's a bet you don't need to make.&lt;/p&gt;

&lt;p&gt;mailbot gives you the full picture: proprietary MTA, per-message event timelines, and event replay. One stack. No hand-offs.&lt;/p&gt;

&lt;p&gt;Start building at &lt;a href="https://getmail.bot/docs/getting-started" rel="noopener noreferrer"&gt;getmail.bot/docs/getting-started&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emailapi</category>
      <category>deliverability</category>
      <category>aiagents</category>
      <category>developertools</category>
    </item>
  </channel>
</rss>
