Make your AI agent email you β proactively
An agent that only answers when you ask is an assistant. An agent that emails you first β the morning digest, the overnight-build report, the "something broke" alert β is an operator. This guide wires that up with parts you already have if you followed /ai-agents-setup.
The whole trick is one pipeline:
trigger (schedule or event) β agent writes the email β a send script delivers it
Nothing exotic. A scheduler fires, your agent composes, a 40-line Python script sends. Everything below is copy-paste.
Step 1 β The send script (works with any provider)
examples/send_email.py in this repo is a complete stdlib-only sender β no pip
installs. It reads everything from environment variables:
export AGENT_SMTP_HOST=smtp.gmail.com # or 127.0.0.1 for Proton Mail Bridge
export AGENT_SMTP_PORT=587
export AGENT_SMTP_USER=you@example.com # keyzhub-allow placeholder
export AGENT_SMTP_PASS=your_app_password_here # keyzhub-allow placeholder
export AGENT_EMAIL_TO=you@example.com # keyzhub-allow placeholder
Test it:
echo "The pipeline is alive." | python3 examples/send_email.py --subject "Agent test"
Success looks like: sent: Agent test in the terminal, and the email in your inbox.
Provider notes (the part everyone gets stuck on):
| Provider | Host | The catch |
|---|---|---|
| Gmail | smtp.gmail.com:587 |
You need an App Password (requires 2FA on) β your normal password will NOT work |
| Proton Mail | 127.0.0.1:1025 |
Runs through Proton Mail Bridge on the same machine; Bridge shows you the local SMTP password |
| iCloud | smtp.mail.me.com:587 |
App-specific password from appleid.apple.com |
| Resend / Postmark | HTTPS API | Best deliverability for higher volume; one curl call instead of SMTP |
Step 2 β Let the agent write the body
Any agent CLI that can print to stdout can compose the email. Two real examples:
Claude Code, non-interactive (-p prints and exits):
claude -p "Read progress.md and feature_list.json in this repo and write a 5-line
status email: what moved, what's blocked, what's next. Plain text, no preamble." \
| python3 examples/send_email.py --subject "Daily build report"
Hermes or any other CLI: same shape β prompt in, text out, pipe to the sender.
The prompt is the product here. Tell it exactly what to read, exactly what shape to write, and "no preamble" β you want an email, not a chat reply.
Step 3 β The trigger (this is what makes it proactive)
macOS/Linux, cron β every weekday at 7:00 AM:
crontab -e
# add (one line, with your real paths):
0 7 * * 1-5 cd /path/to/project && /usr/local/bin/claude -p "..." | python3 /path/to/send_email.py --subject "Morning digest"
examples/digest.sh in this repo is that one-liner as a proper script β point
cron at it instead and keep the prompt editable in one place.
macOS, launchd (survives reboots better than cron): examples/com.agent.digest.plist
β edit the paths, then:
cp examples/com.agent.digest.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.agent.digest.plist
Event triggers (beyond schedules): anything that can run a command can send an email β a git post-receive hook (build reports on push), a failing health check, a new row in a database. Same pipeline, different trigger.
OpenClaw users: Clawd's heartbeat already fires on a schedule β its natural proactive channel is Telegram/WhatsApp/Discord, and for email it can simply run this repo's send script like any other shell command.
The rules (read these before you automate)
- Email yourself first. Only yourself. Run the loop to your own inbox for two weeks before any wider audience. You'll be surprised what it sends at 7 AM.
- Never let an agent email other people unreviewed. Draft-then-approve is the pattern: the agent writes, a human clicks send. Fully autonomous outbound to real contacts is how trust (and deliverability) dies.
- Anything commercial is CAN-SPAM territory β real postal address, working unsubscribe, honest subject lines. Digests to yourself: exempt. Marketing to a list: law.
- Keys in the environment, never in the crontab line (crontabs get copied into
dotfile repos).
examples/digest.shsources them from a gitignored env file. - Rate-limit by design. A schedule caps itself; an event trigger needs a
guard (the example script refuses to send more than one email per 5 minutes
unless you pass
--force).
Files in this repo
| File | What it is |
|---|---|
examples/send_email.py |
Stdlib SMTP sender: env-driven, STARTTLS, rate-limit guard |
examples/digest.sh |
The cron target: env file β agent prompt β send |
examples/com.agent.digest.plist |
launchd schedule for macOS (7:00 weekdays) |
examples/agent-email.env.example |
The env file template (copy, fill, gitignore) |
Part of KeyzHub β take the code, build your own. Previous rung: /ai-agents-setup.
Prefer your phone over your inbox? Same pipeline, Telegram last mile: /telegram-bot-setup.