Skip to the documentation
CLI

Mail

Send with any body and attachments, read and search the mailbox, reply, file threads, and use disposable inboxes.

Send

Terminal
openemail send --to [email protected] --subject "Lunch?" --text "Thursday at noon works for me."

The body comes from the first of these that is there:

  1. --text and --html, both together if you want both parts.
  2. --body-file <path>. A .html or .htm file is sent as HTML and anything else as text, and - reads stdin.
  3. Whatever is piped on stdin. Input that starts with an HTML tag is sent as HTML.
  4. Your editor ($VISUAL, then $EDITOR), when you run it in a terminal.

Without --from, the CLI sends from the only address you can send from, or lets you pick one in a terminal. Unattended with more than one, it stops and asks for --from. In a terminal you see a summary and confirm before anything goes, which --yes skips.

FlagWhat it does
--to, --cc, --bccRecipients, comma separated or repeated. Ada Lovelace <[email protected]> works too
--from, --reply-toThe sending address, and the Reply-To header
-s, --subjectThe subject line
-a, --attach <path>Attach a file. Repeat it for more
--at <when>Send later: an ISO time such as 2026-10-01T09:00:00Z, or a delay such as 10m, 2h, 1d or PT1H
--undo <seconds>Hold an immediate send for up to 900 seconds, so it can still be cancelled
--translate <language>Translate the email into this language before it goes
--template <id>, --props <json>Send a stored template with its props instead of a body
--draft <id>Send a saved draft exactly as it was written
--thread <id>File the sent email into this thread
--tag <key=value>Tag the email. Repeat it for more
--idempotency-key <key>Running the command again with the same key never sends twice
More sends
cat report.md | openemail send --to [email protected] --subject "Weekly report" --attach chart.pngopenemail send --to [email protected] --subject "Invoice" --body-file invoice.html --translate de --at 2hopenemail send --to [email protected] --template welcome --props '{"name":"Ada"}' --tag campaign=onboarding

It prints the email id and its status. sent, partial and failed mean the send was tried within the request, and queued and scheduled mean it is held. A failed send exits with code 1, and --json prints the whole email.

Every send carries an idempotency key, yours or one the CLI makes up, so a retry after a dropped connection never sends the message twice. Pass your own when a pipeline may run the same step again.

Attachments

--attach takes up to 20 files. While they come to 5 MB or less in total, they travel inside the message. When they come to more, each one is uploaded to Files first and the message refers to it, so a large file needs no extra step. The content type comes from the file extension.

Inbox

Terminal
openemail inboxopenemail inbox --unread --limit 50openemail inbox archive --sort oldestopenemail inbox --all --json | jq -r ".items[].id"

Lists one page of threads, newest first, with when each last moved, who wrote last, the subject, the labels and the thread id. A dot marks unread mail. The folder is the inbox unless you name another: sent, archive, starred, snoozed, spam, trash, draft, or a label id such as USER_RECEIPTS.

  • -u, --unread keeps threads with unread mail, -l, --label keeps threads that carry a label, and -q, --query narrows the list with a search.
  • -n, --limit sets the page size, from 1 to 100 (25 unless you say). When more remain, the next cursor is printed on stderr for --cursor, and --all reads every page.
  • --from-contacts keeps threads whose newest message came from a saved contact, and --sort orders them by newest, oldest, sender or subject.
  • --json prints { items, hasMore, nextCursor }, with every thread whole.

Read a thread

Terminal
openemail read CAHk7pQ2x9LmZ4openemail read CAHk7pQ2x9LmZ4 --message -1openemail read CAHk7pQ2x9LmZ4 --html --message 1 > first.html

Prints every message on the thread, oldest first: who sent it and to whom, when, the subject, the attachments with their sizes and ids, and the body. A plain text part is shown as it is, and an HTML-only message is turned into readable text that keeps its lists, quotes, headings and links. Reading marks the thread read, unless you pass --no-mark-read.

  • -m, --message <n> shows one message, counting from 1, or from the newest with a negative number.
  • --html prints the raw HTML instead.
  • An encrypted message is not decrypted here, so open it in the web app.

Reply

Terminal
openemail reply CAHk7pQ2x9LmZ4 --text "Thanks, that works for me."openemail reply CAHk7pQ2x9LmZ4 --all --attach notes.pdfecho "Confirmed." | openemail reply CAHk7pQ2x9LmZ4 --yes

Replies in the same thread to whoever sent the last message, or to its Reply-To address. When the last message is yours, the reply goes to the people it went to. --all copies everyone else on that message, leaving out your own addresses. The subject gets Re: unless it has it already, and the reply goes from the address the thread was delivered to when you can send from it.

It takes the body, attachment, scheduling, translation, tag and idempotency flags of send, with --from and --subject, and --to, --cc and --bcc override the recipients.

File threads

CommandWhat it does
archive <thread-id...>Out of the inbox, still searchable
unarchive <thread-id...>Back into the inbox
trash <thread-id...>To the Bin. It asks you to confirm, because no call takes a thread back out
star, unstarAdd or remove the star
mark read, mark unreadMark threads read or unread
snooze <thread-id...> --until <when>Hide threads until a time, or for a delay such as 3h or 1w. They come back within about an hour of it
unsnoozeBring snoozed threads back now
label add, remove --label <id>Put labels on threads or take them off. openemail labels list shows the ids

Each takes several thread ids and reports on each one. The first failure sets the exit code, and --json prints { results, succeeded, failed }. Ids from a search chain straight in:

Terminal
openemail inbox --query "newer_than:30d from:alerts" --json | jq -r ".items[].id" | xargs openemail archive

Disposable inboxes

openemail temp makes short-lived addresses that need no account and no sign-in, which suits sign-up tests and one-time codes. Each inbox token is kept in ~/.openemail/temp-mail.json, so later commands find an inbox by its id or address, or on their own when you have only one.

Terminal
ADDRESS=$(openemail temp new --ttl 15)openemail temp watch --first --json | jq -r .snippetopenemail temp listopenemail temp readopenemail temp read tinb_4f2a tmail_91c3openemail temp delete --yes
  • temp new prints only the address on stdout. It lives for 60 minutes unless --ttl says otherwise, for up to 24 hours, and --name and --domain choose the address.
  • temp watch prints what has arrived, then checks every 3 seconds until you stop it or the inbox expires. --first stops at the first message, and --json prints one JSON line per message.
  • temp read lists the messages with a short preview, or prints one whole message.
  • temp delete deletes the inbox and its mail at once. The address stays reserved for 7 days after its lease would have ended.
  • --inbox-token reaches an inbox this CLI did not create, in temp read, watch and delete.
  • The sender of mail in a disposable inbox is never verified.

Your inbox,
on your own terms.

Email infrastructure for businesses, AI, agents and personal email. Built for scale, privacy and control. Everything email should have had from day one.

OpenEmail

Email infrastructure for businesses, AI, agents and personal email. Built for scale, privacy and control. Everything email should have had from day one.

© 2026 OpenEmail. All rights reserved.