13 releases (5 breaking)
Uses new Rust 2024
| new 0.7.3 | May 14, 2026 |
|---|---|
| 0.6.6 | Apr 19, 2026 |
| 0.4.0 | Apr 5, 2026 |
| 0.3.0 | Apr 3, 2026 |
| 0.1.0 | Mar 27, 2026 |
#87 in Email
1MB
9K
SLoC
Contains (Mach-o exe, 77KB) email-cli-notify
Email CLI
Send, receive, and manage email from your terminal. Built for AI agents.
A single binary that gives your AI agent a real email address. Send, receive, reply, draft, sync, and manage contacts, broadcasts, segments, and topics through Resend -- all from the command line. No IMAP. No browser inbox. No MCP server. Just a local SQLite mailbox and a command surface agents discover at runtime.
Works inside any agent harness that can invoke a CLI: Claude Code, Cursor, Warp, Codex, Gemini CLI, plain shell scripts. One Resend API key is the only external requirement.
Pair it with Minimail — a macOS menu bar GUI that shells out to this CLI — if you also want a five-second visual peek at the inbox.
Why | Install | How It Works | Commands | Agent Integration | Companion App | Configuration | Contributing
Why
AI agents need email. The existing options are bad:
- IMAP/SMTP requires complex server configuration, credential management, and connection handling. Agents struggle with it.
- Email APIs work for sending, but agents need a local mailbox to track threads, drafts, and read state.
- Browser-based inboxes are not scriptable. Agents cannot use them.
- MCP servers add a network hop and a config burden per harness. Overkill when a signed binary already works.
Email CLI wraps the Resend API in a local-first CLI. Your agent gets a verified email address, a local SQLite mailbox, and structured JSON output with semantic exit codes. It calls agent-info once to learn every command, then works from memory.
It works just as well for humans — and pairs with Minimail if you want a GUI.
Install
One-line install (pre-built binary, no Rust required)
curl -fsSL https://raw.githubusercontent.com/paperfoot/email-cli/main/install.sh | sh
Homebrew
brew tap paperfoot/tap
brew install email-cli
Cargo
cargo install email-cli
Update
email-cli update # self-update from GitHub Releases
email-cli update --check # check without installing
Quick Start
# 1. Add your Resend API key
email-cli profile add default --api-key-env RESEND_API_KEY
# 2. Register a sending identity
email-cli account add agent@yourdomain.com \
--profile default \
--name "Agent" \
--default
# 3. Send an email
email-cli send \
--to someone@example.com \
--subject "Hello from email-cli" \
--text "Sent from the terminal."
# 4. Sync and read incoming mail
email-cli sync
email-cli inbox ls
email-cli inbox read 1 --mark-read
# 5. Reply (threads correctly with In-Reply-To + References)
email-cli reply 1 --text "Got it, thanks."
How It Works
Three concepts:
- Profile -- a Resend API key. You can have multiple profiles for different Resend accounts.
- Account -- a sender/receiver identity (
agent@yourdomain.com). Each account belongs to a profile. - Local mailbox -- a SQLite database that stores messages, drafts, attachments, and sync cursors.
Resend handles delivery. Email CLI handles the local operating model: read tracking, threading, drafts, batch sends, and structured output.
┌────────────────────────────────┐
│ Your Agent / You │
│ (Claude, Codex, Gemini) │
└──────────────┬─────────────────┘
│ CLI commands
▼
┌────────────────────────────────┐
│ email-cli │
│ structured JSON output, │
│ semantic exit codes │
└──────────┬─────────┬───────────┘
│ │
┌─────▼──┐ ┌───▼────────┐
│ SQLite │ │ Resend API │
│ local │ │ (send, │
│ store │ │ receive, │
│ │ │ domains) │
└────────┘ └────────────┘
Every outgoing email gets a unique Message-ID; reply and send --reply-to-msg set In-Reply-To and References per RFC 5322, so threads display correctly in Gmail, Outlook, and Apple Mail.
Menu Bar Daemon (macOS)
email-cli daemon runs in the background, syncs your inbox on a timer, and shows unread count in the menu bar. Native UNUserNotificationCenter banners fire on incoming mail -- including from sync --notify and webhook listen --notify, even if the daemon isn't running.
email-cli daemon # foreground, default 60s interval
email-cli daemon --interval 30 # 30s poll
email-cli daemon --account you@x.com # single account
Run at login
email-cli autostart install # installs ~/Library/LaunchAgents/ai.paperfoot.email-cli.daemon.plist
email-cli autostart status # check if loaded
email-cli autostart uninstall # remove
The LaunchAgent loads immediately (no reboot needed) and restarts the daemon automatically if it exits unexpectedly. Logs go to /tmp/email-cli-daemon.log.
How notifications work
The first time a notification fires, email-cli extracts an embedded codesigned .app bundle to ~/Library/Application Support/email-cli/EmailCLI.app/. macOS 26+ requires this bundle for UNUserNotificationCenter to work -- raw binaries fail TCC silently. If the bundle can't be extracted, email-cli falls back to osascript.
Commands
Email CLI groups its commands by area:
- Email --
send,reply,forward,sync,inbox,draft,attachments - Identity --
profile,account,signature,domain,api-key - Audience (mailing lists) --
contact,segment,contact-property,topic,broadcast - Delivery --
outbox(durable send queue),webhook listen,events,email list,batch send - Daemon --
daemon,autostart(macOS menu bar, native notifications, LaunchAgent autostart) - Agent tooling --
agent-info,skill install,completions - Ops --
update(self-update),log(command audit trail)
The canonical, always-current reference -- every command, subcommand alias, flag, and exit code -- lives in the CLI itself:
email-cli agent-info
Audience primitives: Resend renamed "Audiences" to Segments in November 2025. Contacts are flat -- each contact can belong to zero, one, or multiple segments. Topics drive granular per-contact subscription preferences for broadcasts. Contact properties are typed custom fields for merge tags. Use
segment,topic, andcontact-propertyrespectively.
Agent Integration
Email CLI follows the agent-cli-framework patterns. Any agent that speaks structured JSON can use it.
Capability Discovery
email-cli agent-info
Returns a JSON manifest of every command, flag, exit code, and output format. An agent calls this once and works from memory.
Structured Output
All commands produce JSON when piped or when you pass --json:
{ "version": "1", "status": "success", "data": { ... } }
Errors include actionable suggestions:
{
"version": "1",
"status": "error",
"error": {
"code": "config_error",
"message": "no default account configured",
"suggestion": "Run profile add / account add to configure"
}
}
Semantic Exit Codes
| Code | Meaning | Agent action |
|---|---|---|
| 0 | Success | Continue |
| 1 | Transient error (network) | Retry |
| 2 | Configuration error | Fix setup |
| 3 | Bad input | Fix arguments |
| 4 | Rate limited | Wait and retry |
Skill Self-Install
email-cli skill install
Writes a skill file to ~/.claude/skills/email-cli/, ~/.codex/skills/email-cli/, and ~/.gemini/skills/email-cli/. The skill tells agents the CLI exists and to run agent-info for full details.
Companion App: Minimail
Email CLI is the agent-facing half of a two-component product. The human-facing half is Minimail — a macOS 26 menu bar app that shells out to this CLI for every operation.
| Email CLI | Minimail | |
|---|---|---|
| Interface | Terminal, structured JSON | SwiftUI popover, 420×580 |
| Audience | AI agents, automation, scripting | Humans who want a quick visual peek |
| Platforms | macOS, Linux, Windows | macOS 26 only |
| License | MIT | Proprietary (paid app, coming soon) |
| Required? | Yes, for everything | No — purely optional |
Minimail makes no network calls of its own; it runs email-cli send ... --json and friends, then renders the output. Every feature in the GUI maps to a subcommand here. Install either, both, or neither — the CLI is the product and the GUI is a convenience layer.
Configuration
Local State
All data lives in ~/.local/share/email-cli/email-cli.db (override with --db <path>). Sibling directories:
draft-attachments/-- snapshots of files attached to draftssent-attachments/-- snapshots of files attached to messages sent from this machinedownloads/-- fetched attachments (useattachments get --output-dir, legacy--output, or exact--output-file)
SQLite runs with WAL mode, busy timeout, and foreign keys enabled.
Security
- API keys live in the local SQLite database. Treat
email-cli.dbas sensitive. - Prefer
--api-key-env VAR_NAMEor--api-key-file pathover passing keys directly. - Attachment filenames are sanitized before writing to disk.
- Every send is written to a durable outbox with a stable
Idempotency-Keybefore delivery is attempted; retry withoutbox retryoroutbox flush.
Requirements
- A Resend API key with sending enabled
- A verified Resend domain (enable receiving on the domain for inbox sync)
- Rust 1.85+ if building from source (edition 2024)
Contributing
Contributions are welcome. See CONTRIBUTING.md for guidelines.
License
MIT -- see LICENSE.
Dependencies
~49–71MB
~1M SLoC