A command-line interface for ProtonMail via Proton Bridge IMAP/SMTP. Designed for terminal use and AI agent integration.
go install github.com/bscott/pm-cli/cmd/pm-cli@latest
- Proton Bridge running and logged in
- Go 1.21+
- Start Proton Bridge and log in to your account
- Get your Bridge password from the Bridge app (Account → IMAP/SMTP settings)
- Run the setup wizard:
pm-cli config init
pm-cli mail list # Latest 20 messages in INBOX pm-cli mail list -n 50 # Latest 50 messages pm-cli mail list -m Sent # Messages in Sent folder pm-cli mail list --unread # Only unread (server-side SEARCH UNSEEN) pm-cli mail list --flagged # Only flagged/starred (SEARCH FLAGGED) pm-cli mail list --offset 20 # Skip first 20 messages pm-cli mail list -p 2 -n 20 # Page 2 (messages 21-40) pm-cli mail list --json # JSON output pm-cli mail list --json --fields uid,from_address,subject # Project JSON fields pm-cli mail list --json --compact # Bare JSON array
pm-cli mail read 123 # Read message #123 pm-cli mail read uid:456 # Read by stable UID pm-cli mail read 123 -m Archive # Read from a specific mailbox pm-cli mail read 123 --json # JSON output with body pm-cli mail read 123 --headers # Include all headers pm-cli mail read 123 --unread # Mark unread after reading pm-cli mail read 123 --html # Output HTML body pm-cli mail read 123 --attachments # List attachments pm-cli mail read 123 --raw # Raw MIME source
pm-cli mail send -t user@example.com -s "Subject" -b "Body text" pm-cli mail send -t user@example.com -s "Subject" -a attachment.pdf pm-cli mail send -t user@example.com --template welcome.yaml -V name=Alice echo "Body from stdin" | pm-cli mail send -t user@example.com -s "Subject"
pm-cli mail reply 123 -b "Thanks for the info" pm-cli mail reply 123 --all -b "Reply to all" pm-cli mail forward 123 -t other@example.com -b "FYI"
pm-cli mail read 123 --attachments # List attachments with indices pm-cli mail download 123 0 # Download first attachment pm-cli mail download 123 0 -o ~/Downloads/file.pdf
pm-cli mail draft list # List all drafts pm-cli mail draft create -t user@example.com -s "Subject" -b "Draft body" pm-cli mail draft edit 456 -b "Updated body" pm-cli mail draft delete 456
pm-cli mail thread 123 # Show full conversation threadpm-cli mail watch # Watch INBOX, poll every 30s pm-cli mail watch -m INBOX -i 60 # Poll every 60 seconds pm-cli mail watch --exec "notify-send 'New mail: {}'" # Run command on new mail pm-cli mail watch --once # Exit after first new message
pm-cli mail delete 123 # Move to trash pm-cli mail delete 123 456 789 # Batch delete pm-cli mail delete --query "from:spam@example.com" # Delete by search pm-cli mail delete 123 --permanent # Delete permanently pm-cli mail move 123 Archive # Move to folder pm-cli mail archive 123 # Shortcut: move to Archive pm-cli mail move 123 456 -d Archive # Batch move pm-cli mail flag 123 --read # Mark as read pm-cli mail flag 123 --star # Add star pm-cli mail flag 123 456 --unread # Batch flag
Use sequence numbers by default (the ID shown in mail list), or use explicit UID selectors for stable references:
pm-cli mail read 123 # sequence number (can change over time) pm-cli mail read uid:456 # UID selector (stable within mailbox)
JSON outputs include both seq_num and uid. mail read JSON and header output include message_id (RFC 5322 Message-ID header) when available.
pm-cli mail label list # List available labels pm-cli mail label add 123 -l Important pm-cli mail label add 123 456 -l "Work/Projects" pm-cli mail label remove 123 -l Important
pm-cli mail search "invoice" # Search body text pm-cli mail search --from boss@example.com # Filter by sender pm-cli mail search --subject "meeting" # Filter by subject pm-cli mail search --since 2024年01月01日 # Messages since date pm-cli mail search --before 2024年12月31日 # Messages before date pm-cli mail search --has-attachments # Only with attachments pm-cli mail search --larger-than 1M # Size filters pm-cli mail search --from user@example.com --or --subject "urgent" # Boolean OR pm-cli mail search --from spam@example.com --not # Negate search
pm-cli contacts list # List all contacts pm-cli contacts search "alice" # Search by name or email pm-cli contacts add alice@example.com -n "Alice Smith" pm-cli contacts remove alice@example.com
pm-cli mailbox list # List all mailboxes pm-cli mailbox create "Projects" # Create mailbox pm-cli mailbox delete "Old Folder" # Delete mailbox
pm-cli config show # Display current config pm-cli config set defaults.limit 50 # Set default limit pm-cli config validate # Test Bridge connection pm-cli config doctor # Run diagnostics (8 checks)
pm-cli is designed for AI agent workflows with machine-readable output:
# Get full command schema for agents pm-cli --help-json # All commands support JSON output pm-cli mail list --json pm-cli mail read 123 --json pm-cli mailbox list --json
# Get AI-friendly email summary pm-cli mail summarize 123 --json # Returns: sentiment, priority, action_required, summary # Extract structured data from email pm-cli mail extract 123 --json # Returns: emails, URLs, dates, phone numbers, action items
# Prevent duplicate sends with idempotency keys pm-cli mail send -t user@example.com -s "Alert" -b "..." --idempotency-key "alert-2024年01月15日" pm-cli mail reply 123 -b "..." --idempotency-key "reply-123-v1"
{
"mailbox": "INBOX",
"count": 3,
"messages": [
{
"uid": 97,
"seq_num": 97,
"from": "Sender Name",
"from_address": "sender@example.com",
"to": ["me@example.com"],
"subject": "Meeting tomorrow",
"message_id": "abc123@example.com",
"in_reply_to": "prev456@example.com",
"date": "2024年01月15日 10:30",
"date_iso": "2024年01月15日T10:30:00Z",
"seen": false,
"flagged": true
}
]
}Create reusable templates with YAML frontmatter:
# templates/welcome.yaml --- subject: "Welcome to {{company}}, {{name}}!" to: - "{{email}}" cc: - onboarding@company.com --- Hi {{name}}, Welcome to {{company}}! We're excited to have you. Best regards, The Team
Use with variables:
pm-cli mail send --template welcome.yaml -V name=Alice -V email=alice@example.com -V company=Acme
Config file: ~/.config/pm-cli/config.yaml
bridge: imap_host: 127.0.0.1 imap_port: 1143 smtp_host: 127.0.0.1 smtp_port: 1025 email: user@protonmail.com defaults: mailbox: INBOX limit: 20 format: text
Password is stored securely in the system keyring (libsecret on Linux).
Servers without a desktop session often have no D-Bus secret service, so the
keyring is unavailable. Set the PM_CLI_BRIDGE_PASSWORD environment variable
and pm-cli will use it instead of the keyring:
export PM_CLI_BRIDGE_PASSWORD='your-bridge-password' pm-cli mail list
The environment variable takes precedence over the keyring and is only consulted when set, so interactive users are unaffected. Prefer injecting it from a secrets manager rather than hardcoding it in shell profiles.
| Flag | Description |
|---|---|
--json |
Output as JSON |
--help-json |
Full command schema as JSON |
-c, --config |
Path to config file |
-v, --verbose |
Verbose output |
-q, --quiet |
Suppress non-essential output |
--no-color |
Disable colored output (also respects NO_COLOR env) |
- Download Proton Bridge
- Log in to your Proton account
- Enable and start the service:
# Linux (systemd) systemctl --user enable --now protonmail-bridge
pm-cli works on Linux, macOS, and Windows - anywhere Proton Bridge runs. As long as Proton Bridge is installed, logged in, and running, pm-cli will connect to it.
| Platform | Keyring Backend |
|---|---|
| Linux | libsecret (GNOME Keyring, KWallet) |
| macOS | Keychain |
| Windows | Windows Credential Manager |
MIT