Send 100,000+ personalized emails through Gmail SMTP or AWS SES with real-time tracking, smart throttling, and zero emails in spam.
Live Demo • Report Bug • Request Feature • Sponsor
If CadenceRelay saves you time or money, consider sponsoring the project.
Most email tools either cost a fortune at scale (Mailchimp charges $800/mo for 100K contacts) or require a PhD to self-host. CadenceRelay is different:
- $0/month on your own VPS (just pay for the server + SES costs at ~$0.10 per 1,000 emails)
- Takes 10 minutes to deploy with Docker Compose
- Doesn't land in spam — built-in deliverability best practices, proper headers, throttling
- Tracks everything — opens, clicks, bounces, complaints, with per-recipient event history
- Handles 280K+ contacts — tested with large-scale real-world datasets
Dashboard & Analytics
Real-time analytics with date range filters, campaign selection, send volume charts, engagement metrics, and CSV/PDF export.
Campaign Wizard
4-step campaign creation: Details → Template → Schedule → Review. Auto-save drafts, file attachments, template preview.
Template Editor
Monaco code editor with live split-pane preview. Handlebars variables, version history with nicknames, restore to any version.
Contact Management
Smart lists with dynamic filters (state, district, category, management). CSV import handles 280K+ rows. Per-contact engagement analytics.
| Feature | Description |
|---|---|
| Dual Provider | Switch between Gmail SMTP and AWS SES with one click |
| Smart Throttling | Configurable emails/sec and emails/hr to protect sender reputation |
| Background Processing | Click send and close your browser — BullMQ worker handles the rest |
| Pause / Resume | Pause a running campaign and resume later without losing progress |
| Scheduling | Schedule campaigns for a future date/time with countdown display |
| Attachments | Up to 10 files (25MB each) with preview, thumbnail, and download |
| Personalization | Handlebars variables: {{name}}, {{school_name}}, {{state}}, custom fields |
| Reply-To | Custom reply-to address so responses go where you want |
| From Name | Configurable display name (e.g., "Acme Corp - Marketing") |
| Feature | Description |
|---|---|
| Open Tracking | Invisible pixel tracks every open with timestamp, IP, user agent |
| Click Tracking | All links rewritten through tracking proxy with redirect |
| Open Count | See how many times each recipient opened (not just first open) |
| Click Count | Track every click per recipient with URL details |
| Bounce Detection | Gmail IMAP polling + AWS SNS webhooks for real-time bounce alerts |
| Complaint Tracking | AWS SES feedback loop integration |
| Event Timeline | Expandable per-recipient event history on campaign detail page |
| Contact Analytics | Per-contact engagement metrics across all campaigns |
| Dashboard Filters | Filter by date range, specific campaigns, providers |
| CSV Export | Export analytics data as formatted CSV |
| Unsubscribe | RFC 8058 one-click unsubscribe with List-Unsubscribe headers |
| Feature | Description |
|---|---|
| CSV Import | Stream-based import handles 280K+ rows (65MB+ files) without memory issues |
| Smart Lists | Dynamic lists based on filters (state, district, category, management, classes) |
| School Data | Built-in support for school fields: state, district, block, classes, category, management |
| Custom Variables | Define custom fields (principal_name, phone, etc.) that appear in contact forms and templates |
| Bulk Operations | Multi-select contacts/campaigns for bulk delete with admin password protection |
| Send History | See how many times each contact was emailed, with open/click stats |
| Column Sorting | Sort by name, email, state, district, status, send count |
| Advanced Filters | Filter by state, district, category, management, status, send count |
| Feature | Description |
|---|---|
| Monaco Editor | Full IDE-like HTML editor with syntax highlighting |
| Live Preview | Split-pane preview updates as you type |
| Version History | Browse, preview, restore any version. Add nicknames like "Final version" |
| Handlebars Variables | Auto-detected variables panel with click-to-copy |
| HTML Import | Import HTML files directly into the editor |
| Send Test | Send test emails directly from the editor |
| Feature | Description |
|---|---|
| Draft Auto-Save | Campaigns auto-save as drafts with all settings and attachments preserved |
| Edit Drafts | Return to the full creation wizard to edit any draft |
| Schedule with Countdown | See scheduled time with live countdown, reschedule or cancel |
| Email Preview | View the exact email (subject, body, attachments) on any campaign |
| Attachment Preview | Preview images/PDFs inline, download any file type |
| Feature | Description |
|---|---|
| Dockerized | Full Docker Compose setup with dev, test, and production profiles |
| CI/CD | GitHub Actions: type-check → build → deploy to VPS on every push |
| Zero-Downtime Deploy | Database migrations use IF NOT EXISTS — data is never lost |
| SSL/HTTPS | Let's Encrypt with auto-renewal via Certbot |
| Redis Caching | Settings, contact filters cached for sub-millisecond responses |
| Credential Encryption | AES-256-GCM encryption for all provider credentials at rest |
| Rate Limiting | API rate limiting + tracking endpoint burst protection |
Frontend: React 18 · TypeScript · Vite · Tailwind CSS · React Query · Recharts · Monaco Editor
Backend: Node.js 20 · Express · TypeScript · BullMQ · Nodemailer · AWS SDK v3
Database: PostgreSQL 16 · Redis 7
Infra: Docker Compose · Nginx · Certbot · GitHub Actions
- Docker & Docker Compose
- Git
git clone https://github.com/pulkitpareek18/CadenceRelay.git
cd CadenceRelay
cp .env.example .envEdit .env with your settings:
POSTGRES_DB=cadencerelay
POSTGRES_USER=cadencerelay
POSTGRES_PASSWORD=your-secure-password
JWT_SECRET=your-32-char-secret-here
JWT_REFRESH_SECRET=another-32-char-secret
ADMIN_USERNAME=admin
ADMIN_PASSWORD=your-admin-password
TRACKING_DOMAIN=https://your-domain.comdocker compose up -dOpen http://localhost:5173 and login with your admin credentials.
docker compose -f docker-compose.prod.yml up -dSee Production Deployment for full VPS setup with SSL.
Full VPS deployment guide (click to expand)
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2+ vCPUs |
| RAM | 2 GB | 4+ GB |
| Storage | 20 GB | 50+ GB (for attachments) |
| OS | Ubuntu 22.04+ | Ubuntu 24.04 |
curl -fsSL https://get.docker.com | shcd /opt
git clone https://github.com/pulkitpareek18/CadenceRelay.git cadencerelay
cd cadencerelay
cp .env.example .env
nano .env # Edit with production values# Start nginx for ACME challenge
docker compose -f docker-compose.prod.yml up -d nginx
# Get certificate
docker compose -f docker-compose.prod.yml run --rm certbot certonly \
--webroot --webroot-path=/var/www/certbot \
-d your-domain.comdocker compose -f docker-compose.prod.yml up -dPoint your domain to the server IP:
A your-domain.com → YOUR_SERVER_IP
For email deliverability, also add:
TXT your-domain.com → v=spf1 include:amazonses.com ~all
TXT _dmarc.your-domain → v=DMARC1; p=quarantine; rua=mailto:dmarc@your-domain.com
DKIM is auto-configured by AWS SES.
Every push to main triggers:
Push → Type Check → Build Client & Server → SSH Deploy → Rebuild Containers → Migrate DB → Verify Health
Database is never reset — migrations use IF NOT EXISTS and ADD COLUMN IF NOT EXISTS.
┌─────────────────────────────────────┐
│ Nginx │
│ (SSL termination + reverse proxy) │
└─────┬──────────────────┬────────────┘
│ │
┌─────▼─────┐ ┌───────▼───────┐
│ React │ │ Express │
│ Client │ │ API Server │
│ (Vite) │ │ │
└───────────┘ └───┬───────┬───┘
│ │
┌─────▼───┐ ┌─▼──────────┐
│PostgreSQL│ │ Redis │
│ 16 │ │ 7 │
└─────────┘ └──────┬──────┘
│
┌──────▼──────┐
│ BullMQ │
│ Worker │
│ (send,track,│
│ schedule) │
└─────────────┘
CadenceRelay is built to land in the Primary inbox, not Promotions or Spam:
- SPF/DKIM/DMARC — full support with setup guide
- RFC 8058 one-click unsubscribe headers
- Smart throttling — don't trigger rate limits
- Bounce handling — auto-marks bounced contacts to protect reputation
- Complaint tracking — auto-unsubscribes complainers
- Personalization — per-recipient variable substitution makes emails feel personal
Authentication
POST /api/v1/auth/login — Login (returns JWT + refresh token)
POST /api/v1/auth/refresh — Refresh access token
Campaigns
GET /api/v1/campaigns — List campaigns (paginated, filterable)
POST /api/v1/campaigns — Create campaign (with attachments)
GET /api/v1/campaigns/:id — Get campaign details
PUT /api/v1/campaigns/:id — Update campaign
DELETE /api/v1/campaigns/:id — Delete campaign (admin password required)
POST /api/v1/campaigns/:id/send — Send campaign
POST /api/v1/campaigns/:id/schedule — Schedule campaign
POST /api/v1/campaigns/:id/pause — Pause sending
POST /api/v1/campaigns/:id/resume — Resume sending
GET /api/v1/campaigns/:id/recipients — List recipients (paginated, filterable)
POST /api/v1/campaigns/:id/attachments — Add attachments
DELETE /api/v1/campaigns/:id/attachments/:i — Remove attachment
GET /api/v1/campaigns/:id/attachments/:i — Download attachment
Contacts
GET /api/v1/contacts — List contacts (paginated, filterable, sortable)
POST /api/v1/contacts — Create contact
GET /api/v1/contacts/:id — Get contact with send history
PUT /api/v1/contacts/:id — Update contact
DELETE /api/v1/contacts/:id — Delete contact (admin password required)
POST /api/v1/contacts/import-csv — Import CSV (stream-based, handles 280K+ rows)
GET /api/v1/contacts/filters — Get filter options (states, districts, etc.)
Templates
GET /api/v1/templates — List templates
POST /api/v1/templates — Create template
GET /api/v1/templates/:id — Get template
PUT /api/v1/templates/:id — Update template (creates new version)
DELETE /api/v1/templates/:id — Delete template
GET /api/v1/templates/:id/versions — List versions
GET /api/v1/templates/:id/versions/:v — Get specific version
POST /api/v1/templates/:id/versions/:v/restore — Restore version
PUT /api/v1/templates/:id/versions/:v/label — Set version nickname
Analytics
GET /api/v1/analytics/dashboard — Dashboard stats
GET /api/v1/analytics/campaigns/:id — Campaign analytics
GET /api/v1/analytics/recipients/:id/events — Recipient event history
GET /api/v1/analytics/contacts/:id — Contact engagement analytics
GET /api/v1/analytics/export — Export analytics as CSV
Lists, Settings, Tracking
GET/POST/PUT/DELETE /api/v1/lists/* — Contact list management
GET/PUT /api/v1/settings/* — Provider config, throttling, reply-to
POST /api/v1/settings/test-email — Send test email
GET /api/v1/t/o/:token — Open tracking pixel
GET /api/v1/t/c/:token/:idx — Click tracking redirect
POST /api/v1/t/u/:token — Unsubscribe
Use Handlebars syntax {{variable_name}} in your templates:
| Variable | Example Value |
|---|---|
{{name}} |
St. Xavier's High School |
{{email}} |
school@example.com |
{{state}} |
Goa |
{{district}} |
North Goa |
{{block}} |
Tiswadi |
{{classes}} |
1-12 |
{{category}} |
Secondary with Higher Secondary |
{{management}} |
Private Unaided (Recognized) |
{{address}} |
Plot No. 6, Tiswadi, North Goa |
Define custom variables in Settings → Custom Variables. They appear in contact forms, CSV import, and template editor.
| Scale | Setup | Expected Throughput |
|---|---|---|
| Small (< 10K emails/day) | Single VPS, Gmail SMTP | ~500 emails/hour |
| Medium (10K-100K/day) | Single VPS, AWS SES | ~5,000 emails/hour |
| Large (100K+/day) | Multiple workers, SES production | ~50,000 emails/hour |
To scale the worker:
docker compose -f docker-compose.prod.yml up -d --scale worker=3Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes
- Push to the branch
- Open a Pull Request
By contributing, you agree to the Contributor License Agreement — your contributions are licensed under the same terms.
If CadenceRelay helps you save time or money:
- Star this repo — it helps others discover the project
- Sponsor on GitHub — fund ongoing development
- Share it — tweet, blog, or tell a friend
CadenceRelay is source-available under a custom license. See LICENSE for details.
TL;DR:
- Personal/non-commercial use: Free
- Commercial/SaaS use: Requires license — contact pulkitpareek18@gmail.com
- All rights reserved by Pulkit Pareek
Star this repo if you find it useful.
Made with determination in India.