| apps | ||
| deploy | ||
| docs | ||
| packages/shared | ||
| prisma | ||
| scripts | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.npm.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| prisma.config.ts | ||
| README.md | ||
| tsconfig.json | ||
Warmbox
Self-hosted email warmup and deliverability engine for custom domains. Uses SMTP to send realistic B2B conversations and IMAP to rescue messages from spam folders, flag them as important, and reply — improving sender reputation over time.
Tech Stack
- Runtime: Node.js + TypeScript + Express
- Database: SQLite via Prisma ORM
- Email: nodemailer (SMTP), imapflow (IMAP)
- AI: OpenRouter (free models) for conversation generation
- Scheduler: node-cron
Quick Start
1. Install dependencies
npm install
2. Configure environment
cp .env.example .env
Generate an encryption key:
openssl rand -hex 32
Set these in .env:
| Variable | Description |
|---|---|
DATABASE_URL |
SQLite path, e.g. file:./prisma/dev.db (configured in prisma.config.ts) |
ENCRYPTION_KEY |
64-char hex string (32 bytes) for AES-256-GCM password encryption |
AI_API_KEY |
API key from OpenRouter (free tier works) |
AI_BASE_URL |
Default https://openrouter.ai/api/v1 |
AI_MODEL |
Default openrouter/free (auto-picks a free model) |
API_KEY |
Optional API key for REST auth (x-api-key header) |
PORT |
HTTP port (default 3000) |
Get a free AI API key (OpenRouter)
Email generation only needs a small, fast language model — not GPT-4 class reasoning.
- Sign up at openrouter.ai
- Go to openrouter.ai/keys and create an API key
- Paste it into
.envasAI_API_KEY
Recommended free models (set AI_MODEL in .env):
| Model | Notes |
|---|---|
openrouter/free |
Auto-selects an available free model (default) |
google/gemma-2-9b-it:free |
Good quality, reliable JSON |
meta-llama/llama-3.2-3b-instruct:free |
Fast, lightweight |
You do not need an OpenAI account.
3. Run database migrations
npm run db:migrate
4. Start the server
npm run dev
The API and cron jobs start together. Default schedules:
- Dispatcher: every 15 minutes — sends warmup emails
- Rescuer: every 10 minutes — rescues spam, replies to inbox
- Ramp: midnight daily — increases
currentRampper inbox
5. Add mailboxes
You need at least 2 active inboxes for warmup to work. Warmbox randomly pairs senders and receivers from your pool.
SMTP/IMAP settings by provider
Every inbox needs 4 connection fields plus credentials:
| Field | What it is |
|---|---|
smtpHost / smtpPort |
Where to send mail |
imapHost / imapPort |
Where to read mail (spam rescue + replies) |
username |
Usually the full email address |
password |
App password (not your login password) |
Gmail (including Google Workspace custom domains)
If hello@fillcareapp.com is hosted on Google Workspace, use Gmail servers:
| Field | Value |
|---|---|
smtpHost |
smtp.gmail.com |
smtpPort |
587 |
imapHost |
imap.gmail.com |
imapPort |
993 |
App password setup:
- Enable 2-Step Verification on the Google account
- Go to myaccount.google.com/apppasswords
- Create an app password for "Mail" → copy the 16-character code
- Use that code as
passwordin Warmbox
Yahoo Mail
| Field | Value |
|---|---|
smtpHost |
smtp.mail.yahoo.com |
smtpPort |
587 |
imapHost |
imap.mail.yahoo.com |
imapPort |
993 |
App password: Yahoo Account Security → Generate app password.
Outlook / Hotmail / Live.com (@live.com, @outlook.com, @hotmail.com)
| Field | Value |
|---|---|
smtpHost |
smtp-mail.outlook.com |
smtpPort |
587 |
imapHost |
outlook.office365.com |
imapPort |
993 |
App password: Microsoft Account Security → Advanced security → App passwords. IMAP must be enabled in Outlook settings.
Custom domain (cPanel / generic hosting)
If hello@fillcareapp.com is on cPanel or similar (not Google/Microsoft), check your host's email docs. Common pattern:
| Field | Value |
|---|---|
smtpHost |
mail.fillcareapp.com or smtp.fillcareapp.com |
smtpPort |
587 (or 465 for SSL) |
imapHost |
mail.fillcareapp.com or imap.fillcareapp.com |
imapPort |
993 |
Ask your DNS/hosting provider if unsure — look for "IMAP/SMTP settings" in cPanel → Email Accounts.
Example: FillCare + personal accounts
Add hello@fillcareapp.com as your primary warmup domain (industry: Childcare), then add 4–5 Gmail/Yahoo/Live accounts as the reply pool.
1. Primary domain (FillCare)
curl -X POST http://localhost:3000/api/inboxes \
-H "Content-Type: application/json" \
-d '{
"email": "hello@fillcareapp.com",
"smtpHost": "smtp.gmail.com",
"smtpPort": 587,
"imapHost": "imap.gmail.com",
"imapPort": 993,
"username": "hello@fillcareapp.com",
"password": "hkem mfmi bkmg bqyw",
"industry": "Childcare",
"dailyLimit": 40,
"currentRamp": 5
}'
If FillCare email is not on Google Workspace, swap
smtpHost/imapHostto your host's values (see table above).
2. Gmail account
curl -X POST http://localhost:3000/api/inboxes \
-H "Content-Type: application/json" \
-d '{
"email": "you@gmail.com",
"smtpHost": "smtp.gmail.com",
"smtpPort": 587,
"imapHost": "imap.gmail.com",
"imapPort": 993,
"username": "you@gmail.com",
"password": "your-gmail-app-password",
"industry": "SaaS"
}'
3. Yahoo account
curl -X POST http://localhost:3000/api/inboxes \
-H "Content-Type: application/json" \
-d '{
"email": "you@yahoo.com",
"smtpHost": "smtp.mail.yahoo.com",
"smtpPort": 587,
"imapHost": "imap.mail.yahoo.com",
"imapPort": 993,
"username": "you@yahoo.com",
"password": "your-yahoo-app-password",
"industry": "Marketing"
}'
4. Live.com / Outlook account
curl -X POST http://localhost:3000/api/inboxes \
-H "Content-Type: application/json" \
-d '{
"email": "you@live.com",
"smtpHost": "smtp-mail.outlook.com",
"smtpPort": 587,
"imapHost": "outlook.office365.com",
"imapPort": 993,
"username": "you@live.com",
"password": "your-microsoft-app-password",
"industry": "Finance"
}'
Repeat for each additional Gmail/Yahoo/Live account. Mixing providers is fine — Warmbox only needs them all in the same pool.
6. Test connections
After adding each inbox, verify SMTP + IMAP:
curl -X POST http://localhost:3000/api/inboxes/{id}/test
A successful response looks like:
{
"ok": true,
"smtp": { "ok": true },
"imap": { "ok": true }
}
If either fails, double-check the app password and that IMAP is enabled on the account.
7. Monitor logs
curl http://localhost:3000/api/logs
Watch for status progression: sent → rescued_from_spam → replied → complete.
API Reference
All endpoints under /api accept optional x-api-key header when API_KEY is set.
| Method | Path | Description |
|---|---|---|
POST |
/api/inboxes |
Create inbox |
GET |
/api/inboxes |
List inboxes |
GET |
/api/inboxes/:id |
Get inbox |
PATCH |
/api/inboxes/:id |
Update inbox |
DELETE |
/api/inboxes/:id |
Delete inbox |
POST |
/api/inboxes/:id/test |
Test SMTP + IMAP |
GET |
/api/logs |
List warmup logs (?status=&inboxId=&page=&limit=) |
GET |
/api/settings |
Get settings |
PATCH |
/api/settings |
Update settings |
GET |
/health |
Health check |
Project Structure
src/
├── config.ts # Environment validation
├── index.ts # Express + scheduler bootstrap
├── jobs/
│ ├── dispatcher.job.ts # Sends warmup emails
│ ├── rescuer.job.ts # Spam rescue + replies
│ └── scheduler.ts # Cron wiring
├── lib/ # Prisma, logger, inbox guard
├── middleware/ # Auth, error handling
├── models/ # Enums, types
├── routes/ # REST endpoints
└── services/
├── ai.service.ts # OpenRouter conversation generator
├── crypto.service.ts # AES-256-GCM encryption
├── email.service.ts # SMTP via nodemailer
├── imap.service.ts # IMAP via imapflow
└── settings.service.ts
prisma/
└── schema.prisma # Database schema
How It Works
- Dispatcher picks random sender/receiver pairs from active inboxes, generates a B2B email via AI, and sends via SMTP.
- Rescuer scans spam folders (
[Gmail]/Spam,Junk,Spam) for warmup emails, moves them to INBOX, flags them as read/starred/important. - Rescuer also processes unread warmup emails already in INBOX, generates contextual AI replies, and sends them back.
- Failed IMAP/SMTP connections mark the inbox as
errorand skip it until credentials are fixed.
Production
npm run build
npm start
Use a process manager (systemd, PM2) and back up the SQLite database file regularly.
License
MIT