macOS Mail Integration
This guide explains how EmailOS integrates with Apple Mail on macOS for Message-ID links, message deeplinks, forwarding, and attachment export.
What works
- Read by Message-ID from Apple Mail-style links (
message://...). - Convert/canonicalize Message-ID links from Apple Mail into lookup-safe IDs.
- Generate and open Apple Mail deep links with:
mailos read --message-id "<...>" --message-linkmailos read --message-id "<...>" --open-message-link
- Forward a known Apple Mail message with:
mailos send --forward-message-link "message://..."
- Save attachments directly while reading:
mailos read --message-id "message://..." --save-attachments /Users/<you>/output/attachments/<case> --markdown
Message-ID vs message link
A Message-ID is the cross-platform email header value, for example <[email protected]>. A macOS Mail message link wraps that ID in a message://... URL so Mail.app can open it, for example message://%3cabc%40example.com%3e.
Use --forward-message-id when you have the raw Message-ID. Use --forward-message-link when you copied a macOS Mail deeplink from Mail.app, Raycast, or the repo AppleScript helpers.
Message-ID lookup behavior
EmailOS uses two Message-ID modes in read:
--message-id <id>: auto sync+retry on miss, then Apple Mail fallback on macOS.- Positional Message-ID (
mailos read "<id>"): no auto sync by default.- Use
--syncto force sync+retry for positional IDs.
- Use
Examples:
# Apple Mail URL input
mailos read --message-id "message://%3cabc%40example.com%3e" --markdown
# Positional Message-ID with explicit sync
mailos read "<[email protected]>" --sync --markdownForwarding from a macOS Mail link
mailos send \
--to [email protected] \
--subject "FYI" \
--body "Adding this for context." \
--forward-message-link "message://%3cabc%40example.com%3e" \
--dry-run--forward-message-link and --forward-message-id use the same lookup path after normalization. If both are provided and point to different messages, mailos send exits with an error instead of guessing.
Attachment export on macOS
--save-attachments now supports a macOS fallback path via Mail.app when attachment bytes are not present in the primary read payload.
That means the command can still export files from Mail.app by Message-ID and append paths to markdown output:
mailos read --message-id "message://%3cabc%40example.com%3e" \
--save-attachments "/Users/<you>/output/attachments/case-123" \
--markdownExpected footer in output:
---
Attachments saved to:
- /Users/<you>/output/attachments/case-123/file.pdf
- /Users/<you>/output/attachments/case-123/image001.pngClipboard workflow (Raycast / script)
If you use a helper script like copy-mail-id.sh, keep script logic thin and delegate to mailos:
- Copy an encoded Mail deeplink with
copy-mail-id.sh; setCOPY_MAIL_ID_FORMAT=message-idonly when you need the raw Message-ID. copy-mail-content.shcopies both aDEEPLINKblock andCONTENTblock by default; setCOPY_MAIL_CONTENT_INCLUDE_DEEPLINK=0for content only.- Resolve/read via
mailos read --message-id ... --markdown - Include
--save-attachments <dir>so output contains paths - For clipboard pipelines, add
--suppress-attachment-errorsto avoid timeout text in copied markdown - Copy command output directly to clipboard
This avoids duplicate AppleScript logic and keeps behavior consistent with CLI updates.
Troubleshooting
- Message not found
- Retry with
--message-idform (auto sync+retry), or runmailos syncthen retry.
- Retry with
- No attachment paths in output
- Ensure
--save-attachmentspoints to a writable directory. - Confirm the message actually has attachments (not only remote/cid references).
- Ensure
- Apple Mail lookup timeouts
- Retry once after
mailos sync. - Keep Mail.app open and account/mailbox data fully loaded.
- Use
--attachment-timeout <seconds>to fail fast (for example--attachment-timeout 15).
- Retry once after
- Wrong binary used by automation
- Pin automation to the intended binary path (for example
/Users/andrewmaguire/go/bin/mailos).
- Pin automation to the intended binary path (for example
