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.
Why | Install | How It Works | Commands | Agent Integration | Configuration | Contributing
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.
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.
curl -fsSL https://raw.githubusercontent.com/paperfoot/email-cli/main/install.sh | shbrew tap paperfoot/tap
brew install email-clicargo install email-cliemail-cli update # self-update from GitHub Releases
email-cli update --check # check without installing# 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."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.
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 accountemail-cli autostart install # installs ~/Library/LaunchAgents/ai.paperfoot.email-cli.daemon.plist
email-cli autostart status # check if loaded
email-cli autostart uninstall # removeThe LaunchAgent loads immediately (no reboot needed) and restarts the daemon automatically if it exits unexpectedly. Logs go to /tmp/email-cli-daemon.log.
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.
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-infoAudience 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.
Email CLI follows the agent-cli-framework patterns. Any agent that speaks structured JSON can use it.
email-cli agent-infoReturns a JSON manifest of every command, flag, exit code, and output format. An agent calls this once and works from memory.
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"
}
}| 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 |
email-cli skill installWrites 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.
All data lives in ~/.local/share/email-cli/email-cli.db (override with --db <path>). Sibling directories:
draft-attachments/-- snapshots of files attached to draftsdownloads/-- fetched attachments (configurable via--output)
SQLite runs with WAL mode, busy timeout, and foreign keys enabled.
- 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.
- 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)
Contributions are welcome. See CONTRIBUTING.md for guidelines.
MIT -- see LICENSE.