When I audited my own SaaS subscriptions by hand, I found 7 tools I remembered paying for. A scan of my mailbox found 23. The gap between those two numbers is the entire reason SaaSClerk exists, and this post is about how the scanning engine works: the architecture, the trade-offs, and the parts I got wrong first.
The core problem
An email mailbox contains every receipt, renewal notice, and price change a SaaS vendor ever sent. The challenge is reading thousands of emails efficiently while making strict privacy guarantees — because "paste your Gmail password into my web app" is a non-starter, and so is "let my server read all your mail."
My first attempt used a traditional email client library with direct IMAP access. Three drawbacks killed it: performance fell apart on large mailboxes, the authentication flows were painful to maintain across providers, and the privacy boundary was blurry — the server could technically see everything.
The architecture
The stack I settled on:
- Next.js (TypeScript) for the app and API routes
- Prisma as the ORM
- Neon Postgres as the database
- OAuth read-only scopes for Gmail/Outlook — the app can read messages matching billing patterns and nothing else
- AES-256-GCM encryption for every stored token
The flow: user connects a mailbox via OAuth → the scanner queries only messages that match billing patterns (subjects containing "invoice", "receipt", "subscription", "renewal", plus known billing sender domains) → a lightweight natural-language pass extracts the service name, amount, currency, billing interval, and next renewal date → the result is a structured list the user reviews.
Nothing outside those matched messages is ever touched. Email content isn't persisted — extraction happens in memory and only the structured subscription record survives.
The part I underestimated: renewal emails are where the money hides
The first version only extracted amounts. It worked — the scanner immediately surfaced duplicates and forgotten plans. But the most valuable signal turned out to be change inside renewal emails.
Vendors rarely announce price increases on their pricing pages. The increase lives in the renewal notice: "Your plan will renew at $6 per month starting with the next billing cycle." Mine did exactly that — GitHub Pro, $4 to $6, and I caught it two months late on a bank statement.
So the scanner now parses renewal emails for change signals — phrases like "will increase", "new price", "effective" adjacent to a numeric value. When it detects an increase, the subscription gets a price-change badge with the old and new amounts side by side. If the detected amount differs from the stored one by more than 5%, it goes into the review queue automatically.
This one feature generates more "how did it know that" reactions than the core inventory does.
The reminder engine
Finding waste is half the job; acting before the renewal date is the other half. My own failure mode: I marked a subscription "Cancel" in a spreadsheet, set a Google Calendar reminder, snoozed it, and paid for another year.
SaaSClerk's reminder is opt-out instead of opt-in: mark an item "Cancel" and the engine schedules an email 14 days before the next billing date, with the service, the price, and — where known — a direct link to the cancellation page. If you change your mind, clicking "Keep" in that email stops the reminders. The closed loop matters: a reminder you have to configure yourself is a reminder you'll eventually ignore.
Trade-offs I'd make again (and one I wouldn't)
Server-side scanning over a client-side extension. A browser extension would have solved the privacy perception problem differently, but it can't run when the browser is closed, and renewal monitoring needs to run on a schedule. Server-side with read-only scopes was the right call.
Neon over a self-managed Postgres. Branching databases made development measurably faster, and the cold-start behavior is fine for this workload.
No IMAP password flows. OAuth read-only only. It cost me compatibility with some providers, but it's the difference between "a tool that reads your mail" and "a tool that can only ever read receipts."
The one I'm still paying for: parsing non-English billing emails. My pattern set started English-only, and early users in other markets immediately hit the gap. Expanding the phrase matching (Spanish, French, German next) without weakening the no-storage guarantee is the current work in progress.
Numbers, since build-in-public posts should have them
The scanner surfaced $4,200 in annual spend across my forgotten, duplicated, and quietly-increased subscriptions. The free tier at saasclerk.app scans your last twelve months if you want to run the same audit on your own mailbox — and if you build anything similar, the one lesson I'd pass on: extract changes, not just amounts. The amounts are what people know. The changes are what they're losing.
Top comments (0)