Comprehensive guide for accessing and automating Apple Mail using AppleScript, including reading emails, sending messages, and managing mailboxes.
This skill provides comprehensive guidance on using AppleScript to automate Apple Mail operations, including reading emails, sending messages, and managing mailboxes.
inbox directly on accountsAlways start by listing accounts to verify the account exists and get the correct name:
osascript <<'EOF'
tell application "Mail"
set accountNames to {}
repeat with acc in accounts
set end of accountNames to (name of acc)
end repeat
return accountNames as string
end tell
EOF
Example output:
The correct pattern for accessing emails from a specific account:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set inboxMsgs to messages of mbox
set msgCount to count of inboxMsgs
set numToFetch to 5
if msgCount < 5 then set numToFetch to msgCount
set output to ""
repeat with i from 1 to numToFetch
set msg to item i of inboxMsgs
set output to output & "Email #" & i & return
set output to output & "Subject: " & subject of msg & return
set output to output & "From: " & sender of msg & return
set output to output & "Date: " & (date received of msg as string) & return
set output to output & return & "---" & return & return
end repeat
return output
end if
end repeat
return "Inbox not found"
end tell
EOF
Key lessons from trial and error:
inbox of targetAccount (doesn't work, causes error -1728)address of acc (address property doesn't exist reliably)every mailbox of targetAccount then iterate to find "INBOX" or "Inbox"Get the full body content of a specific email:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set msg to item 1 of messages of mbox
set emailContent to content of msg
return emailContent
end if
end repeat
end tell
EOF
Create and send a new email:
osascript <<'EOF'
tell application "Mail"
set newMessage to make new outgoing message with properties {subject:"Your Subject", content:"Your email body here", visible:true}
tell newMessage
make new to recipient at end of to recipients with properties {address:"[email protected]"}
send
end tell
end tell
EOF
Options:
visible:true - Shows the compose window before sending (useful for review)visible:false - Sends silently in the backgroundReply to an existing email (preserves threading):
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
-- Find the message to reply to (e.g., by sender)
set foundMessages to (messages of mbox whose sender contains "[email protected]")
set originalMsg to item 1 of foundMessages
-- Create reply
set theReply to reply originalMsg
tell theReply
set content to "Your reply message here"
send
end tell
return "Reply sent successfully"
end if
end repeat
end tell
EOF
Key points:
reply originalMsg to create a threaded reply (NOT make new outgoing message)reply command automatically sets the recipient and subject with "Re:"with opening window false parameter, it causes syntax errorsReply vs New Email:
set newMessage to make new outgoing message with properties {subject:"Re: Subject"}
set theReply to reply originalMsg
Find emails matching a subject keyword:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set foundMessages to (messages of mbox whose subject contains "keyword")
set output to ""
repeat with msg in foundMessages
set output to output & "Subject: " & subject of msg & return
set output to output & "From: " & sender of msg & return
set output to output & "Date: " & (date received of msg as string) & return
set output to output & "---" & return
end repeat
return output
end if
end repeat
end tell
EOF
Find all emails from a specific sender:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set foundMessages to (messages of mbox whose sender contains "[email protected]")
set output to "Found " & (count of foundMessages) & " messages" & return & return
repeat with msg in foundMessages
set output to output & subject of msg & return
end repeat
return output
end if
end repeat
end tell
EOF
Get subject, sender, and date without loading full content (faster):
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set recentMsgs to items 1 thru 10 of messages of mbox
set output to ""
repeat with msg in recentMsgs
set output to output & subject of msg & " | " & sender of msg & return
end repeat
return output
end if
end repeat
end tell
EOF
Count unread messages in inbox:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set unreadCount to count of (messages of mbox whose read status is false)
return "Unread messages: " & unreadCount
end if
end repeat
end tell
EOF
Move a message to a different mailbox:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
-- Find the source mailbox
repeat with mbox in allMailboxes
if name of mbox is "INBOX" then
set theMessage to item 1 of messages of mbox
-- Find the destination mailbox
repeat with destBox in allMailboxes
if name of destBox is "Archive" then
move theMessage to destBox
return "Message moved to Archive"
end if
end repeat
end if
end repeat
end tell
EOF
Delete emails matching specific criteria:
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" then
set messagesToDelete to (messages of mbox whose subject contains "spam")
repeat with msg in messagesToDelete
delete msg
end repeat
return "Deleted " & (count of messagesToDelete) & " messages"
end if
end repeat
end tell
EOF
Different email providers use different mailbox names:
INBOX - Main inbox[Gmail]/All Mail - All mail archive[Gmail]/Sent Mail - Sent messages[Gmail]/Trash - Deleted items[Gmail]/Drafts - Draft messages[Gmail]/Spam - Spam folderInbox - Main inbox (note the capitalisation)Sent Messages - Sent mailTrash - Deleted itemsDrafts - Draft messagesSent Items, Deleted Items, etc.Common properties you can access on a message object:
subject of msg -- Email subject line
sender of msg -- Sender email address
date received of msg -- Date the email was received
date sent of msg -- Date the email was sent
content of msg -- Full email body (can be slow)
read status of msg -- Boolean: true if read, false if unread
flagged status of msg -- Boolean: true if flagged/starred
message id of msg -- Unique message identifier
to recipients of msg -- List of recipient objects
cc recipients of msg -- List of CC recipient objects
all headers of msg -- Raw email headers
message size of msg -- Size in bytes
Error: "Can't get inbox of account"
-- ❌ WRONG: Trying to access inbox directly
tell application "Mail"
set targetAccount to account "[email protected]"
set inboxMsgs to messages of inbox of targetAccount -- This fails!
end tell
-- ✅ CORRECT: Iterate through mailboxes
tell application "Mail"
set targetAccount to account "[email protected]"
set allMailboxes to every mailbox of targetAccount
repeat with mbox in allMailboxes
if name of mbox is "INBOX" or name of mbox is "Inbox" then
set inboxMsgs to messages of mbox
end if
end repeat
end tell
Error: "Can't get address of account"
-- ❌ WRONG: address property is unreliable
if address of acc contains "[email protected]" then
-- ✅ CORRECT: Use name property instead
if name of acc contains "[email protected]" then
Error: "Can't get item 1 of every account"
-- ❌ WRONG: Don't try to access properties in bulk operations
repeat with acc in accounts
if address of acc contains "..." then -- This fails!
-- ✅ CORRECT: Get properties individually
repeat with acc in accounts
set accName to name of acc
if accName contains "..." then
Error: Message not found or mailbox doesn't exist
-- Solution: Always check existence first
set allMailboxes to every mailbox of targetAccount
if (count of allMailboxes) = 0 then
return "No mailboxes found for this account"
end if
Always list accounts first when debugging:
osascript -e 'tell application "Mail" to name of every account'
Check mailbox names before accessing:
set mailboxNames to name of every mailbox of targetAccount
Use heredoc syntax for multi-line AppleScript in bash:
osascript <<'EOF'
tell application "Mail"
-- Your script here
end tell
EOF
Handle case sensitivity in mailbox names:
if name of mbox is "INBOX" or name of mbox is "Inbox" then
Limit message retrieval to avoid slowdowns:
set recentMsgs to items 1 thru 10 of messages of mbox
Check message count before iterating:
set msgCount to count of messages of mbox
if msgCount < numToFetch then set numToFetch to msgCount
Use return early when you find what you need:
repeat with mbox in allMailboxes
if name of mbox is "INBOX" then
-- Process and return
return result
end if
end repeat
content of msg is slowwhose clauses) to narrow results before processing# List all accounts
osascript -e 'tell application "Mail" to name of every account'
# Get inbox message count for an account
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
repeat with mbox in mailboxes of targetAccount
if name of mbox is "INBOX" or name of mbox is "Inbox" then
return count of messages of mbox
end if
end repeat
end tell
EOF
# Get unread count
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
repeat with mbox in mailboxes of targetAccount
if name of mbox is "INBOX" then
return count of (messages of mbox whose read status is false)
end if
end repeat
end tell
EOF
# List all mailbox names for an account
osascript <<'EOF'
tell application "Mail"
set targetAccount to account "[email protected]"
set mailboxNames to name of every mailbox of targetAccount
return mailboxNames as string
end tell
EOF
Invoke this skill when you need to:
When working with email automation:
The key insights from developing this skill:
inbox of account directly - Always iterate through mailboxesname not addressevery X can fail - Get properties individuallyitems 1 thru N for safe list slicing with bounds checkingreply command, not new messages - To maintain threading, use reply originalMsg instead of creating a new outgoing messagewith opening window parameter - The reply command doesn't support the with opening window false parameter, causes syntax errorsRemember: AppleScript error messages are often cryptic. When debugging, simplify the script to the minimum necessary and build up complexity gradually.