AI-powered email triage that classifies your Gmail inbox into smart buckets — so you see what matters first.
Most email clients give you a search bar and a prayer. Inbox Concierge takes a different approach: a five-stage streaming pipeline that syncs your Gmail, embeds every thread into 384-dimensional vector space, classifies with a cascade of heuristics, semantic similarity, and LLM refinement, then triages with urgency scoring and action detection — all streamed to your browser in real time via Server-Sent Events.
The result is an inbox where the signal is already separated from the noise before you even look at it.
Every email runs through five pipeline stages, each progressively enriching and classifying:
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ 1 SYNC │───▸│ 2 EMBED + │───▸│ 3 DOMAIN │───▸│ 4 SEMANTIC + │───▸│ 5 ANALYZE │
│ │ │ ENRICH │ │ FASTPATH │ │ LLM CLASSIFY │ │ + TRIAGE │
│ Gmail │ │ 384-dim │ │ Known sender │ │ Exemplar cosine │ │ Urgency, │
│ History │ │ vectors + │ │ rules: ~40% │ │ similarity → │ │ deadlines, │
│ API │ │ security │ │ resolved at │ │ Claude Sonnet │ │ action items,│
│ delta │ │ scan │ │ zero LLM │ │ for the rest │ │ reply status │
│ sync │ │ │ │ cost │ │ │ │ │
└──────────┘ └──────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘
Each stage streams progress events to the frontend. Buckets fill in as classification completes — you don't wait for the full run to see results.
This is the core design insight: three tiers of classification at increasing cost, each catching what the previous tier couldn't.
Tier 1 — Domain Fast-Path (free, instant) Pattern matching on sender domain. GitHub notifications → Notifications. Substack → Newsletters. Amazon → Promotions. About 40% of email resolves here at zero LLM cost.
Tier 2 — Semantic Similarity (cheap, fast) Each thread's embedding is compared against weighted exemplar embeddings per bucket (top-K=5, weighted mean). If the margin between the best and second-best bucket exceeds 0.15, it's classified with high confidence. The system bootstraps immediately — new buckets get synthetic exemplar embeddings generated from their description, so there's no cold-start problem.
Tier 3 — LLM Refinement (precise, last resort) Low-confidence threads go to Claude Sonnet 4 with bucket descriptions and few-shot examples. If Claude fails, Gemini 2.0 Flash takes over. The LLM sees the full context — subject, sender, snippet, attachments, security flags — and returns structured classifications via tool use.
The exemplar pool is self-improving: every high-confidence classification (>0.7) becomes a new weighted exemplar, so the semantic tier catches more over time and the LLM tier handles less.
Describe a category in plain English — "receipts and order confirmations from online purchases" — and the system generates a rich description, boundary notes, synthetic exemplar embeddings, and overlap warnings against existing buckets. New buckets start classifying accurately from the moment they're created.
Every thread is scanned in parallel for:
- Phishing patterns — "verify your account", "suspended", "unusual activity"
- Financial fraud — "wire transfer urgently", "prize claim"
- Suspicious URLs — shortened links (bit.ly, t.co)
- PII exposure — SSN and credit card number patterns
- Dangerous attachments — executables (.exe, .bat, .ps1), archives (.zip, .rar)
Flagged threads surface with risk scores and security badges so nothing dangerous hides in the noise.
A weighted formula ranks urgency, deadlines, action items, and reply status to surface what matters most. Each item links directly to the original thread in Gmail. For threads needing a response, the system generates draft replies in multiple tones — professional, concise, friendly — personalized to your writing style (extracted from your sent mail).
Volume trends, top senders, reply rates, urgency distribution, action item counts, and deadline tracking — all computed from parallel SQL aggregations over the enriched data. Available in 7-day, 30-day, and 90-day views.
An AI-generated narrative summary of today's high-priority emails. What needs your attention, what can wait, what's just noise — in one paragraph.
Recursive extraction from nested MIME parts. Attachment filenames factor into embeddings and classification. File type icons and tooltips in the thread list. Dangerous extensions flagged by the security scanner.
AI-generated draft replies with tone selection, inline editing, two-click send confirmation, and an audit trail. Gracefully handles OAuth scope upgrades when gmail.send permission is needed.
The pipeline streams SSE events as each stage progresses. Buckets fill in as threads are classified. The analyze stage shows a progress bar as it works through triage. No spinners, no waiting for the full run — you see results as they happen.
Try the full inbox experience without connecting a Google account. Eighteen realistic fixture threads across five buckets, with a working dashboard, action queue, and daily briefing. One click to enter, no signup required.
| Layer | Technology | Role |
|---|---|---|
| Framework | Next.js 16, React 19 | App Router, RSC, SSE streaming |
| Database | Neon Postgres, pgvector | Serverless DB + 384-dim vector similarity |
| ORM | Drizzle | Type-safe queries, migrations |
| Primary LLM | Claude Sonnet 4 | Classification, triage, draft generation |
| Fallback LLM | Gemini 2.0 Flash | Fallback classification + triage |
| Embeddings | Gemini embedding-001 | 384-dim thread vectors (batched 100/req) |
| Gmail API | OAuth, incremental sync, send | |
| Auth | JOSE (JWT) | httpOnly secure session cookies |
| UI | Tailwind CSS, Framer Motion | Styling + animation |
| Charts | Recharts | Dashboard visualizations |
| Icons | Lucide React | Consistent icon set |
| Testing | Playwright | E2E tests |
| Deployment | Vercel | Edge-optimized hosting |
- Node.js 18+
- pnpm
- A Neon Postgres database with pgvector enabled
- Google Cloud project with Gmail API + OAuth credentials
- Anthropic API key (Claude)
- Google AI API key (Gemini embeddings + fallback)
pnpm install
cp .env.local.example .env.localFill in .env.local:
# Google OAuth
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
# Database
DATABASE_URL=postgresql://user:pass@your-project.neon.tech/neondb?sslmode=require
# Session
SESSION_SECRET=any-random-uuid
# LLMs
ANTHROPIC_API_KEY=sk-ant-...
GEMINI_API_KEY=AI...
# App URL
NEXT_PUBLIC_URL=http://localhost:3100Run migrations and start:
pnpm db:migrate
pnpm dev # → http://localhost:3100pnpm tsc --noEmitnpx playwright test --reporter=listThe data model centers around classifications — one row per user per Gmail thread, progressively enriched through the pipeline stages.
users ──────────┐
│ │
├─▸ buckets │
│ │ │
│ ├─▸ tags │
│ │ │
│ ├─▸ categoryExemplars (384-dim embeddings, weighted)
│ │
│ └─▸ classifications (thread metadata, embedding, enrichment, triage)
│ │
│ ├─▸ draftResponses ──▸ draftSendLog
│ │
│ └─▸ reclassificationLog
│
├─▸ aiUsage (cost tracking per LLM call)
│
└─▸ userStyleProfiles (writing style extraction)
Key design decisions:
- Embeddings stored in Postgres via pgvector — no external vector DB needed
- Exemplars are weighted (0.0–1.0) with source tracking (description, confirmed, synthetic)
- Idempotent upserts — re-running the pipeline only processes new/unclassified threads
- Cost tracking per LLM call with per-model pricing
| Bucket | Color | What goes here |
|---|---|---|
| Important | Blue | Person-to-person email, high-urgency threads |
| Can Wait | Amber | Non-urgent but relevant — FYIs, team updates |
| Newsletters | Teal | Substack, Beehiiv, mailing lists |
| Promotions | Green | Marketing, deals, product updates |
| Auto-Archive | Gray | Receipts, shipping confirmations, 2FA codes, password resets |
Users can create custom buckets at any time. The LLM generates synthetic exemplars from the description, so new buckets classify accurately immediately.
Why three classification tiers? LLM calls are slow and expensive. Domain heuristics are instant and free. Semantic similarity is fast and cheap. By cascading through these tiers, roughly 40% of email never touches an LLM, another 30-40% resolves via semantic matching, and only the genuinely ambiguous 20-30% goes to Claude. This keeps per-user costs low while maintaining high accuracy.
Why exemplar-based semantic matching instead of fine-tuning? Exemplars adapt in real time. Every user correction improves the embedding space immediately — no retraining, no batch jobs, no model versioning. Synthetic exemplars solve the cold-start problem. Weight decay (0.5x after 90 days) keeps the space fresh.
Why SSE streaming instead of polling? The pipeline takes 10-30 seconds for a full run. Polling would either be too slow (missing progress updates) or too aggressive (wasted requests). SSE gives real-time progress with zero overhead — each stage reports its status as it completes, and partial results render immediately.
Why Neon Postgres with pgvector instead of a dedicated vector DB? One database for everything: relational data, vector similarity, and aggregation queries. No sync layer, no consistency issues, no extra infrastructure. Neon's serverless scaling handles the load, and pgvector's IVFFlat indexes keep similarity search fast enough for this use case.
Why Claude + Gemini instead of just one? Claude Sonnet 4 produces the best classification results, but availability isn't 100%. Gemini 2.0 Flash is faster and cheaper with slightly lower accuracy. The dual-provider setup means the pipeline never hard-fails — it gracefully degrades from Claude to Gemini to heuristic passthrough.
| Route | Method | Description |
|---|---|---|
/api/auth/google |
GET | Initiates Google OAuth flow |
/api/auth/callback |
GET | OAuth callback, creates session |
/api/auth/demo |
POST | Creates demo session, redirects to inbox |
/api/auth/signout |
POST | Clears session, redirects to landing |
/api/classify |
POST | Runs pipeline, streams SSE events |
/api/inbox |
GET | Fetches classified threads + buckets |
/api/dashboard |
GET | Analytics aggregations (7d/30d/90d) |
/api/action-queue |
GET | High-urgency threads needing action |
/api/action-queue/{id}/generate |
POST | Generate draft replies |
/api/action-queue/{id}/send |
POST | Send reply via Gmail |
/api/action-queue/{id}/dismiss |
POST | Dismiss from queue |
/api/buckets |
GET/POST | List or create buckets |
/api/buckets/{id} |
PATCH/DELETE | Update or delete bucket |
/api/briefing |
GET | AI-generated daily summary |
/api/style-profile |
GET/POST | Writing style extraction |