Imap Mail
Personal email via your own IMAP/SMTP server. Send and receive emails, manage folders, and search messages using standard protocols — no third-party email pl...
Description
name: imap-mail description: Personal email via your own IMAP/SMTP server. Send and receive emails, manage folders, and search messages using standard protocols — no third-party email platform required. Use when you need to check inbox, send emails, move messages between folders, search by sender/subject/date, list all mailboxes, schedule emails for future delivery, save attachments, or get push notifications for new mail (IMAP IDLE). metadata: {"openclaw":{"requires":{"bins":["python3"]},"install":{"uv":{"packages":["fastapi","uvicorn"]}}}}
IMAP Mail Skill
Send and receive email through your own IMAP/SMTP server.
A lightweight local REST API (FastAPI) runs as a bridge between the agent and your mail server — no third-party email platform needed.
Setup (first time)
1. Install dependencies
pip3 install fastapi uvicorn
2. Configure credentials
Create /etc/imap-mail.env (or any path, then set IMAP_MAIL_ENV):
MAIL_IMAP_HOST=mail.example.com
MAIL_IMAP_PORT=993
MAIL_SMTP_HOST=mail.example.com
MAIL_SMTP_PORT=465
MAIL_USER=agent@example.com
MAIL_PASS=yourpassword
MAIL_FROM_NAME=MyAgent
3. Start the API server
# One-time / foreground
python3 {baseDir}/scripts/mail-api.py
# Or as a systemd service (recommended)
# See: {baseDir}/references/systemd.md
The API listens on http://127.0.0.1:8025 by default.
Checking Email
# List recent messages
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com
# Unread only
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --unseen
# Specific folder
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --folder Sent
# Read a specific message (use UID from list output)
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --message 42
# Read message and save all its attachments
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --message 42 --save-attachments /tmp/mail/
# List threads
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --threads
# List all folders
python3 {baseDir}/scripts/check_inbox.py --inbox agent@example.com --folders
Searching Email
# Search by keyword (subject + body)
python3 {baseDir}/scripts/search.py --inbox agent@example.com --q "invoice"
# Search by sender
python3 {baseDir}/scripts/search.py --inbox agent@example.com --from "alice@example.com"
# Search by subject + date range
python3 {baseDir}/scripts/search.py --inbox agent@example.com --subject "meeting" --since 2026-01-01
# Find unread messages
python3 {baseDir}/scripts/search.py --inbox agent@example.com --unseen
# Find messages with attachments and save them
python3 {baseDir}/scripts/search.py --inbox agent@example.com --has-attachments --save-attachments /tmp/mail/
# Find messages from VIP senders only
python3 {baseDir}/scripts/search.py --inbox agent@example.com --vip
# Find only flagged/starred messages
python3 {baseDir}/scripts/search.py --inbox agent@example.com --flagged
# Find flagged messages from a specific sender
python3 {baseDir}/scripts/search.py --inbox agent@example.com --from "craig@example.com" --flagged
# Combined filters: unread messages from a specific sender since a date
python3 {baseDir}/scripts/search.py --inbox agent@example.com --from "alice@example.com" --since 2026-03-01 --unseen
# Unread messages with a specific subject keyword
python3 {baseDir}/scripts/search.py --inbox agent@example.com --subject "invoice" --unseen
# Search in a specific folder
python3 {baseDir}/scripts/search.py --inbox agent@example.com --q "report" --folder Archive
Sorting Inbox (Personal vs Forwarded)
Automatically move messages into sub-folders based on addressing:
- Personal — inbox address is directly in
To:orCc: - Forwarded — subject starts with
Fwd:/FW:, orX-Forwarded-*/Resent-*headers present - Forwarded takes priority over Personal when both apply
# Preview what would be moved (no changes)
python3 {baseDir}/scripts/sort_inbox.py --inbox agent@example.com --dry-run
# Sort all messages
python3 {baseDir}/scripts/sort_inbox.py --inbox agent@example.com
# Sort only unread messages
python3 {baseDir}/scripts/sort_inbox.py --inbox agent@example.com --unseen
# Custom folder names
python3 {baseDir}/scripts/sort_inbox.py --inbox agent@example.com \
--personal-folder MyInbox --forwarded-folder Forwards
# Sort a specific source folder
python3 {baseDir}/scripts/sort_inbox.py --inbox agent@example.com --folder AllMail
Messages that match neither category (e.g. BCC, bulk mail) stay in the source folder.
Folder Management
# List all folders
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --list
# Create a folder
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --create Archive
# Delete a folder
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --delete OldFolder
# Move a message to another folder (use UID from check_inbox output)
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --move 42 --to Archive
# Move from a specific source folder
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --move 5 --to INBOX --from-folder Junk
# Delete a message
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --delete-msg 42
# Mark all messages in INBOX as read
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --mark-seen
# Mark all messages in a specific folder as read
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --mark-seen --from-folder Sent
# Mark one specific message as read (use UID from check_inbox output)
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --mark-seen-uid 42
# Mark several specific messages as read (space-separated UIDs)
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --mark-seen-uid 42 55 73
# Flag (star) specific messages as important
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --flag-uid 42 55 73
# Flag ALL messages from a specific sender
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --flag-from "craig@example.com"
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --flag-from "igor@example.com"
# Remove flag from messages
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --unflag-uid 42
python3 {baseDir}/scripts/manage_folders.py --inbox agent@example.com --unflag-from "craig@example.com"
Sending Email
# Send a plain text email
python3 {baseDir}/scripts/send_email.py \
--to recipient@example.com \
--subject "Hello" \
--text "Message body here"
# Send to multiple recipients
python3 {baseDir}/scripts/send_email.py \
--to alice@example.com \
--to bob@example.com \
--subject "Hello everyone" \
--text "Hi all!"
# Reply to a message (preserves thread)
python3 {baseDir}/scripts/send_email.py \
--to sender@example.com \
--subject "Re: Original Subject" \
--text "My reply" \
--reply-to "<original-message-id>"
Contact Memory (CRM)
Persistent notes and history per contact. Flagged senders are automatically tracked — when their message arrives, the agent pulls full context and reports in detail.
# Look up everything about a contact
python3 {baseDir}/scripts/manage_contacts.py --get craig@example.com
# List all contacts
python3 {baseDir}/scripts/manage_contacts.py --list
# Save a contact with name and tags
python3 {baseDir}/scripts/manage_contacts.py --save craig@example.com --name "Craig Smith" --tags "client,priority"
# Append a timestamped note (agent does this automatically after each flagged message)
python3 {baseDir}/scripts/manage_contacts.py --note craig@example.com "Wants proposal by Friday. Budget ~$5k."
# Delete a contact
python3 {baseDir}/scripts/manage_contacts.py --delete craig@example.com
How it works with flagged mail:
- Flagged (starred) UNSEEN messages are processed separately from regular mail
- For each flagged message, the agent fetches the sender's full contact history
- Reports to user with: who wrote, what they want, full context from previous interactions
- Automatically saves a note summarizing the new message
Auto-Rules (persistent flag/action on incoming mail)
Rules are defined once and applied automatically to every new (UNSEEN) message on each mail check — until you remove them.
# Flag ALL future messages from Craig as important (persists forever)
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --add --from "craig@example.com"
# Flag messages with "URGENT" in subject
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --add --subject "URGENT"
# Auto-mark-as-read messages from a newsletter
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --add --from "news@newsletter.com" --action mark-seen
# List all active rules
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --list
# Apply all rules right now (without waiting for next cron)
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --apply
# Temporarily disable a rule (ID from --list)
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --disable 1
# Re-enable
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --enable 1
# Remove permanently
python3 {baseDir}/scripts/manage_rules.py --inbox agent@example.com --remove 1
Scheduled Send
Queue emails for future delivery. The API background scheduler checks every 60 seconds.
# Schedule via API directly (ISO datetime, UTC recommended)
# POST /inboxes/{inbox}/scheduled
# Body: {"to": ["user@example.com"], "subject": "...", "text": "...", "send_at": "2026-03-10T09:00:00Z"}
# List all scheduled messages
# GET /inboxes/{inbox}/scheduled
# Cancel a scheduled message
# DELETE /inboxes/{inbox}/scheduled/{id}
IMAP IDLE (Push Notifications)
Instead of polling every N minutes, IDLE keeps a persistent connection open so the server pushes notifications immediately when new mail arrives.
Add to your env file:
# Required: webhook URL to POST new mail events to
MAIL_IDLE_WEBHOOK=http://127.0.0.1:8080/mail-event
# Optional: folder to watch (default: INBOX)
MAIL_IDLE_FOLDER=INBOX
When new mail arrives, the API POSTs to the webhook:
{
"event": "new_mail",
"uid": "123",
"subject": "Hello",
"from_": [{"name": "Alice", "email": "alice@example.com"}],
"vip": false,
...full message fields...
}
Check IDLE status:
# GET http://127.0.0.1:8025/idle/status
VIP Sender List
Mark specific senders as VIP — their messages will have "vip": true in the API response and in IDLE webhook payloads, enabling urgent/priority handling.
Add to your env file:
MAIL_VIP_SENDERS=boss@company.com,important@client.com
Messages from VIP senders are flagged in all responses ("vip": true) and can be filtered:
# Show only VIP messages
python3 {baseDir}/scripts/search.py --inbox agent@example.com --vip
API Endpoints
The local REST API at http://127.0.0.1:8025 exposes:
| Method | Path | Description |
|---|---|---|
| GET | /health |
Check API status (includes IDLE state, VIP list) |
| GET | /idle/status |
IMAP IDLE watcher status |
| GET | /inboxes/{inbox}/folders |
List folders |
| POST | /inboxes/{inbox}/folders |
Create folder |
| DELETE | /inboxes/{inbox}/folders/{name} |
Delete folder |
| GET | /inboxes/{inbox}/messages |
List messages (?folder=INBOX&limit=N&unseen=true) |
| GET | /inboxes/{inbox}/messages/{uid} |
Get full message (?folder=INBOX) |
| GET | /inboxes/{inbox}/messages/{uid}/attachments/{index} |
Download attachment (base64) |
| POST | /inboxes/{inbox}/messages |
Send email |
| POST | /inboxes/{inbox}/messages/{uid}/move |
Move message (?folder=src) |
| DELETE | /inboxes/{inbox}/messages/{uid} |
Delete message |
| POST | /inboxes/{inbox}/sort |
Sort into Personal/Forwarded folders (?dry_run=true) |
| GET | /inboxes/{inbox}/search |
Search (?q=&from=&subject=&since=&vip_only=true) |
| GET | /inboxes/{inbox}/threads |
List threads |
| POST | /inboxes/{inbox}/scheduled |
Schedule email for future delivery |
| GET | /inboxes/{inbox}/scheduled |
List scheduled messages |
| DELETE | /inboxes/{inbox}/scheduled/{id} |
Cancel scheduled message |
Environment Variables
| Variable | Default | Description |
|---|---|---|
MAIL_IMAP_HOST |
— | IMAP server hostname |
MAIL_IMAP_PORT |
993 |
IMAP port (TLS) |
MAIL_SMTP_HOST |
— | SMTP server hostname |
MAIL_SMTP_PORT |
465 |
SMTP port (TLS) |
MAIL_USER |
— | Email login / address |
MAIL_PASS |
— | Password |
MAIL_FROM_NAME |
Agent |
Display name in From header |
MAIL_IDLE_WEBHOOK |
— | Webhook URL for IMAP IDLE push events |
MAIL_IDLE_FOLDER |
INBOX |
Folder to watch with IDLE |
MAIL_VIP_SENDERS |
— | Comma-separated VIP email addresses |
MAIL_SCHEDULED_DB |
/tmp/imap-mail-scheduled.db |
SQLite path for scheduled sends |
MAIL_ALLOW_SELF_SIGNED |
false |
Set true to skip TLS cert verification (self-signed certs) |
IMAP_MAIL_API |
http://127.0.0.1:8025 |
API base URL (for scripts) |
IMAP_MAIL_ENV |
/etc/imap-mail.env |
Path to env file |
IMAP_MAIL_PORT |
8025 |
API listen port |
Security Notes
- Credentials — mail login and password are stored in a local env file (e.g.
/etc/imap-mail.env), readable only by the service user. Never hardcode credentials in scripts. - SSL/TLS — all IMAP and SMTP connections use TLS by default. Certificate verification is enabled by default (
ssl.create_default_context()). SetMAIL_ALLOW_SELF_SIGNED=trueonly if your mail server uses a self-signed certificate and you accept the risk. - Local API only — the REST bridge listens on
127.0.0.1(loopback) only and is not exposed to the network. - Webhook (IDLE) — if
MAIL_IDLE_WEBHOOKis set, full message contents (including body) are POSTed to that URL on new mail. Ensure the webhook URL is a trusted local endpoint.
Compatibility
Works with any standard IMAP/SMTP server:
- Self-hosted: Dovecot, Postfix, Exim, Maddy
- Hosted: Gmail (App Password), Outlook/Hotmail, Yahoo Mail, Fastmail, ProtonMail Bridge, and any provider that supports IMAP
Note: Self-signed TLS certificates are accepted automatically.
References
Reviews (0)
No reviews yet. Be the first to review!
Comments (0)
No comments yet. Be the first to share your thoughts!