Inbox-to-CRM Automation
Turns messy inbound email into clean, routed CRM records — contact, intent, urgency, summary, and a drafted reply — so nothing slips and follow-up is instant.
The problem
Small teams hand-copy details from inbound email into their CRM — losing leads to slow follow-up, fumbling angry support emails in a shared inbox, and mis-routing billing questions. The inbox is the top of the funnel, and it leaks.
The approach
A pipeline reads each inbound email and an LLM extracts a structured record — contact, company, intent (lead/support/billing/spam/other), urgency, a one-line summary, the queue it belongs in, and a suggested reply — then routes it to the right place. The extraction runs server-side on claude-haiku-4-5 with strict JSON output, parsed defensively; spam and automated notices are filtered out before they ever reach a human.
The outcome
A live, interactive demo: a seeded messy inbox on the left, the structured before/after on the right, and a mock CRM that fills up colour-coded by destination. It's rate-limited and ships a pre-computed extraction for every email, so it stays fully functional with zero API calls — the demo never looks broken.
The interactive version lives at /projects/inbox-to-crm — click into a seeded email, hit Process, and watch a clean CRM record fall out the other side and route itself to the right queue.
How it works
A single server route (/api/extract) does the work. It takes one raw email,
sends it to claude-haiku-4-5 behind a JSON-only system prompt, and parses the
result defensively — stripping any stray fences, slicing to the outermost braces,
and validating against a strict schema before trusting a single field. What comes
back is a typed record: contact, company, intent, urgency, a one-line summary, a
suggested owner/queue, and a two-sentence reply.
The decision that keeps it honest
A live demo that depends on an API key is a demo that breaks the day the key rotates or the rate limit trips. So the pipeline is designed to degrade invisibly: every seeded email ships a pre-computed extraction of the exact same shape the model produces. The route is rate-limited (20 requests per IP per hour), and on a missing key, a hit limit, or any model/parse failure it falls back to the pre-computed record instead of erroring. The key is read server-side only and never reaches the browser. A small badge ("live · Claude" vs "sample data") is the only tell — the experience is identical either way.