A self-hosted email waitlist system that runs entirely on Cloudflare's edge.
Zero external dependencies. No SaaS fees. Deploy in 5 minutes.
✅ Email Signup with validation and deduplication
✅ Double Opt-In (optional) with secure token-based confirmation
✅ Beautiful Email Templates with dark mode support
✅ Bot Protection via Cloudflare Turnstile
✅ Admin Dashboard (JSON/CSV export)
✅ Broadcast Campaigns to your entire list
✅ Template Previews for testing
✅ 100% Edge Native - runs on Cloudflare Workers + D1
Most waitlist tools lock you into expensive SaaS platforms. AutoMail gives you:
- Full control - Your data stays in your D1 database
- Zero cost - Cloudflare's free tier covers most projects (100k reads/day, 1k writes/day)
- Fast - Edge-first architecture, global deployment
- Simple - 5-minute setup, no complex integrations
npm install -g wrangler
wrangler loginnpm run db:create
# Note the database_id that's returnedCopy wrangler.example.toml to wrangler.toml and add your database ID:
[[d1_databases]]
binding = "D1"
database_name = "automail-db"
database_id = "YOUR_DATABASE_ID_HERE"npm run db:migrate# Required
npx wrangler secret put ADMIN_TOKEN
# Enter a secure random string
# Optional (for sending emails)
npx wrangler secret put RESEND_API_KEY
# Get your key from resend.comnpm run deployDone! Your waitlist API is live at:
POST /api/signup- Accept signupsGET /admin/waitlist- View signups (requires auth)POST /admin/broadcast- Send campaigns
ADMIN_TOKEN- Secure token for admin endpoints
RESEND_API_KEY- API key from ResendFROM_EMAIL- Sender email (default:no-reply@yourdomain.com)FROM_NAME- Sender name (default:Your App)BASE_URL- Your app's URL (for links in emails)
DOUBLE_OPT_IN- Set to"true"to require email confirmationSEND_WELCOME_EMAIL- Set to"false"to disable welcome emailsPUBLIC_TURNSTILE_SITE_KEY- Enable bot protection (client-side)TURNSTILE_SECRET_KEY- Turnstile secret (server-side)
Accept email signups.
curl -X POST https://your-site.com/api/signup \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "source": "homepage"}'Response:
{
"success": true,
"message": "Welcome to the waitlist!",
"position": 42
}View waitlist data (requires authentication).
curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
https://your-site.com/admin/waitlistResponse:
{
"stats": {
"total_signups": 150,
"confirmed_count": 120,
"unsubscribed_count": 5
},
"signups": [
{
"email": "user@example.com",
"source": "homepage",
"created_at": "2024-01-15T10:30:00Z"
}
]
}Export as CSV.
curl -X POST -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
https://your-site.com/admin/waitlist > waitlist.csvSend email campaigns to your list.
# Dry run (preview recipients)
curl -X POST https://your-site.com/admin/broadcast \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"template": "day1",
"dryRun": true,
"confirmedOnly": true
}'
# Send for real
curl -X POST https://your-site.com/admin/broadcast \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"template": "day1",
"confirmedOnly": true
}'Preview email templates by sending them to yourself.
curl -X POST https://your-site.com/admin/preview \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to": "you@example.com"}'See example/signup-component.html for a complete example with:
- Email validation
- Turnstile bot protection
- Success/error handling
- Accessible form design
Basic example:
<form id="signup-form">
<input type="email" name="email" required placeholder="Enter your email">
<button type="submit">Join Waitlist</button>
</form>
<script>
document.getElementById('signup-form').addEventListener('submit', async (e) => {
e.preventDefault();
const email = e.target.email.value;
const res = await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, source: 'landing' })
});
const data = await res.json();
if (data.success) {
alert(data.message);
}
});
</script>Edit functions/lib/email.js and functions/lib/campaigns.js to customize:
- Welcome email (sent on signup or after confirmation)
- Confirmation email (double opt-in)
- Campaign emails (e.g., Day 1 tips)
All templates support:
- Dark mode
- Mobile responsive
- Plain text fallback
- Unsubscribe links
┌─────────────┐
│ Browser │
└──────┬──────┘
│ POST /api/signup
↓
┌─────────────────┐
│ Cloudflare │
│ Workers (Edge) │
└────┬───────┬────┘
│ │
↓ ↓
┌─────┐ ┌──────┐
│ D1 │ │Resend│
│(DB) │ │(Mail)│
└─────┘ └──────┘
- Frontend: Any framework (vanilla JS, React, Vue, etc.)
- Backend: Cloudflare Workers (serverless functions)
- Database: Cloudflare D1 (SQLite at the edge)
- Email: Resend API (optional, or bring your own SMTP)
| Service | Free Tier | Typical Usage |
|---|---|---|
| Cloudflare D1 | 100k reads/day, 1k writes/day | $0 for most projects |
| Cloudflare Workers | 100k requests/day | $0 for most projects |
| Resend | 100 emails/day | $0 for early stages |
Total: $0/month for projects under 1k signups/day.
# Install dependencies
npm install
# Run locally
npm run dev
# Test signup
curl -X POST http://localhost:8788/api/signup \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com"}'
# Query database
npx wrangler d1 execute automail-db \
--command="SELECT * FROM waitlist"- Tokens: HMAC-signed with expiration (not reversible)
- Admin Auth: Bearer token in headers
- Rate Limiting: Configure via Cloudflare (WAF rules)
- Bot Protection: Optional Turnstile integration
- CORS: Configure in
wrangler.tomlas needed
- CLI tool:
npx create-automail - Simple workflow builder
- Dashboard UI (view/export/manage signups)
- Webhooks support
- Segments/tags
- Email A/B testing
- Analytics dashboard
Contributions welcome! Please open an issue first to discuss major changes.
MIT License - see LICENSE
AutoMail is something we created for automem.ai.
If you found this useful, consider:
- ⭐️ Starring this repo
- 🐦 Following us on Twitter
- 🤖 Checking out AutoMem
Questions? Open an issue 🧡