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)
|
Homebrew
Cargo
Update
Quick Start
# 1. Add your Resend API key
# 2. Register a sending identity
# 3. Send an email
# 4. Sync and read incoming mail
# 5. Reply (threads correctly with In-Reply-To + References)
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.
Run at login
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:
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
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:
Errors include actionable suggestions:
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
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.
Built by Boris Djordjevic at 199 Biotechnologies | Paperfoot AI
If this is useful to you: