A privacy-focused email management platform that helps users take control of their inbox by automating subscription management, creating email digests, and protecting against spam.
- Multi-Provider Support - Connect Gmail, Outlook, Yahoo, AOL, Fastmail, and iCloud accounts
- Full Inbox Scanning - Syncs your entire inbox to find all subscriptions (not just recent emails)
- Smart Detection - Automatically identify newsletter and marketing subscriptions
- Browser-Automated Unsubscribe - Puppeteer-powered automation that actually clicks buttons and checks checkboxes on unsubscribe pages
- Fresh URL Fetching - Fetches current unsubscribe URLs from email provider to avoid expired tokens
- Cleanup Folders - After unsubscribing, move old emails to a cleanup folder for easy deletion
- Email Rollups - Consolidate newsletters into daily or weekly digests (up to 10 rollups)
- Custom Scheduling - Choose when and how often you receive digest emails
- Sender Management - Organize subscriptions by sender with custom preferences
- Inbox Shield - Advanced spam filtering and cold email blocking
- Do Not Disturb - Set quiet hours to focus without interruptions
- Privacy Aliases - Generate shielded email addresses for enhanced privacy
- Priority Senders - Whitelist important contacts
- Next.js 14 - React framework with App Router
- TypeScript - Type-safe development
- Tailwind CSS - Utility-first styling
- React Hook Form + Zod - Form validation
- Zustand - State management
- TanStack Query - Server state management
- Node.js + Express - API server
- TypeScript - Type-safe backend
- Prisma ORM - Database management
- PostgreSQL - Primary database
- Redis - Caching and job queue
- BullMQ - Background job processing
- Puppeteer - Browser automation for unsubscribe execution
- JWT - Authentication
- Winston - Logging
- Docker - Containerized development
- Turborepo - Monorepo management
- pnpm - Fast, disk-efficient package manager
unsubly/
├── apps/
│ ├── web/ # Next.js frontend application
│ │ ├── src/
│ │ │ ├── app/ # App router pages
│ │ │ ├── components/
│ │ │ ├── lib/ # Utilities and API client
│ │ │ └── hooks/
│ │ └── package.json
│ │
│ └── api/ # Express backend application
│ ├── src/
│ │ ├── server.ts
│ │ ├── routes/
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── workers/ # Background workers
│ │ ├── middleware/
│ │ └── prisma/ # Database schema
│ └── package.json
│
├── packages/
│ └── shared/ # Shared types and utilities
│ └── src/
│ ├── types/
│ └── constants/
│
└── docker/
└── docker-compose.yml
- Node.js >= 18.0.0
- pnpm >= 8.0.0
- Docker and Docker Compose
-
Clone the repository
git clone <repository-url> cd unsubly
-
Install dependencies
pnpm install
-
Start Docker containers
cd docker docker-compose up -d -
Set up the database
cd apps/api pnpm generate # Generate Prisma Client pnpm migrate # Run migrations
-
Configure environment variables
Copy
.env.exampleto.envinapps/api/and update values:cp apps/api/.env.example apps/api/.env
-
Start development servers
# From root directory pnpm run dev
- Frontend: http://localhost:3000
- Backend API: http://localhost:4000
- API Health Check: http://localhost:4000/health
- Prisma Studio: Run
pnpm studioinapps/api/→ http://localhost:5555
pnpm run dev- Start all development serverspnpm run build- Build all applicationspnpm run lint- Lint all codepnpm run clean- Clean build artifacts
pnpm run dev- Start Next.js dev serverpnpm run build- Build for productionpnpm run start- Start production server
pnpm run dev- Start API dev server with hot reloadpnpm run build- Build TypeScriptpnpm run migrate- Run database migrationspnpm run generate- Generate Prisma Clientpnpm run studio- Open Prisma Studiopnpm run workers:dev- Start background workers
View Database
cd apps/api
pnpm studioCreate Migration
cd apps/api
pnpm prisma migrate dev --name migration_nameReset Database (
cd apps/api
pnpm prisma migrate resetRegister
POST /api/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword",
"name": "User Name"
}Login
POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword"
}Get Current User
GET /api/auth/me
Authorization: Bearer <token>Connect Gmail Account
GET /api/email-accounts/gmail/auth
Authorization: Bearer <token>List Connected Accounts
GET /api/email-accounts
Authorization: Bearer <token>Fetch Emails from Account
GET /api/email-accounts/:accountId/emails?maxResults=10
Authorization: Bearer <token>Disconnect Account
DELETE /api/email-accounts/:accountId
Authorization: Bearer <token>Toggle Sync
PATCH /api/email-accounts/:accountId/sync
Authorization: Bearer <token>
Content-Type: application/json
{
"syncEnabled": true
}For detailed Gmail testing instructions, see TESTING_GMAIL.md.
Get Shield Settings
GET /api/shield/:emailAccountId
Authorization: Bearer <token>Update Shield Settings
PATCH /api/shield/:emailAccountId
Authorization: Bearer <token>
Content-Type: application/json
{
"spamFilteringEnabled": true,
"spamThreshold": 0.7,
"coldEmailBlockingEnabled": true,
"coldEmailAction": "quarantine",
"dndModeEnabled": false,
"dndStartTime": "22:00",
"dndEndTime": "08:00",
"dndDays": [0, 1, 2, 3, 4, 5, 6],
"prioritySendersEnabled": true,
"priorityOnlyMode": false
}Add Priority Sender
POST /api/shield/:emailAccountId/priority-senders
Authorization: Bearer <token>
Content-Type: application/json
{
"senderEmail": "important@example.com",
"senderDomain": "example.com"
}Remove Priority Sender
DELETE /api/shield/:emailAccountId/priority-senders/:senderEmail
Authorization: Bearer <token>List Priority Senders
GET /api/shield/:emailAccountId/priority-senders
Authorization: Bearer <token>Get Spam Statistics
GET /api/shield/:emailAccountId/stats/spam
Authorization: Bearer <token>Get Cold Email Statistics
GET /api/shield/:emailAccountId/stats/cold-email
Authorization: Bearer <token>Batch Analyze Emails
POST /api/shield/:emailAccountId/analyze
Authorization: Bearer <token>
Content-Type: application/json
{
"limit": 100
}Train Classifier
POST /api/shield/classifier/train
Authorization: Bearer <token>
Content-Type: application/json
{
"emailAccountId": "optional-account-id"
}Get Classifier Stats
GET /api/shield/classifier/stats
Authorization: Bearer <token>Submit Spam Feedback
POST /api/shield/feedback/:emailId
Authorization: Bearer <token>
Content-Type: application/json
{
"isSpam": true
}- Free: 10 unsubscribes, 1 email account, 1 rollup
- Pro: Unlimited unsubscribes, 5 email accounts, 10 rollups, Inbox Shield, Aliases
- Business: All Pro features + priority support
The application uses PostgreSQL with the following main tables:
users- User accounts and subscription tiersemail_accounts- Connected email accounts with OAuth tokensemails- Email metadata (partitioned by month for scalability)senders- Aggregated sender information and statisticsuser_sender_preferences- User decisions for each senderunsubscribe_attempts- Tracking unsubscribe execution and verificationrollups- Email digest configurationsrollup_digests- Individual digest instancesemail_aliases- Privacy-focused shielded email addressesinbox_shield_settings- Spam filtering configurationpriority_senders- Whitelisted important contactsjob_executions- Background job trackingaudit_logs- Security and activity audit trail
- OAuth2 for Gmail and Outlook
- IMAP/SMTP for Yahoo, AOL, Fastmail, iCloud
- Provider abstraction layer for unified interface
- Token refresh and management
- Folder Management - Create labels/folders and move emails programmatically
The app performs a comprehensive inbox scan to identify all your subscriptions:
Initial Sync (First Connection)
- Fetches email metadata in pages of 100 from the email provider
- For each page, fetches individual message details in small batches (5 at a time)
- Adds 50ms delays between batches to respect API rate limits
- If rate limited, automatically waits 5 seconds and retries with doubled delays
- Processes each email: creates sender records, detects unsubscribe links, queues for classification
- Continues until entire inbox is scanned (can handle 10,000+ emails)
Incremental Sync (Subsequent Syncs)
- Only fetches emails newer than the last sync
- Limited to 200 most recent emails per sync
- Runs automatically every 15 minutes via background workers
Rate Limit Protection
- Gmail: Batched requests with automatic retry and exponential backoff
- Outlook: Respects Microsoft Graph API limits
- IMAP: Connection pooling with provider-specific limits
Performance
- Initial sync of ~10,000 emails takes approximately 7-10 minutes
- Incremental syncs complete in seconds
- Progress is logged for monitoring
- BullMQ with Redis for job queues
- Workers for email scanning, unsubscribe execution, and rollup generation
- Retry logic with exponential backoff
- Job monitoring and error handling
- JWT-based authentication with HTTP-only cookies
- OAuth tokens encrypted at rest (AES-256)
- Rate limiting on all endpoints
- Input validation with Zod
- SQL injection prevention (Prisma parameterized queries)
- XSS prevention
- CSRF protection
- Secure password hashing (bcrypt with 12 salt rounds)
- Stateless API servers for horizontal scaling
- Database partitioning for large datasets
- Redis caching for frequently accessed data
- Email metadata in PostgreSQL, bodies in S3
- Read replicas for query distribution
- Monorepo setup with Turborepo
- Next.js frontend with Tailwind CSS
- Express backend with TypeScript
- PostgreSQL + Redis infrastructure
- User authentication (JWT)
- Database schema design
- Gmail OAuth2 integration
- Provider abstraction layer
- OAuth token management and refresh
- Email account management API
- Email fetching with pagination
- Outlook/Microsoft Graph API integration
- Generic IMAP provider (Yahoo, AOL, Fastmail, iCloud)
- Token encryption (AES-256-GCM)
- Email scanning background workers
- Rate limiting and retry logic
- Frontend account connection UI
- Subscription detection algorithm
- List-Unsubscribe header parsing
- Email body unsubscribe link extraction
- Browser-automated unsubscribe with Puppeteer
- Checkbox detection and automatic checking
- Button pattern detection (Unsubscribe, Confirm, Save Preferences, etc.)
- CAPTCHA and login detection with manual fallback
- Success/failure detection from page content
- Fresh URL fetching from email provider before unsubscribe
- Body link prioritization over header links (more reliable, don't expire)
- Verification and retry logic
- Sender tracking and statistics
- Subscriptions management UI with real-time stats updates
- Full inbox scanning with pagination (syncs entire mailbox)
- Gmail API rate limit handling with batched requests and auto-retry
- Cleanup folder feature (move unsubscribed sender emails to "Unsubly Cleanup")
- Bulk unsubscribe with batch cleanup support
- Search and filter subscriptions by name, email, or domain
- Keep workflow for subscription triage (mark to keep, then bulk unsubscribe the rest)
- Rollup configuration UI
- Digest generation service
- Responsive email templates
- Scheduling system
- Sender-to-rollup assignment
- Spam score calculation
- Machine learning classifier (Naive Bayes)
- Cold email detection
- Do Not Disturb mode
- Priority senders system
- Domain and MX record setup (configurable via ALIAS_DOMAIN env var)
- Alias generation service (random and custom prefix aliases)
- Email forwarding infrastructure (webhook endpoints for Postmark/SendGrid)
- Usage tracking and analytics (received, forwarded, blocked counts)
- Auto-disable features (blockAfterFirstSender, autoDisableDate)
See Privacy Aliases Setup Guide for domain configuration.
- Stripe checkout integration
- Subscription management
- Feature gating by tier
- Webhook handlers
- Billing portal
- Comprehensive testing (unit, integration, E2E)
- Security audit
- Performance optimization
- Error tracking (Sentry)
- Monitoring and alerting
- CI/CD pipeline
- Documentation
Contributions are welcome! Please feel free to submit a Pull Request.
Private - All Rights Reserved
For issues or questions, please open an issue on GitHub.