An AI-powered email sorting agent that classifies, prioritizes, summarizes, and organizes your inbox into IMAP folders — hosted entirely on Google Cloud Run. Supports any email provider that speaks IMAP (Gmail, Outlook, Yahoo, iCloud, Fastmail, custom servers).
- Connects to your email accounts via IMAP (provider-agnostic)
- Automatically classifies every email into a category and moves it into the matching IMAP folder
- Scores each email by priority so the important stuff rises to the top
- Generates a one-line AI summary so you never have to open spam
- Files receipts and bills into
Receipts/YYYY/folders organized by year - Bulk-cleans your existing backlog (handles thousands of emails in batches)
- Auto-unsubscribes from mailing lists using
List-Unsubscribeemail headers - Presents everything in a clean, sortable web inbox you self-host
Every email is moved into its matching IMAP folder after classification. Folders are created automatically if they don't exist.
| Folder | What goes here |
|---|---|
EmailHelper/ActionRequired |
Emails that need a reply, decision, or task |
EmailHelper/JobOpportunities |
Recruiter outreach, interview requests, application updates |
EmailHelper/Deals |
Promo codes, limited-time sales, subscription offers |
EmailHelper/Bills |
Invoices, payment confirmations, bank alerts |
EmailHelper/Receipts/YYYY |
Purchase receipts filed by year (e.g.Receipts/2024) |
EmailHelper/Newsletters |
Subscribed digests and editorial content |
EmailHelper/Personal |
Real humans you know |
EmailHelper/Notifications |
App alerts, social pings, shipping updates |
EmailHelper/Spam |
Unsolicited bulk mail, phishing attempts |
EmailHelper/Trash |
Confirmed junk moved by the bulk cleaner |
Receipts live under
Billsfor the catch-all but get their own year-based subfolder tree underReceipts/for browsability.
Email Providers (Gmail, Outlook, Yahoo, iCloud, custom IMAP)
│
│ IMAP (universal protocol)
▼
Cloud Scheduler (every 2 min)
│ triggers
▼
Cloud Run Job: email-poller
├── Connect to each account's IMAP server
├── Fetch unseen emails since last check (cursor per account)
├── Publish raw emails → Cloud Pub/Sub
└── Update last_checked timestamp in Firestore
Cloud Pub/Sub Topic: raw-emails
│ triggers (one message per email)
▼
Cloud Run Job: email-processor
├── Parse email (headers, body, attachments metadata)
├── Call Claude API (Sonnet) → category + priority score + summary
├── Store full email body → Cloud Storage bucket (email-bodies/)
├── Store attachments → Cloud Storage bucket (email-attachments/)
├── Move email into correct IMAP folder
│ └── Receipts → Receipts/YYYY subfolder by email date
└── Write metadata + AI results → Firestore
Cloud Run Job: bulk-cleaner [on-demand]
├── Fetch ALL emails (not just unseen) — handles existing backlog
├── Dry-run mode: shows what would be moved/deleted first
├── Classifies in batches (100 at a time, rate-limit aware)
├── Logs results → Cloud Storage bucket (job-logs/)
└── Moves confirmed spam/junk → IMAP Trash
Cloud Run Job: unsubscriber [on-demand]
├── Reads List-Unsubscribe headers from classified newsletters
├── Fires HTTP unsubscribe requests or sends unsubscribe reply emails
├── Marks sender as unsubscribed in Firestore (never fires on bills/banks)
├── Logs all actions → Cloud Storage bucket (job-logs/)
└── Moves existing emails from unsubscribed senders → Trash
Cloud Run Service: email-api [always on]
├── User account management
├── IMAP credential storage (via Secret Manager)
├── REST API consumed by UI
├── Trigger endpoints for bulk-cleaner and unsubscriber
└── Future: webhook receiver for Gmail/Outlook push
Cloud Run Service: email-ui [always on]
└── Next.js inbox — sorted, filtered, summarized, folder view
Firestore (free tier)
├── users/
├── email_accounts/ (per-user IMAP configs)
├── emails/ (metadata + AI labels + GCS body reference)
├── polling_state/ (last_checked cursor per account)
└── unsubscribe_log/ (sender → status + timestamp)
Cloud Storage buckets (free tier — 5GB)
├── email-bodies/ (full email text, one file per email)
├── email-attachments/ (attachment files)
└── job-logs/ (bulk cleaner + unsubscriber run reports)
Secret Manager
├── IMAP credentials (encrypted)
├── OAuth tokens (future: Gmail API, Microsoft Graph)
└── ANTHROPIC_API_KEY
Folders are created inside an EmailHelper/ namespace to keep them separate from your existing inbox structure.
EmailHelper/
├── ActionRequired
├── JobOpportunities
├── Deals
├── Bills
├── Newsletters
├── Personal
├── Notifications
├── Spam
├── Trash
└── Receipts
├── 2020
├── 2021
├── 2022
├── 2023
├── 2024
├── 2025
└── 2026
Cloud Run Job triggered every 2 minutes by Cloud Scheduler. Connects to each registered IMAP account, fetches emails received since the last check (tracked by a cursor in the database), and publishes them to Pub/Sub. Exits after each run — no persistent connection required.
Cloud Run Job triggered by each Pub/Sub message. Parses the raw email, sends it to Claude API for classification, then moves the email into the correct IMAP folder. Receipts and bills get an extra step: the email date is extracted and used to route them into the correct Receipts/YYYY subfolder.
On-demand Cloud Run Job for processing your existing email backlog (tested with 6,000+ emails). Runs in two modes:
- Dry run — classifies and previews what would be moved, no changes made
- Execute — moves emails in batches of 100, respects Claude API rate limits, logs all actions
Moves confirmed spam and junk to IMAP Trash. Does not permanently delete — you review and empty trash yourself.
On-demand Cloud Run Job that reads List-Unsubscribe headers (RFC 2369) from classified newsletters and marketing emails. Fires unsubscribe requests via HTTP (for URL-type headers) or sends an unsubscribe reply email (for mailto: headers). Blocked from running on emails categorized as bills, banks, job opportunities, or action-required. After unsubscribing, moves all existing emails from that sender to Trash.
FastAPI service — the only always-running component. Handles IMAP credential management, serves the REST API to the UI, and exposes trigger endpoints to launch the bulk cleaner and unsubscriber jobs on demand.
Next.js web app. Displays your sorted inbox grouped by folder/category, shows AI summaries, priority scores, and lets you trigger bulk actions. Accessible from any browser — you host it, no third party sees your emails.
| Provider | IMAP Host | Notes |
|---|---|---|
| Gmail | imap.gmail.com:993 |
Requires App Password if 2FA enabled |
| Outlook / Hotmail | outlook.office365.com:993 |
Requires App Password |
| Yahoo | imap.mail.yahoo.com:993 |
Requires App Password |
| iCloud | imap.mail.me.com:993 |
Requires App-Specific Password |
| Fastmail | imap.fastmail.com:993 |
Standard IMAP |
| Custom server | configurable | Any IMAP-compliant server |
Replace polling with real-time push for supported providers:
- Gmail: Gmail API + Cloud Pub/Sub watch
- Outlook: Microsoft Graph API subscriptions
| Service | Free Tier | Purpose |
|---|---|---|
| Cloud Run | 2M requests/mo, 360K vCPU-sec | API, UI, poller, processor, bulk cleaner, unsubscriber |
| Cloud Pub/Sub | 10GB messages/month | Email queue between poller and processor |
| Firestore | 1GB, 50K reads/day, 20K writes/day | Email metadata, labels, user state, unsubscribe log |
| Cloud Storage | 5GB storage, 1GB egress/month | Email bodies, attachments, job logs |
| Cloud Scheduler | 3 free jobs | Poller cron every 2 minutes |
| Secret Manager | 6 secret versions free | Credentials + API keys |
| Artifact Registry | 500MB free | Docker images |
Infrastructure cost: $0/month within free tier limits. Only real cost: Claude Sonnet API — ~$10–15 one-time for the 6,000 email bulk run, ~$0.10/day ongoing.
EmailHelper/
├── services/
│ ├── api/ # FastAPI — auth, REST endpoints, job triggers
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ ├── routers/
│ │ │ ├── accounts.py # IMAP account CRUD
│ │ │ ├── emails.py # Fetch/filter classified emails
│ │ │ ├── jobs.py # Trigger bulk-cleaner / unsubscriber
│ │ │ └── auth.py # User authentication
│ │ └── providers/
│ │ ├── base.py # Abstract EmailProvider interface
│ │ └── imap.py # Generic IMAP + folder management
│ ├── processor/ # Cloud Run Job — AI classification + folder move
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ ├── classifier.py # Claude Sonnet API integration
│ │ ├── folder_router.py # IMAP folder move logic + Receipts/YYYY routing
│ │ └── body_store.py # Upload email body/attachments to GCS
│ ├── poller/ # Cloud Run Job — IMAP polling
│ │ ├── Dockerfile
│ │ └── main.py
│ ├── bulk_cleaner/ # Cloud Run Job — backlog processing + trash
│ │ ├── Dockerfile
│ │ └── main.py
│ ├── unsubscriber/ # Cloud Run Job — List-Unsubscribe firing
│ │ ├── Dockerfile
│ │ └── main.py
│ └── ui/ # Next.js inbox frontend
│ ├── Dockerfile
│ ├── app/
│ └── components/
├── shared/
│ ├── firestore.py # Firestore client + collection helpers
│ ├── gcs.py # Cloud Storage read/write helpers
│ └── pubsub.py # Pub/Sub helpers
├── infra/
│ └── terraform/ # All GCP infrastructure as code
├── docker-compose.yml # Local dev
└── DEVELOPMENT.md # Build progress and decisions log
git clone https://github.com/dayian326/emailhelper
cd EmailHelper
docker-compose up
# API: http://localhost:8000
# UI: http://localhost:3000Required environment variables (or Secret Manager in GCP):
ANTHROPIC_API_KEY=
DATABASE_URL=
PUBSUB_TOPIC=raw-emails
GOOGLE_CLOUD_PROJECT=
# Build and push all images
docker build -t gcr.io/$PROJECT/email-api ./services/api
docker build -t gcr.io/$PROJECT/email-processor ./services/processor
docker build -t gcr.io/$PROJECT/email-poller ./services/poller
docker build -t gcr.io/$PROJECT/bulk-cleaner ./services/bulk_cleaner
docker build -t gcr.io/$PROJECT/unsubscriber ./services/unsubscriber
# Deploy infrastructure + services
cd infra/terraform
terraform applyEmails are classified by Claude Sonnet (Anthropic API). The processor receives:
{
"category": "receipt",
"priority": 3,
"summary": "Amazon order #112-9284 confirmed, total $47.23, delivery Friday.",
"action_required": false,
"folder": "EmailHelper/Receipts/2025",
"sender_is_list": true,
"body_gcs_path": "email-bodies/user123/2025/abc-email-id.txt"
}Priority is scored 1–10. Anything 7+ surfaces at the top of the inbox regardless of category. sender_is_list: true marks emails eligible for unsubscription. body_gcs_path points to the full email text stored in Cloud Storage — kept out of Firestore to stay within free tier document size limits.