Claude Scored 312 Emails/Week. 47 Were Actual Buyers.

Two hours and eleven minutes. That's the average I burned each day for a month sorting inbox across three accounts before I built this. Eleven hours a week gone to playing human classifier for people who mostly weren't going to pay me anyway.
The fix wasn't a smarter filter or a Gmail rule. It was borrowing the 3-level agent architecture that trading bots use — ingestion, scoring, action — and pointing it at my inbox instead of the market. Trading is zero-sum. Inbox intent is signal extraction from text, which is what Claude is genuinely good at. Four months in production on a home PC, $8/month in API cost, 89% precision on the buyer flag. Here's exactly how it's built.
Why a single-shot classifier fails on real inbox volume
A single Claude call that reads an email and returns "buyer or not" collapses the first time something breaks. Gmail rate-limits you, the API times out, the prompt drifts, and you have no idea which part failed because ingestion, judgment, and action are tangled into one blob.
The 3-level pattern separates concerns: level one moves data, level two scores, level three acts. Each layer has one job, one failure mode, one place to fix things.
- Isolation of failure: when Gmail's API throttled me last month, level one queued and retried while levels two and three kept chewing through the backlog.
- Isolation of change: when I tightened the scoring rubric, I only touched level two — routing rules stayed identical.
- Isolation of cost: ingestion is free (Gmail MCP), scoring is where the tokens burn (~$8/mo), routing is basically free.
The trading world figured this out years ago because they had to. Single-shot bots blow up on the first weird tick. The same collapse happens to a single-shot email classifier on the first mail-merge template that looks like spam but is actually a buyer.
Level 1: Gmail MCP ingestion into SQLite
Level one runs every 15 minutes via cron. It uses the Gmail MCP server connected to Claude to pull unread emails from the last 7 days across all three accounts. For each email it extracts exactly four fields: sender address, sender domain, subject line, and the first 200 characters of the body. No attachments, no threads, no history.
200 characters is enough. If someone can't communicate intent in the first sentence, they aren't a buyer.
CREATE TABLE emails (
id TEXT PRIMARY KEY, -- Gmail message ID
account TEXT NOT NULL,
sender_email TEXT NOT NULL,
sender_domain TEXT NOT NULL,
subject TEXT,
body_snippet TEXT, -- first 200 chars only
fetched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status TEXT DEFAULT 'unscored' -- unscored | scored | routed
);
CREATE INDEX idx_status ON emails(status);
The Gmail message ID is the primary key, so re-runs are idempotent. If the pull crashes halfway through, the next cron picks up cleanly. Everything lands in SQLite on WSL Ubuntu on my home PC. Zero cloud infra. The file is 47 MB after four months.
Ingestion rules that matter
- Idempotent by Gmail ID: never double-score the same email.
- Snippet only, not full body: keeps token cost predictable and prevents the classifier from getting distracted by signatures and disclaimers.
- Domain extracted at ingest time: level two doesn't do string parsing, it just reads a column.
Level 2: The scoring prompt that actually works
Level two is the second Claude call. It reads every row where status = 'unscored' and classifies each into one of four buckets: buyer_intent, tire_kicker, partnership, or noise. Along with the bucket, it returns a confidence score from 0 to 1 and a one-sentence justification.
The critical design choice: I don't ask Claude to predict if someone will buy. I ask it to detect concrete signals. That turns it from a fuzzy prediction problem (bad) into a text classification problem with a clear rubric (good — this is what LLMs are built for).
SCORING_PROMPT = """
Classify this email into: buyer_intent, tire_kicker, partnership, or noise.
Look for these BUYER signals in the snippet:
1. Explicit budget mention ($ figure, "budget for")
2. Timeline ("by Q3", "next month", "ASAP")
3. Specific problem to solve (not "curious about")
4. Team size or company context
5. Current tools they're replacing
Rules:
- 3+ signals present -> buyer_intent, confidence 0.75-0.95
- 1-2 signals -> tire_kicker or partnership, confidence 0.5-0.7
- 0 signals + generic -> noise, confidence 0.9+
- "love your content, quick question" with no context -> tire_kicker
Return JSON: {bucket, confidence, justification}
Email:
From: {sender_email}
Domain: {sender_domain}
Subject: {subject}
Body: {body_snippet}
"""
The four-bucket split matters. A three-bucket system (buyer / not-buyer / noise) loses partnerships, which have different economic value than tire-kickers and deserve their own routing path. A five-bucket system starts overlapping and confidence drops.
The mail-merge false positive patch
The main failure mode: cold outreach from real buyers using a mail-merge template looks identical to spam in the first 200 characters. The agent used to downgrade them to noise. I lost roughly one real deal a month to this.
The fix was a second pass at scoring time: if the sender's domain resolves to a real company website with a /pricing page, upgrade the score by 0.2. That single rule caught most of the false negatives. Precision on the buyer flag went from ~81% to the current 89%.
Level 3: Telegram routing with drafted replies
Any email scored buyer_intent with confidence above 0.75 gets pushed to my Telegram in real time with a suggested reply that Claude drafts from the full email body (not the 200-char snippet — this is the one place I pay for the full context). I read it on my phone, edit if needed, hit send.
Everything else routes automatically:
| Bucket | Confidence | Action | Latency |
|---|---|---|---|
buyer_intent |
>= 0.75 | Telegram push + drafted reply | < 30s |
buyer_intent |
0.5-0.75 | Telegram push, no draft | < 30s |
partnership |
>= 0.6 | Secondary Telegram channel | batched hourly |
tire_kicker |
any | Canned polite reply + archive | immediate |
noise |
>= 0.85 | Auto-archive with agent/noise label |
immediate |
noise |
< 0.85 | Leave in inbox for manual review | — |
The agent/noise Gmail label is the audit hook. Every Sunday I pull 20 random noise-labeled emails and check for misclassifications. That's how I verified 89% precision is real and not a number I made up. It's a 10-minute weekly ritual and it's non-negotiable — if you don't audit, you don't actually know your precision, you just have vibes.
def route(email, score):
if score.bucket == "buyer_intent" and score.confidence >= 0.75:
draft = claude_draft_reply(email.full_body)
telegram_push(PRIMARY_CHAT, format_alert(email, score, draft))
elif score.bucket == "buyer_intent":
telegram_push(PRIMARY_CHAT, format_alert(email, score, draft=None))
elif score.bucket == "partnership" and score.confidence >= 0.6:
telegram_batch(SECONDARY_CHAT, email, score)
elif score.bucket == "tire_kicker":
gmail_reply(email.id, CANNED_TIRE_KICKER)
gmail_archive(email.id)
elif score.bucket == "noise" and score.confidence >= 0.85:
gmail_label(email.id, "agent/noise")
gmail_archive(email.id)
mark_routed(email.id)
Real numbers from four months in production
Averaged across three accounts:
- 312 emails/week ingested
- 47/week flagged
buyer_intent(~15%) - 89% precision on the buyer flag — of 47 flagged, ~42 were real
- ~5 false positives/week, mostly mail-merge from real buyers (patched above)
- ~11 hours/week saved on triage, measured by calendar-tracked email time before vs after
- $8/month in Claude API cost (small prompts, snippets not full bodies)
- $0/month in infrastructure — WSL Ubuntu on the PC that's already running
- ~30 seconds median latency from email arrival to Telegram alert
Recall on buyer intent is harder to measure because I'd have to manually score every noise email to know what I missed. Sunday audits on 20 random noise emails have surfaced 2 misclassified buyers in four months — call it a false negative rate around 1-2% at the audit sample size, but treat that number as directional.
Payback was immediate. Week one saved roughly 10 hours, and the highest-confidence buyer flagged in week one converted to a $4,200 project. The whole thing paid for itself before I finished writing the Sunday audit script.
Why bizflowai.io helps with this
Inbox intent routing is one of the automations we ship for solopreneur and small-team clients through bizflowai.io — the same 3-level pattern, adapted per client (Slack instead of Telegram, HubSpot instead of SQLite, Outlook instead of Gmail where needed). The architecture doesn't change because it's the right shape for the problem: separate ingestion from scoring from action, audit weekly, iterate on the rubric not the plumbing. Most clients see the 10+ hours/week saved show up in week one because the buyer flag is right often enough to trust on day one.
What to build first if you're copying this
Don't build all three levels at once. Ship level one alone for a week — just ingest into SQLite and eyeball the rows. You'll learn what your actual inbox distribution looks like, which is different from what you think it looks like. Mine turned out to be 60% noise, 25% tire-kicker, 10% partnership, 5% buyer. Yours will be different.
Then add level two with a rough rubric and audit for a week before turning on any auto-archive. Then add level three, and start with only Telegram pushes — no auto-replies, no auto-archive — until you trust the scoring. Turning on autonomous archiving before you trust precision is how you miss the $4k email.
The pattern is boring on purpose. Boring is what runs for four months without you touching it.
Want more like this?
I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.
Subscribe to bizflowai.io on YouTube — never miss a new tutorial.
Planning an AI automation project or need a second opinion on your architecture?
Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.
Visit bizflowai.io for our services, case studies, and AI consulting.
Frequently asked questions
What is the 3-level agent pattern for email triage?
It's a system borrowed from trading bots and applied to inbox management. Level one ingests unread emails via the Gmail MCP server, extracting sender, domain, subject, and the first 200 characters of the body into SQLite. Level two uses Claude to classify each email into buyer intent, tire-kicker, partnership, or noise with a confidence score. Level three autonomously routes them to Telegram, canned replies, or the archive.
How do I classify buyer intent emails accurately with Claude?
Don't ask Claude to predict if someone will buy. Give it a concrete rubric and have it look for specific signals in the email text: mentions of budget, timeline, a specific problem, team size, or current tools. When three or more signals appear in the first 200 characters, it's almost always a buyer. This turns it into a text classification problem, which Claude handles reliably.
Why does inbox triage matter for solopreneurs?
Manual email classification consumes significant productive time. Measured across three accounts over a month, inbox triage averaged 2 hours and 11 minutes per day, or roughly 11 hours per week, before any real client work started. Automating classification and routing recovers those hours and ensures buyer-intent emails get a fast response instead of being buried among newsletters, cold outreach, and tire-kicker requests.
When should I trust an autonomous email agent to auto-archive messages?
Only auto-archive categories with low downside risk, like noise and tire-kickers, and always keep an audit loop. In this setup, noise emails are archived with a label, and every Sunday 20 random archived emails are reviewed for misclassification. Buyer-intent emails are never auto-actioned; they're pushed to Telegram with a draft reply for human review before sending.
How do I fix false positives when real buyers look like spam?
The main failure mode is genuine buyers using mail-merge templates, which look like cold spam in the first 200 characters and get downgraded. Fix this with a second-pass enrichment: if the sender's domain has a real company website with a pricing page, upgrade the confidence score by 0.2. This recovers legitimate buyers without loosening the primary classification rubric.