We built an email triage system for our own SaaS product. It worked so well that we extracted it into a standalone product — while keeping the original running in production. Here’s how we did it, what we learned, and why the Django ORM was both the biggest obstacle and the best teacher.
The Problem: Email Is a Black Hole
Our product SignShield generates a lot of email. Support requests, error alerts from Sentry, prospect inquiries, Slack notifications, newsletter noise — all landing in the same inbox. We were spending 30+ minutes a day just sorting email before we could respond to any of it.
So we built a mail triage system directly into SignShield’s Django backend. Three classification layers:
- Pattern matching — glob patterns that auto-skip noise (noreply@*, *@linkedin.com) and route known senders (error alerts → error category)
- Contact matching — if we’ve seen this sender before, we know what category they are
- AI classification — Claude classifies everything else with a confidence score and reasoning
It worked great. Within a week, 50% of our email was being auto-handled. Within a month, we’d trained enough rules that 70% never needed human review.
The Idea: This Could Be a Product
Every small business has this problem. The classification logic wasn’t SignShield-specific — it was inbox-specific. The only things tying it to SignShield were Django ORM queries and a lead-gen integration layer we’d added for our own sales pipeline.
We decided to extract it into a standalone product: the AI Inbox Manager.
The Extraction: 4 Sprints, Zero Downtime
The biggest risk was breaking SignShield’s production email handling while extracting. We planned it in 4 sprints:
Sprint 1: IMAP/SMTP Services + Config System
We started with the infrastructure layer. SignShield’s IMAP and SMTP code was tightly coupled to Django models — MailAccount.objects.get() everywhere. We refactored both services to accept plain config dicts instead.
Then we built a 3-layer config system: sensible defaults → preset profiles (SaaS, e-commerce, agency) → customer overrides. This meant a new customer could be running in under 5 minutes by picking a profile and adding their IMAP credentials.
Sprint 2: Classification Pipeline + Data Models
This was the surgical sprint. We had to convert 10 Django models (465 lines of ORM code) to standalone Python dataclasses with SQLite storage — while keeping the schema compatible for a future Django migration back.
The classification pipeline itself was 298 lines. Three changes made it portable:
- Removed the lead-gen layer (SignShield-specific prospect matching)
- Made categories configurable from YAML instead of hardcoded in the model
- Replaced
apps.marketing.ai._get_client()with direct Anthropic SDK injection
One gotcha: references is a reserved word in SQLite. Our generic storage class was generating unquoted column names in INSERT statements. Took 10 minutes to find, 1 line to fix (quote all column names with double quotes).
Sprint 3: MCP Server + Autonomy + Learning
We built the MCP server with 12 tools — every operation supports multi-mailbox via an optional account parameter. The autonomy engine evaluates configurable confidence thresholds before taking action, with rate limiting (max 5 auto-sends/hour, 20/day) and full audit logging.
The learning loop is the feature we’re most proud of: correct a classification 3 times for emails from the same sender domain, and the system auto-generates a permanent rule. No configuration needed — it just watches your corrections and learns.
Sprint 4: Dogfood + Launch
We pointed the extracted agent at SignShield’s own production mailbox. Same credentials, same IMAP server, read-only mode. 10 emails classified, 0 errors, 5 noise auto-skipped. The pattern matching produced identical results to the Django version.
SignShield’s original code? Still running, untouched. Zero downtime, zero breakage.
What We Learned
Django models are secretly a great schema spec. We thought converting from Django ORM to dataclasses would be painful. It wasn’t — Django models are essentially documented schemas with defaults and constraints. We translated field-by-field, and the resulting dataclasses were cleaner and more portable.
Config-driven everything. Hardcoded values are the enemy of extraction. Every category, every threshold, every folder mapping — if it’s configurable, it’s portable. Our 3-layer config system (defaults → profiles → overrides) means customers can get started in minutes and customize over time.
Test before you extract, not after. We wrote the tests for the extracted code alongside the extraction, not as a follow-up. This meant we caught the SQLite reserved word issue during Sprint 2, not after shipping. 169 tests at the end — every layer covered.
Try It
The AI Inbox Manager is now in beta. We’re onboarding the first 3-5 customers with concierge setup — we’ll configure your mailboxes personally and make sure everything works.
Plans start at $79/mo for 2 mailboxes. Get in touch to join the beta.