Draft Format
Markdown email drafts with YAML frontmatter.
Basic Format
Email drafts are plain Markdown files with YAML frontmatter. The frontmatter holds
metadata (recipients, subject, status), and the body below the second ---
is the email content written in Markdown, which gets converted to HTML when sent.
---
to: recipient@example.com
cc: optional@example.com
bcc: hidden@example.com
subject: "Your subject line"
status: draft
from: alternate@example.com
reply_to: reply@example.com
attachments:
- /path/to/file.pdf
---
Email body in **Markdown** format. Frontmatter Fields
| Command | Description |
|---|---|
to | Recipient address(es). Required. Comma-separated for multiple. |
cc | Carbon copy address(es). Optional. |
bcc | Blind carbon copy address(es). Optional. |
subject | Email subject line. Required. |
status | Workflow state: draft, approved, or sent. Required. |
from | Sender address. Optional; overrides default_from from config. |
reply_to | Reply-to address. Optional. |
attachments | List of file paths to attach; a folder path attaches every file inside it. Optional. |
in_reply_to | Message-ID of the message this draft answers. Written by mp reply; on send, the source is marked answered. |
forwarded_from | Message-ID of the message this draft forwards. Written by mp forward; on send, the source is marked forwarded. |
Status Workflow
Drafts follow a strict workflow. Only approved emails can be sent: the send
command rejects drafts that have not been approved first. These three are the whole
vocabulary of status:; where a message sits (inbox, archive, sent) is a
mailbox, not a status.
After Sending
When an email is sent, the file is updated in-place. The status changes
to sent, and three fields are added: sent_at (ISO 8601 timestamp),
sent_via (tool version), and message_id (the RFC 822 Message-ID,
used for sync deduplication).
---
to: recipient@example.com
subject: "Project update"
status: sent
sent_at: "2026-04-22T10:30:00Z"
sent_via: "mailypoppins 0.6.0"
message_id: "<unique-id@example.com>"
---
Email body here. A send that reached every recipient and was recorded in the outbox then deletes the draft file: the copy that matters lives on the server, where the outbox appends it to Sent and sync reads it back into the local store.
A partial send, or one the outbox store could not record, keeps the file with
status: sent, because it is the only local copy left. Sending it again means
editing status: back to approved by hand, because
mp send refuses anything that is not approved and mp mark-approved
refuses anything already sent. Trim the recipient lines first: a re-send delivers to
everyone the file still lists, including the ones who already received it.
The raw RFC 822 message is also appended to the server-side Sent folder
via IMAP APPEND with the \Seen flag. This is best effort: a failed
APPEND is logged but does not fail the send.
Editing Drafts
A draft is edited as the file it is. The TUI suspends itself and opens the file in
$EDITOR, and returns when the editor exits.
The desktop app runs a terminal editor such as Neovim inside its reader pane, with your
own configuration and plugins, while the message list stays usable beside it. It picks
the editor from MP_DESKTOP_EDITOR, its editor setting, $VISUAL
or $EDITOR, and otherwise looks for nvim, vim and
hx, also in the Homebrew directories, so a launch from the Finder finds them.
Every save updates the list at once. Quitting with :wq shows the draft's
preview; :q! drops only the unsaved changes and never the file, and an editor
that crashes leaves the draft as last saved, with a Reopen button.
Selecting another message while the editor runs asks whether to keep editing in the
background, close the editor (the draft stays), or stay. Closing the window asks whether
to close the editor or stay. A GUI editor such as VS Code or Zed named in the setting opens
the draft in its own window instead, and so does one in $VISUAL or
$EDITOR when neither of the two names a terminal editor.
Reply Drafts
Reply drafts are created with mp reply (or r to reply,
ca to reply-all, in the TUI). They record the source message in in_reply_to. The account signature
(if configured) is appended to the reply area, above a <!-- mp:quote -->
comment that marks the boundary with the quoted original message.
A reply goes to the message's Reply-To address, or to its sender when it has
none; a reply-all also copies everyone else the message was sent to, except you.
Replying to a message you sent yourself (its sender is the account's
default_from) goes back to that message's recipients instead: a reply
addresses its original To, and a reply-all its original To and Cc. Your own address is
left out of both, unless it was the only To.
When the reply is sent, the message it answers is marked as answered, locally and on the server. The list then shows a reply arrow next to it instead of the plain read marker.
The signature is part of the draft body, so it is visible and editable before you
send. The <!-- mp:quote --> marker carries no text of its own; the
send path uses it to place the quoted original below your reply and strips it from
every sent copy. If it is removed, the quote falls back to the end of the body.
Drafts written by older versions carry {{SIGNATURE}} in the same
place; it is read as the same boundary, so those drafts still send correctly.
---
to: original-sender@example.com
subject: "Re: Original subject"
status: draft
in_reply_to: "<message-id@example.com>"
---
Your reply goes here.
--
Your Name
<!-- mp:quote -->
> On 2026-01-15, sender wrote:
>
> Original message text quoted here. Forward Drafts
Forward drafts are created with mp forward (or cf in the TUI).
The to: field is left empty for you to fill in. The subject gets a
"Fwd:" prefix, and any attachments from the original email are listed in the
attachments: array with their absolute file paths. The source message
is recorded in forwarded_from and is marked as forwarded once the draft
goes out.
---
to: ""
subject: "Fwd: Original subject"
status: draft
forwarded_from: "<message-id@example.com>"
attachments:
- /path/to/original-attachment.pdf
---
Your note to the recipient.
--
Your Name
<!-- mp:quote -->
---------- Forwarded message ----------
From: Original Sender <sender@example.com>
Date: 2026-01-15
Subject: Original subject
To: you@example.com
Original message body here. Signatures
A signature is one Markdown file in ~/.config/mailypoppins/signatures/,
named by its file stem: work.md is the signature work.
Which one an account uses by default is app state, recorded in the app's own state
file at <data_dir>/state.json.
The signature is appended to the draft body when the draft is created: after the body for a new message, in the reply area above the quoted content for a reply or forward. An account with no default signature adds nothing.
# ~/.config/mailypoppins/signatures/work.md
--
**Your Name**
Title | Organization
your.name@example.com
Use -s <name> to select a specific signature, or
--no-signature to skip it entirely.
cs in the TUI opens the signature manager: the files with the default
starred, Enter to set or clear the default, e to edit the
selected one in $EDITOR, n to create, r to
rename, d to delete after a confirmation.
In the TUI compose wizard the Signature field selects which
signature the draft uses: cycle it with Up/Down (or
Ctrl+n/Ctrl+p), and press e to edit the
selected signature file in $EDITOR. The same field appears when you edit
an existing draft's recipients (ce in Drafts): changing the selection
re-splices the signature block in the draft body. The active selection is recorded in
the draft's signature frontmatter field; the spliced block is wrapped in
<!-- mp:sig-start --> / <!-- mp:sig-end -->
comment markers, which are stripped from every sent copy and from the preview.
Attachments
A draft's attachments: field holds file paths, and any readable path works.
An entry that points at a folder attaches every file directly inside it (sorted by name,
subfolders and dotfiles skipped), so a batch of files can be named by one directory rather
than listed one by one.
Received attachments are not files: they live as blobs beside the message row in the
account's store. Forwarding a message therefore materialises its attachments once, into a
stable per-message directory under the account, and writes those paths into the draft, so
the forward still resolves them after the original has been archived.
Images the original's HTML body shows inline through a cid: reference, such as
a signature's logos, are part of the body rather than attachments and are not carried; an
image the body does not reference is.
accounts/<name>/attachments/<message-id>/
document.pdf
image.png ---
to: recipient@example.com
subject: "Fwd: Document delivery"
status: draft
attachments:
- "/home/you/.local/share/mailypoppins/accounts/work/attachments/abc@example.com/document.pdf"
- "/home/you/Documents/cover-letter.pdf"
---
To read an attachment of a received message rather than forward it: in the TUI press
to to open one in the default app (it is written to a private temp directory
first) or ts to save it to a directory you choose, and from the CLI use
mp save <selector> --output <dir>.