General

Config file not found

Run mp config init to create the config file at ~/.config/mailypoppins/config.toml. The interactive wizard will walk you through SMTP/IMAP credentials, test connections, discover server mailboxes, and assign roles.

Password not found in keyring

Run mp config set-password to store your SMTP or IMAP password in the OS keyring (macOS Keychain, Windows Credential Manager, or Linux Secret Service). Passwords are never stored in the config file.

Email not approved for sending

The draft workflow requires explicit approval before sending. Run mp mark-approved <selector> to change a draft's status from draft to approved; only approved emails can be sent. In the TUI, x approves and sends the current draft in one step. To reverse an accidental approval, run mp mark-draft <selector> and the draft is editable again.

SMTP authentication failed

Check your credentials with mp config show and re-run mp config set-password smtp if needed. For OAuth2 accounts, try mp config oauth2-login to refresh the token.

My draft came out without a signature

A signature is a Markdown file in ~/.config/mailypoppins/signatures/, and the account needs one of them set as its default. mp config show prints the directory, the current default, and every file it found; press cs in the TUI to set the default, or to create a signature if there is none yet.

mp warns that my [accounts.signatures] tables are dead

Signatures moved out of config.toml into their own Markdown files. The first run after the upgrade copied your entries to ~/.config/mailypoppins/signatures/ and kept the account default. mailypoppins does not edit config.toml for you: delete the [accounts.signatures] tables yourself and the notice stops.

Read the notice before you delete anything. It lists any entry that could not be copied out, and those tables are the only place that content still exists.

I want one row per message, not per conversation

By default the list groups each mailbox by conversation. Press tc (or run "Conversation view on / off for this account" from the command palette) to list the account one row per message; press it again to group it once more. The TUI keeps the choice per account in ~/.config/mailypoppins/tui.json as {"flat_list_accounts": ["work"]}. The desktop app keeps its own choice in its local preferences, and tc, the palette and the "Conversation view" switch on the account's Settings card all change it. The Drafts mailbox always lists one draft per row.

I forgot a key binding

Press : or Ctrl+p to open the command palette, type part of an action's name and press Enter to run it against the current context. Keys are grouped in mnemonic families (c compose, t thread, conversation view and attachments, f find, s sync and settings, g go-to), so pressing a leader alone pops up a which-key list of its continuations. ? shows the full catalogue, and mp dump-keys prints the same table outside the TUI.

OAuth2 / Exchange

IMAP is disabled for my organization

Your Microsoft 365 tenant may have IMAP disabled. Contact IT to enable it, or use auth_method = "graph" in your account config, which bypasses IMAP/SMTP entirely and uses the Microsoft Graph API for all mail operations.

Needs admin consent for API permissions

The IMAP.AccessAsUser.All and SMTP.Send permissions often require admin consent. Request your IT administrator to grant consent via Azure portal > Entra ID > App registrations > API permissions.

OAuth2 token expired

Run mp config oauth2-login --account <name> to re-authenticate. Access tokens expire after roughly one hour and are auto-refreshed, but refresh tokens expire after approximately 90 days of inactivity.

AUTHENTICATE failed with OAuth2

This can mean IMAP is disabled for the account, the token has wrong scopes, or admin consent is missing. Check the app permissions in Azure and ensure IMAP is enabled. If your tenant blocks IMAP, consider switching to auth_method = "graph".

Sync and Workflow

How does sync work?

mp sync is additive by default: it only downloads new emails, deduplicating by Message-ID. It uses a two-pass approach: first fetches headers (~50 bytes per message) to check what is new, then downloads full messages only for emails not already stored locally.

What happens to messages deleted on the server?

The same sync pass prunes them. Every sync enumerates the whole mailbox even when it downloads only the newest messages, so a message the server no longer lists is removed from the local store whatever its age, and a mailbox cleaned up in another client converges on the next sync.

One pass holds removals back: the one that could not bring in everything that landed since the previous sync, because a message missing from the store is not evidence that it is gone from the server. The status line says so when it happens (N removal(s) held back), and the next complete pass applies them. A first sync is such a pass by definition, since nothing local is known yet: that covers a brand-new account, and equally the first sync of an account you have had for months after an upgrade rebuilds its local store.

A message that was moved converges as soon as the sync has read the copy in its destination. On servers that issue a new identifier on a move (Exchange, Dovecot) that is the same pass. On Gmail, archiving only removes the Inbox label and the copy in All Mail keeps its original, old identifier, so a capped sync will not see it again: the inbox row goes and the archived copy is re-filed by the next full sync (sS in the TUI).

Can I use mailypoppins with multiple accounts?

Yes. Use the [[accounts]] array in config.toml. Each account has its own SMTP/IMAP settings, local store, and mailboxes. Switch between accounts in the TUI with ga.

How are attachments handled?

Incoming attachments are stored as blobs beside the message row, not as files you browse. Use to in the TUI to open one (it is written to a private temp directory first), ts to save it to a directory you choose, or mp save from the CLI. For outgoing drafts, list file paths in the attachments: frontmatter field, or point one entry at a folder to attach every file inside it; forwarding a message materialises its attachments under the account's attachments/ directory and lists those paths for you.

Can I take a send back?

A send from the TUI is parked for email.send_hold_secs (default 20 seconds) before it reaches the transport. The status line shows the countdown and u cancels it: nothing is transmitted and the approved draft stays in place to edit or send again. Set send_hold_secs = 0 under [email] to hand off immediately. The hold covers both SMTP and Graph accounts; mp send and the batch send-approved run are never held.

Does it support HTML emails?

Outgoing emails are written in Markdown and converted to HTML for sending, with a plaintext fallback. For incoming mail both parts are kept as blobs beside the message row. In the preview pane an HTML message is shown as the plain text extracted from it when the message was stored, word-wrapped like any other body, so its links, emphasis, tables and lists come through as flat text. Press tb in the TUI to open the original HTML in your browser when you want the sender's layout. On a draft, tb opens the HTML it will be sent as, signature and quoted reply included.