Skip to content
proton-cli

Mail

Read, write, send, search and organize mail. Bodies are decrypted on your machine and outgoing mail is encrypted and signed with your address key, exactly like the web client.

Every command and flag is in the proton mail reference. This page is the things people actually do.

Terminal window
proton mail messages list # the inbox
proton mail messages list --unread
proton mail messages list --folder archive
proton mail messages get "Invoice #2291" # headers, body, attachment list
proton mail messages get REF --body-only > body.txt

Folders are inbox, sent, drafts, trash, spam, archive, starred, scheduled, snoozed, all, or any label. Proton’s inbox tabs are folders too: social, promotions, updates, newsletters, transactions.

get shows the plain-text body by default; --render html gives you the original markup, --render raw the untouched body, and --strip-quotes drops quoted reply blocks. A Signature: line reports the verdict of the signature check on the sender’s key.

Searching is list with a predicate, because it is one request to Proton either way. list opens on the inbox, so --folder all is what widens it to everything.

Terminal window
proton mail messages list --keyword invoice --folder all
proton mail messages list --from billing@example.com --after 2026-01-01 --folder all
proton mail messages list --subject "Q1 report" --folder archive

--from and --to match addresses; --keyword also matches display names and body text. Proton’s index lags a change by a few seconds (why).

Terminal window
proton mail messages send --to alice@proton.me --subject Hi --body "Hello there"
proton mail messages send --to alice@proton.me --subject Report --body "See attached." --attach ./report.pdf
proton mail messages send --to alice@proton.me --subject Hi --body "<b>Hi</b>" --html
echo "Deployed." | proton mail messages send --to me@proton.me --subject Deploy --body -

send prints the new message ID on stdout, so ID=$(proton mail messages send …) works.

Your signature is applied automatically, as in the web client: the address’s own signature, plus Proton’s “Sent with Proton Mail secure email.” footer when your account has it enabled. Free accounts have that footer forced on. --no-signature leaves both out of one message; proton mail settings set pm-signature off turns the footer off account-wide.

On an account with several addresses, --from chooses which one it leaves from - plus-aliases included:

Terminal window
proton mail messages send --from work@example.com --to alice@proton.me --subject Hi --body Hello

Scheduling, self-destruction, and a password for recipients outside Proton:

Terminal window
proton mail messages send --send-at 2026-05-01T09:00 # local time; confirms the resolved time
proton mail messages send --expires 7d
proton mail messages send --eo-password hunter2 --eo-password-hint "our usual"

A scheduled send sits in the scheduled folder until it goes. proton mail messages unschedule REF pulls it back to Drafts.

Terminal window
proton mail messages reply "Invoice #2291" --body "Thanks, paid today."
proton mail messages reply REF --everyone --body "Looping in the team."
proton mail messages forward REF --to alice@proton.me --body "FYI"
proton mail conversations reply REF --body "Works for me." # newest message in a thread

The original is quoted below your text, the subject gains Re: or Fw: (never twice), and the thread stays a thread. A reply leaves from the address the original arrived on; a forward carries the original’s attachments without re-uploading them.

--no-quote sends your text alone, --no-attachments leaves them behind, and --draft stops before sending and prints the new draft’s ID - the same as clicking Reply and leaving the composer open. Reply and forward take everything send takes.

A draft is a message, so messages get, move and the rest already work on one. mail drafts holds what only makes sense before it goes out.

Terminal window
proton mail drafts create --to alice@proton.me --subject Report --body "Draft one."
proton mail drafts update REF --body "Draft two." --attach ./report.pdf --detach old-annex.xlsx
proton mail drafts send REF

update replaces only what you pass. --to, --cc and --bcc replace the whole list; --attach adds a file and --detach removes one. REF resolves within Drafts only, so editing “Report” can never reach a message you already sent. Sending a draft delivers it exactly as stored.

A message lives in exactly one folder and carries any number of labels. So moving and labelling are different verbs, and passing a label to move is an error rather than a silent relabel.

Terminal window
proton mail messages move REF --into archive # it leaves where it was
proton mail messages label REF --label Work # it stays where it is
proton mail messages mark read REF
proton mail messages star REF
proton mail messages trash REF

Every one of those takes filters instead of references, and acts on everything that matches:

Terminal window
proton mail messages trash --unread --older-than 30d
proton mail messages move --into archive --from newsletter@example.com --older-than 7d
proton mail messages delete --folder spam --all

Add --dry-run to see the list first. --limit defaults to 150, which is Proton’s per-page cap.

empty is not delete --all. A filtered delete enumerates what it will touch and shows you; empty asks Proton to clear the folder without ever naming its contents. That is why it takes no filter and always asks.

Terminal window
proton mail messages empty --folder trash
proton mail messages expire REF --in 7d # delete itself later; --never stops it
proton mail messages unsubscribe REF # ask a mailing list to stop

unsubscribe uses whatever the message offered - a List-Unsubscribe header, or the one-click form behind it. Proton does the asking, because Proton is the party the list already knows.

Conversations are whole threads, with the same verbs as messages.

Terminal window
proton mail conversations get REF # every message, chronological
proton mail conversations get --summary REF # one line per message
proton mail conversations snooze REF --until 3d

Snooze is on threads, not messages, because that is what Proton snoozes: a conversation leaves the inbox as a whole and returns as a whole. --until takes a duration from now or a moment; a moment in the past is refused.

Terminal window
proton mail messages attachments list REF
proton mail messages attachments download REF --output-dir ./attachments/
proton mail conversations attachments download REF --output-dir ./thread/

Existing files are never overwritten silently: names collide into file (2).pdf, or pass --force. --include-inline covers embedded images too.

export writes ordinary RFC 822 files you can open in any mail client.

Terminal window
proton mail messages export --folder archive --older-than 1y --all --output-dir ./backup
proton mail messages export --folder inbox --all --format mbox --output inbox.mbox
proton mail messages export REF --output - | formail -X ""

It takes the same filters as trash and move. --format eml writes one file per message named <date> <subject>.eml; --format mbox concatenates. --no-attachments skips downloads, which is much faster for a large archive.

Exported files are not encrypted, and their DKIM signatures no longer verify. The web client’s export behaves the same way.

Going the other way, --eml reads a file back into a draft or a send:

Terminal window
proton mail drafts create --eml ./message.eml
proton mail messages send --eml ./message.eml --to someone-else@proton.me

Any flag you also pass overrides what the file says, and no signature is appended - the file is already a finished message. There is no way to place an old message into your archive: Proton exposes no endpoint that ingests one, for any client.

Terminal window
proton mail messages watch
proton mail messages watch --output json | jq -c .

Stays attached and prints a line the moment a message lands. It reports what happens while it is watching, so nothing that arrived beforehand comes up; a thread coming back from snooze counts as landing. Without --folder it covers the inbox plus every folder whose notifications are on.

Each JSON line names the thread in conversation_id, so a consumer can act on the whole thread without looking it up. See Streams and Desktop notifications.

Terminal window
proton mail settings folders create --name Projects
proton mail settings folders create --name Clients --parent PARENT_FOLDER_ID
proton mail settings labels create --name Important --color "#8080FF"
proton mail settings labels delete Important # by name, or by ID

Colours have to be one of Proton’s 20 accent colours; an invalid one prints the palette and is refused before anything is sent. Deleting a folder or label asks first, and the messages it held are not deleted.

Folders carry a NOTIFY switch - whether mail landing there is worth telling you about. It shows in folders list, --notify sets it, and it decides what messages watch covers by default. Proton offers it on folders alone, so labels have neither.

Server-side filters, the same ones the web client creates. Describe one with --if and Proton writes the Sieve - the same script the web client’s builder produces, so a filter made here opens in it.

Terminal window
proton mail settings filters create --name "Archive invoices" \
--if "subject contains invoice" --move-to Archive
proton mail settings filters create --name Receipts \
--if "sender contains billing@" --if "subject not contains draft" \
--label Receipts --mark-read
proton mail settings filters create --name Loud --match any \
--if "subject starts [ALERT]" --if "attachments contains" --star

A condition reads FIELD [not] COMPARATOR VALUE:

  • Field - subject, sender, recipient, attachments
  • Comparator - contains, is, starts, ends, matches

is wants the whole value, matches takes * and ? as wildcards, not inverts, and attachments takes no value - it asks whether there is one. Every condition has to hold unless you pass --match any.

Actions are --move-to (one folder), --label (repeatable), --mark-read and --star. A filter needs at least one, and every filter skips mail Proton has already called spam.

update rewrites the rule in place and replaces the whole thing rather than adding to it - half a rule is not one. The filter keeps its place in the order and whether it is running, which is what makes this different from deleting it and making a new one. get shows the rule as it stands, in the words --if takes.

Bring your own script with --sieve -; get then shows the script itself rather than words that would not rebuild it.

A filter ordinarily acts once, as mail arrives, so a rule written today does nothing about yesterday’s mail:

Terminal window
proton mail settings filters apply # every enabled filter
proton mail settings filters reorder Newsletters Receipts Archive

Order decides the outcome - the first rule to file a message wins - so reorder replaces the whole order and refuses a partial one.

Terminal window
proton mail settings autoreply set --repeat fixed \
--start 2026-07-01T09:00 --end 2026-07-14T18:00 \
--message "I'm away until the 14th."
proton mail settings autoreply disable

--start and --end are written in the grammar the repeat mode dictates:

--repeat --start / --end
fixed 2026-07-01T09:00 - a date and time
daily 09:00 - a time of day, with --days mon,tue,wed
weekly mon:09:00 - a weekday and time
monthly 1:09:00 - a day of the month and time
permanent not used

All of them take --zone, any IANA name, defaulting to your system’s. Saving a schedule turns the auto-reply on; disable keeps it for later. Proton sends every auto-reply with the subject Auto and offers no way to change it. Auto-reply is a paid feature.

Proton’s settings page shows three lists - spam, block, allow - but they are one record with a destination on it, so list shows all three:

Terminal window
proton mail settings senders list
proton mail settings senders block spammer@example.com
proton mail settings senders block @example.com # a whole domain
proton mail settings senders allow billing@example.com
proton mail settings senders forget billing@example.com

A decision applies before the spam filter forms an opinion, and deciding again about the same sender replaces the earlier decision. forget lets the filter decide again.

Terminal window
proton mail settings # everything, at a glance
proton mail settings set # the writable keys, grouped by page
proton mail settings set view-mode conversations
proton mail settings addresses update me@proton.me --display-name "Roman L."
proton mail settings addresses update me@proton.me --signature - < signature.html --html

Every key has a fixed set of values, checked before anything is sent, and values can be given by name or by Proton’s own number. Signatures are stored as HTML: plain text is escaped and its newlines become line breaks, --html passes markup through.