An AI Email Agent That Sorted 12,000 Messages Without Chaos
One founder's AI email agent nearly sent a refund promise it had no authority to make. Here's the failure, the fix, and the triage-first design that finally worked.
def handle_email(email_body):
reply = client.messages.create(
model="claude-sonnet-4-6", max_tokens=500,
system="You are the founder. Reply helpfully to this email.",
messages=[{"role": "user", "content": email_body}],
).content[0].text
send(reply) # <-- every email, auto-sent, no gate
return replyThe email went out at 2:14 a.m. A founder's shiny new AI email agent, wired to auto-respond to support mail, read a frustrated customer's message and confidently promised a full refund plus three months free - a promise no human had approved and the company's policy didn't allow. The customer screenshotted it. Support spent a day cleaning it up.
That single email taught the team the lesson this whole case study is about: an email agent's first job is not to reply. It's to triage. Get triage right and response gets safe. Skip triage and you get 2 a.m. refund promises.
The Problem a Two-Person Startup Faced
The company was tiny - two founders, one shared inbox, about 300 emails a day spanning support, sales, vendor invoices, and cold pitches. The inbox was the bottleneck for everything. Real customer issues sat unread under waves of noise, and the founders were checking email at midnight just to stay above water.
They didn't need eloquent auto-replies. They needed the important 30 emails surfaced from the noisy 300, and the truly routine ones handled without a human. What they built first did the opposite.
The Wrong Approach
Version one was an auto-responder. Every incoming email hit the model, which wrote and sent a reply. Fast, hands-off, and exactly the wrong design.
[object Object], ,[object Object],(,[object Object],):
reply = client.messages.create(
model=,[object Object],, max_tokens=,[object Object],,
system=,[object Object],,
messages=[{,[object Object],: ,[object Object],, ,[object Object],: email_body}],
).content[,[object Object],].text
send(reply) ,[object Object],
,[object Object], replyWhat this does: it generates a reply to every incoming email as if it were the founder and sends it immediately, with no classification and no authority limits.
The refund fiasco was inevitable. "Reply helpfully" plus a customer begging for compensation plus no policy boundary equals an agent inventing generous terms to be helpful. The agent wasn't malfunctioning - it was doing exactly what a vague prompt told it to. That's the uncomfortable truth about most agent failures: the model followed instructions faithfully; the instructions were the problem.
Dig into why "reply helpfully" is so dangerous and you find a general principle. Helpfulness, unbounded, drifts toward whatever makes the person on the other end happiest in the moment - which for an angry customer means concessions the business never authorized. An agent has no sense of what your policy allows unless you tell it, and it has no reason to say "let me check with a human" unless you build that reason in. Every powerful auto-action an agent can take needs a matching boundary, or the action will eventually be taken in a situation you'd never have chosen. The refund email wasn't a freak event. It was the predictable output of authority without constraint.
⚠️ Common mistake: giving an email agent send authority before you've given it judgment about which emails it should touch. Response power without triage is how one bad classification becomes a public promise you can't keep.
The Correct Prompt: Triage First
The redesign put a classification gate in front of everything. Before any reply, the agent sorts each email into a category and a required action, and only a narrow, safe slice is ever eligible for auto-response.
[object Object], ,[object Object],(,[object Object],):
,[object Object], client.messages.create(
model=,[object Object],, max_tokens=,[object Object],,
system=(
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
,[object Object],
),
messages=[{,[object Object],: ,[object Object],, ,[object Object],: email_body}],
).content[,[object Object],].textWhat this does: it classifies each email by category, urgency, and required action, and structurally forbids auto-replying to anything involving money or complaints - routing those to a human-reviewed draft instead.
The refund email, under this design, gets category
supportdraft_for_human⚡ Pro tip: define your
auto_replyNotice what the triage step buys you beyond safety. Separating classification from response means each half can be evaluated on its own. You can measure classification accuracy directly - does the agent put refund requests in the right bucket? - without entangling it with reply quality. When something goes wrong, you know immediately whether the failure was a misread email or a bad draft, because they're different stages. A single "read and reply" blob gives you none of that; when it fails, you're left guessing which part broke. Decomposition isn't just safer, it's debuggable, and debuggable is what lets a two-person team actually trust an agent running unattended overnight.
Results: What the AI Email Agent Changed
Over the next quarter the agent triaged just over 12,000 emails. The founders stopped touching the inbox at midnight.
About 40% of mail was spam or cold pitches the agent flagged and filed - never seen by a human unless asked for. Roughly 15% hit the narrow auto-reply lane: shipping questions, doc-link requests, meeting confirmations, all handled in seconds. The remaining 45% - the emails that actually needed a person - arrived pre-sorted by urgency with a draft attached, so the founders answered a clean, prioritized queue instead of digging through chaos.
Zero unauthorized refund promises. The triage gate held. And there was an unexpected benefit: because every email now carried a category and a reason, the founders got a weekly readout of what their inbox was actually made of - a spike in invoice questions one month flagged a billing bug they'd have otherwise missed for weeks.
⚡ Pro tip: log every email the agent auto-replied to and sample ten a week. Auto-reply is where silent errors hide - a category that seemed safe can drift as your product changes, and weekly sampling catches it before it compounds.
How to Apply This to Your Situation
Map your own inbox to the same three-lane model: file-and-forget, safe auto-reply, and human-draft. The exact categories differ by role.
A solo consultant auto-replies only to scheduling requests and drafts everything client-facing, because relationship email is the business. A support lead at a growing company can safely widen the auto-reply lane to order status and password resets once the data shows those categories are boring and stable. A sales rep wants the agent to draft-and-prioritize inbound leads by intent, never auto-send, because a clumsy auto-reply to a hot lead costs real money.
A fourth case: an operations manager drowning in vendor and invoice mail uses the AI email agent purely as a sorter and summarizer, never a responder - and still reclaims an hour a day just from not triaging by hand. The pattern is identical; only where you draw the auto-reply line changes.
The deeper point is that "how much should the agent do" is the wrong first question. The right first question is "what does this email need," and triage answers it. Once the agent can reliably say what an email needs, you decide - per category, with data - how much of that need it's allowed to meet on its own. That ordering keeps you from ever handing the agent authority it hasn't earned. It also means the same triage layer serves a cautious solo consultant and an aggressive high-volume support team; they share the classifier and differ only in their allowlists.
One more design note from the founders' experience: they made the agent explain its routing in the
reason⚡ Pro tip: start with an empty auto-reply allowlist. Run the agent in draft-only mode for two weeks, review what it would have sent, and promote categories to auto-reply one at a time only after you've seen them behave. Earn the automation; don't assume it.
Next Steps
Build triage before response, always. An agent that sorts your inbox well is immediately valuable and can't embarrass you; an agent that auto-replies before it can classify is a liability with a countdown timer.
Keep your triage categories and the auto-reply allowlist in one versioned place rather than buried in a script. Teams running email agents store these rules in PromptABCD so the "money and complaints always go to a human" boundary is defined once and shared - because the fastest way back to a 2 a.m. refund promise is two copies of the rules that quietly drifted apart.
And review that allowlist on a schedule, not just when something breaks. Products add features, policies change, and a category that was safe to auto-reply in one quarter can quietly turn risky in the next. A ten-minute monthly check of what the agent is auto-sending - and why - is the difference between an email agent that stays trustworthy for years and one that surprises you the week after everyone stopped watching it. Automation you don't periodically re-check isn't automation; it's a bet you've forgotten you placed, and forgotten bets are the ones that quietly cost you the most.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
