mails
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Fail
- execSync — Synchronous shell command execution in src/cli/commands/claim.ts
- process.env — Environment variable access in src/cli/commands/claim.ts
- network request — Outbound network request in src/cli/commands/claim.ts
- execSync — Synchronous shell command execution in src/cli/commands/deploy.ts
- process.env — Environment variable access in src/cli/commands/doctor.ts
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
mails-agent: Email infrastructure for AI agents. Send, receive, search, extract verification codes. CLI + SDK + Cloudflare Worker. Based on chekusu/mails.
mails-agent
Email infrastructure for AI agents. Send, receive, search, and extract verification codes.
Agent Integration: Use mails-skills to give your Claude Code, OpenClaw, or any AI agent email capabilities with one command.
Why mails?
Unlike raw email APIs that only send, mails gives your agent a complete email identity — send, receive, search, and extract verification codes in one package. Deploy on your own domain with Cloudflare (free tier). Full control, no third-party dependency.
Features
- Send emails — via Resend with CC/BCC, In-Reply-To threading, and attachment support
- Receive emails — via Cloudflare Email Routing → Worker → D1, with raw-first R2 persistence (zero email loss)
- Search inbox — FTS5 full-text search across subject, body, sender, code
- Semantic search — AI-powered vector search via Workers AI + Cloudflare Vectorize (keyword, semantic, hybrid modes)
- Dashboard console — visual email management UI at
mails0.com/console - Verification code extraction — auto-extracts 4-8 char codes (EN/ZH/JA/KO)
- Email threading — auto-assign
thread_idvia In-Reply-To / References headers - Auto labels — rule-based classification: newsletter, notification, code, personal
- Structured data extraction — extract orders, shipping, calendar, receipts from emails (rule-based, no LLM)
- Attachments — send via CLI (
--attach) or SDK; receive with R2 storage for large files - Webhook notifications — POST to your URL on email receive, with HMAC-SHA256 signature verification
- SSE real-time events — subscribe to
message.receivedevents via/api/events - Mailbox isolation — per-token mailbox binding via
auth_tokensD1 table with scoped keys - Mailbox pause/resume — temporarily stop processing for a mailbox
- Suppression list — auto-suppress bounced/complained recipients, protects domain reputation
- Rate limits — per-mailbox daily send limits (configurable via
DAILY_SEND_LIMIT) - Custom domains — manage and verify custom sending domains via API
- Inbound idempotency — deduplication via
message_id, safe against replay/redelivery - Smart email routing — per-label webhook URLs, route different email types (code/newsletter/notification/personal) to different endpoints
- Mailbox CRUD — update webhook URL via
PATCH /api/mailbox, cascade delete viaDELETE /api/mailbox - One-click deploy —
mails deployautomates the entire self-hosting setup (D1, secrets, Worker) via wrangler - Delete API — remove processed emails with cascade cleanup (attachments + R2)
- Storage providers — local SQLite (dev) or remote Worker API (production)
- Zero runtime dependencies — all providers use raw
fetch() - Self-hosted — deploy your own Worker on Cloudflare (free tier), full control over your data
Install
npm install -g mails-agent
# or
bun install -g mails-agent
# or use directly
npx mails-agent
Quick Start
# 1. Deploy your Worker (see Self-Hosted Deployment guide below)
cd worker && wrangler deploy
# 2. Configure the CLI
mails config set worker_url https://your-worker.example.com
mails config set worker_token YOUR_TOKEN
mails config set mailbox [email protected]
mails config set default_from [email protected]
# 3. Use it
mails send --to [email protected] --subject "Hello" --body "World"
mails inbox # List received emails
mails inbox --query "password" # Search emails
mails code --to [email protected] # Wait for verification code
How it works
SENDING RECEIVING
Agent External
| |
| mails send --to [email protected] | email to [email protected]
| |
v v
+--------+ +-------------------+
| CLI |------ /api/send ----------------->| Cloudflare Email |
| /SDK |<----- /api/inbox -----------------| Routing |
+--------+ +-------------------+
| |
v v
+--------------------------------------------------+
| Your Cloudflare Worker |
| /api/send → Resend API → SMTP delivery |
| /api/inbox, /api/code → D1 query (FTS5 search) |
| email() handler → parse MIME → store in D1 |
+--------------------------------------------------+
| |
v v
+--------+ +------------+
| D1 | | R2 |
| emails | | attachments|
+--------+ +------------+
|
| query via CLI/SDK
v
Agent
mails inbox
mails inbox --query "code"
mails code --to [email protected]
CLI Reference
send
mails send --to <email> --subject <subject> --body <text>
mails send --to <email> --subject <subject> --html "<h1>Hello</h1>"
mails send --from "Name <email>" --to <email> --subject <subject> --body <text>
mails send --to <email> --subject "Report" --body "See attached" --attach report.pdf
inbox
mails inbox # List recent emails
mails inbox --mailbox [email protected] # Specific mailbox
mails inbox --query "password reset" # Search emails
mails inbox --query "invoice" --direction inbound --limit 10
mails inbox <id> # View email details + attachments
code
mails code --to [email protected] # Wait for code (default 30s)
mails code --to [email protected] --timeout 60 # Custom timeout
The code is printed to stdout for easy piping: CODE=$(mails code --to [email protected])
config
mails config # Show all config
mails config set <key> <value> # Set a value
mails config get <key> # Get a value
deploy
One-click self-hosting setup. Automates D1 creation, schema migration, secret setup, and Worker deployment.
cd worker
mails deploy # Automated setup via wrangler
Prerequisites: wrangler login and a Cloudflare account. The command generates a random AUTH_TOKEN and prompts for RESEND_API_KEY.
SDK Usage
import { send, getInbox, searchInbox, getEmail, deleteEmail, waitForCode } from 'mails-agent'
// Send
const result = await send({
to: '[email protected]',
subject: 'Hello',
text: 'World',
})
// Send with attachment
await send({
to: '[email protected]',
subject: 'Report',
text: 'See attached',
attachments: [{ path: './report.pdf' }],
})
// List inbox
const emails = await getInbox('[email protected]', { limit: 10 })
// Search inbox
const results = await searchInbox('[email protected]', {
query: 'password reset',
direction: 'inbound',
})
// Get email details (with attachments)
const email = await getEmail('email-id')
// Delete email (cascade: attachments + R2)
await deleteEmail('email-id')
// Wait for verification code
const code = await waitForCode('[email protected]', { timeout: 30 })
if (code) console.log(code.code) // "123456"
Storage Providers
The CLI auto-detects the storage provider:
worker_urlin config → remote (queries Worker API)- Otherwise → local SQLite (
~/.mails/mails.db)
| Key | Set by | Description |
|---|---|---|
mailbox |
manual | Your receiving address |
worker_url |
manual | Worker URL (enables remote provider) |
worker_token |
manual | Auth token for Worker |
resend_api_key |
manual | Resend API key (not needed when worker_url is set) |
default_from |
manual | Default sender address |
storage_provider |
auto | sqlite or remote (auto-detected) |
Run the entire email system on your own domain using Cloudflare + Resend. Full control, no third-party dependency.
Prerequisites
| What | Why | Cost |
|---|---|---|
A domain (e.g. example.com) |
Email address [email protected] |
You already own one |
| Cloudflare account | DNS, Email Routing, Worker, D1 | Free tier is enough |
| Resend account | SMTP delivery | Free 100 emails/day |
Step 1: Add domain to Cloudflare
If your domain's DNS is not already on Cloudflare, add it at dash.cloudflare.com. Update your registrar's nameservers to the ones Cloudflare provides.
Step 2: Set up Resend for sending
- Create a Resend account
- Go to Domains → Add Domain → enter your domain (e.g.
example.com) - Resend will give you DNS records to add. Go to Cloudflare DNS and add:
- SPF —
TXTrecord on@:v=spf1 include:amazonses.com ~all(Resend uses SES) - DKIM —
CNAMErecords as provided by Resend (usually 3 records) - DMARC —
TXTrecord on_dmarc:v=DMARC1; p=none;(start withnone, tighten later)
- SPF —
- Wait for Resend to verify your domain (usually minutes, can take up to 48h)
- Copy your Resend API key (
re_...) from the Resend dashboard
Step 3: Deploy the Worker
cd worker
bun install
# Create D1 database
wrangler d1 create mails
# → Copy the database_id from the output
# Edit wrangler.toml — paste your database_id
# Replace REPLACE_WITH_YOUR_DATABASE_ID with the actual ID
# Initialize database schema
wrangler d1 execute mails --file=schema.sql
# Set secrets
wrangler secret put AUTH_TOKEN # Choose a strong random token
wrangler secret put RESEND_API_KEY # Paste your re_... key from Resend
# Deploy
wrangler deploy
# → Note the Worker URL: https://mails-worker.<your-subdomain>.workers.dev
Step 4: Set up Cloudflare Email Routing
- Go to Cloudflare Dashboard → your domain → Email → Email Routing
- Click Enable Email Routing (Cloudflare will add MX records automatically)
- Go to Routing rules → Catch-all address → set action to Send to a Worker → select your deployed Worker
- Now all emails to
*@example.comwill be routed to your Worker
Step 5: (Optional) Create R2 bucket for large attachments
wrangler r2 create mails-attachments
The R2 binding is already configured in wrangler.toml. Redeploy after creating the bucket:
wrangler deploy
Step 6: Configure the CLI
mails config set worker_url https://mails-worker.<your-subdomain>.workers.dev
mails config set worker_token YOUR_AUTH_TOKEN # Same token from Step 3
mails config set mailbox [email protected] # Your email address
mails config set default_from [email protected] # Default sender
Step 7: Verify
# Check Worker is reachable
curl https://mails-worker.<your-subdomain>.workers.dev/health
# Check inbox (should be empty)
mails inbox
# Send a test email
mails send --to [email protected] --subject "Test" --body "Hello from self-hosted mails"
# Send an email TO your mailbox from any email client, then:
mails inbox
Architecture after setup
Your Agent External sender
| |
| mails send / mails inbox | email to [email protected]
v v
+--------+ +-------------------+
| CLI |------ /api/send ------->| Cloudflare Email |
| /SDK |<----- /api/inbox -------| Routing |
+--------+ +-------------------+
| |
v v
+--------------------------------------------------+
| Your Cloudflare Worker |
| /api/send → Resend API → SMTP delivery |
| /api/inbox, /api/code → D1 query (FTS5 search) |
| email() handler → parse MIME → store in D1 |
+--------------------------------------------------+
| |
v v
+--------+ +------------+
| D1 | | R2 |
| emails | | attachments|
+--------+ +------------+
Worker Secrets Reference
| Secret | Required | Description |
|---|---|---|
AUTH_TOKEN |
Recommended | API authentication token. If set, all /api/* endpoints require Authorization: Bearer <token> |
RESEND_API_KEY |
Yes (for sending) | Resend API key (re_...). The Worker uses this to send emails via /api/send |
WEBHOOK_SECRET |
Optional | HMAC-SHA256 key for signing outbound webhook payloads (X-Webhook-Signature header) |
RESEND_WEBHOOK_SECRET |
Recommended | Svix HMAC-SHA256 secret for verifying Resend delivery callbacks. If not set, all delivery webhooks are rejected (503). |
DAILY_SEND_LIMIT |
Optional | Max emails per mailbox per day (default: 100) |
Worker API Endpoints
| Endpoint | Description |
|---|---|
POST /api/send |
Send email (requires RESEND_API_KEY secret) |
GET /api/inbox?to=<addr>&limit=20 |
List emails |
GET /api/inbox?to=<addr>&query=<text> |
Search emails (FTS5 full-text search) |
GET /api/code?to=<addr>&timeout=30 |
Long-poll for verification code |
GET /api/email?id=<id> |
Get email by ID (with attachments) |
DELETE /api/email?id=<id> |
Delete email (and its attachments + R2 objects) |
GET /api/attachment?id=<id> |
Download attachment |
GET /api/threads?to=<addr> |
List conversation threads |
GET /api/thread?id=<id>&to=<addr> |
Get all emails in a thread |
GET /api/search?to=<addr>&q=<text>&mode=hybrid |
Semantic/hybrid search (alias for inbox with mode=hybrid) |
POST /api/extract |
Extract structured data (order, shipping, calendar, receipt, code) |
GET /api/events?to=<addr> |
SSE stream of real-time email events |
GET /api/stats?to=<addr> |
Mailbox usage statistics |
GET /api/domains |
List/manage custom sending domains |
POST /api/claim/auto |
Headless mailbox claim (returns API key) |
GET /api/mailbox |
Mailbox info (status, settings) |
PATCH /api/mailbox |
Update mailbox webhook_url |
DELETE /api/mailbox |
Cascade delete mailbox and all its data |
POST /api/mailbox/pause |
Pause mailbox processing |
POST /api/mailbox/resume |
Resume mailbox processing |
GET /api/mailbox/routes |
List label-specific webhook routes |
PUT /api/mailbox/routes |
Upsert label-specific webhook route |
DELETE /api/mailbox/routes?label= |
Delete label-specific webhook route |
GET /api/me |
Worker info and capabilities |
GET /health |
Health check (always public, no auth) |
Send Priority
When the CLI/SDK sends an email, it checks config in this order:
worker_url→ sends via your Worker's/api/send(recommended)resend_api_key→ sends directly to Resend API
Once worker_url is set, you don't need resend_api_key on the client — the Worker holds the Resend key as a secret.
bun test # Unit + mock E2E tests
bun test:coverage # With coverage report
bun test:live # Live E2E with real Resend + Cloudflare (requires .env)
360 tests across 44 test files.
Ecosystem
┌─────────────────────────────────────────────────────────────┐
│ mails ecosystem │
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌───────────┐ │
│ │ mails CLI │ │ mails Worker │ │ mails │ │
│ │ & SDK │───▶│ (Cloudflare) │◀───│ -skills │ │
│ │ │ │ │ │ │ │
│ │ npm i mails- │ │ Receive + Send │ │ Agent │ │
│ │ agent │ │ │ │ │ │
│ │ │ │ + Search + Code │ │ Skills │ │
│ └──────────────┘ └──────────────────┘ └───────────┘ │
│ Human / Script Infrastructure AI Agents │
└─────────────────────────────────────────────────────────────┘
| Project | What it is | Who uses it |
|---|---|---|
| mails (this repo) | Email server (Worker) + CLI + SDK | Developers deploying email infra |
| mails-agent-mcp | MCP Server for AI agents | Claude Desktop, Cursor, any MCP client |
| mails-agent (Python) | Python SDK | Python developers, async agents |
| mails-skills | Skill files for AI agents | AI agents (Claude Code, OpenClaw, Cursor) |
Quick agent setup:
# MCP Server (Claude Desktop / Cursor)
npm install -g mails-agent-mcp
# Python SDK
pip install mails-agent
# Agent Skills
git clone https://github.com/Digidai/mails-skills && cd mails-skills && ./install.sh
Contributing
See CONTRIBUTING.md for development setup, project structure, and PR guidelines.
Acknowledgments
This project is based on mails by turing, originally created as email infrastructure for AI agents. We forked and extended it with mailbox isolation, webhook notifications, delete API, R2 attachment storage, Worker file refactoring, and comprehensive test coverage (360 tests). Thank you to the original author for the excellent foundation.
License
MIT — see LICENSE for details. Original copyright retained per MIT terms.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found