Email Bot
Deploy AI agents to handle email via IMAP/SMTP or the zero-config AgentMail API with event-driven modes
Email bots connect your AI agents to email — choose AgentMail (zero-config API) or IMAP/SMTP (self-hosted).
import os
import asyncio
from praisonai.bots import AgentMailBot
from praisonaiagents import Agent
agent = Agent(name="assistant", instructions="Handle emails concisely")
bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent)
asyncio.run(bot.start())The user sends mail to the inbox; the agent reads threads, drafts replies, and sends responses via AgentMail or IMAP/SMTP.
How It Works
Quick Start
AgentMail (Recommended)
Zero-config email — no IMAP/SMTP setup. Get an email address instantly.
- Set API Key
bash export AGENTMAIL_API_KEY="am_..." - Start Bot
python import os import asyncio from praisonai.bots import AgentMailBot from praisonaiagents import Agent agent = Agent(name="assistant", instructions="Handle emails concisely") bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent) asyncio.run(bot.start())
IMAP/SMTP (Self-Hosted)
Use your own Gmail, Outlook, or corporate email server.
- Set Environment Variables
bash export EMAIL_ADDRESS="your-bot@gmail.com" export EMAIL_APP_PASSWORD="xxxx xxxx xxxx xxxx" # Optional: defaults to Gmail export EMAIL_IMAP_SERVER="imap.gmail.com" export EMAIL_SMTP_SERVER="smtp.gmail.com" - Start Bot
python import asyncio from praisonai.bots import Bot from praisonaiagents import Agent agent = Agent(name="assistant", instructions="Handle emails professionally") bot = Bot("email", agent=agent) asyncio.run(bot.start())
Platform Comparison
| Feature | EmailBot (IMAP/SMTP) | AgentMailBot (API) |
|---|---|---|
| Setup | Email credentials + IMAP/SMTP config | API key only |
| Email Address | Your existing address | Auto-generated (@agentmail.to) |
| Custom Domains | ✅ Any provider | ✅ Via create_inbox(domain=...) |
| Inbox Lifecycle | ❌ Manual | ✅ create_inbox() / delete_inbox() |
| Event-Driven Modes | ❌ Poll only | ✅ poll, ws, webhook, hybrid |
| Fastest Latency | 10-30s (poll interval) | ~250ms (WebSocket push) |
| Dependencies | None (stdlib) | pip install agentmail |
| Best For | Corporate/existing email | New projects, multi-inbox, real-time |
Event-Driven Modes (AgentMail)
AgentMailBot supports 4 reception modes. Choose based on your latency and reliability needs.
| Mode | Trigger | Latency | Use Case |
|---|---|---|---|
poll | Timer (default) | 10-30s | Backwards compat, simple |
ws | AgentMail WebSocket push | ~250ms | Dev, single-bot, self-hosted |
webhook | AgentMail HTTP POST | ~350ms | Production, horizontal scale |
hybrid | WS primary + 60s poll fallback | ~250ms | Recommended — fast + reliable |
YAML Configuration
```yaml
# Poll (default — omit mode)
channels:
agentmail:
token: ${AGENTMAIL_API_KEY}
```
<!-- slide -->
```yaml
# WebSocket (simplest event-driven)
channels:
agentmail:
token: ${AGENTMAIL_API_KEY}
mode: ws
```
<!-- slide -->
```yaml
# Webhook (production, scalable)
channels:
agentmail:
token: ${AGENTMAIL_API_KEY}
mode: webhook
webhook_url: https://my-server.com
webhook_port: 8080 # AgentMail always binds its own server (historical 8080 default)
```
<!-- slide -->
```yaml
# Hybrid (recommended)
channels:
agentmail:
token: ${AGENTMAIL_API_KEY}
mode: hybrid
```Python API
```python
import os
# Poll (default)
bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent)
await bot.start()
```
<!-- slide -->
```python
# WebSocket — sub-second
bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent, mode="ws")
await bot.start()
```
<!-- slide -->
```python
# Hybrid — recommended
bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent, mode="hybrid")
await bot.start()
```
<!-- slide -->
```python
# Webhook — production
bot = AgentMailBot(
token=os.getenv("AGENTMAIL_API_KEY"), agent=agent,
mode="webhook", webhook_port=9090
)
await bot.start()
```Webhook Verification
When using mode: webhook, set AGENTMAIL_WEBHOOK_SECRET in production. An unset secret causes all inbound webhooks to be rejected with HTTP 401 (fail-closed).
Accepted signature headers: X-AgentMail-Signature, X-Webhook-Signature, X-Signature.
For local development without a real AgentMail webhook, set PRAISONAI_INSECURE_WEBHOOKS=true — never in production. See Webhook Verification.
Duplicate protection on retries
AgentMailBot derives one stable AgentMail Idempotency-Key per outbound send and reuses it across every retry (the outbound resilience layer retries up to 3× by default). If a send succeeds on AgentMail but the client times out, the retry is deduplicated at the provider — the recipient gets exactly one email.
import os
import asyncio
from praisonai.bots import AgentMailBot
from praisonaiagents import Agent
agent = Agent(name="assistant", instructions="Reply concisely")
bot = AgentMailBot(token=os.getenv("AGENTMAIL_API_KEY"), agent=agent)
# Default: a UUID is auto-generated per logical send
asyncio.run(bot.send_message(
channel_id="user@example.com",
content={"subject": "Hi", "body": "hello"},
))Pass your own key when you need deterministic replay across process restarts — for example a key derived from a durable job or webhook id:
# Advanced: reuse a stable key so a re-send is dedup'd by AgentMail
await bot.send_message(
channel_id="user@example.com",
content={"subject": "Order confirmation", "body": "..."},
idempotency_key=f"order-{order_id}", # 1–256 chars, honoured by AgentMail for 24h
)Inbox Lifecycle Management
AgentMailBot implements the EmailProtocol from the core SDK, enabling programmatic inbox creation:
# Create a new inbox
inbox = await bot.create_inbox(domain="mycompany.com")
print(inbox["email_address"]) # support-abc@mycompany.com
# List all inboxes
inboxes = await bot.list_inboxes()
# Delete an inbox
await bot.delete_inbox(inbox["id"])Using as Agent Tools
Don’t need a full bot? Use email as one-shot agent tools — auto-detects backend from env vars:
from praisonaiagents import Agent
from praisonaiagents.tools import (
send_email, list_emails, read_email, reply_email,
search_emails, archive_email, draft_email, resolve_profiles,
)
agent = Agent(
name="Mailer",
instructions="Send, read, and manage emails",
tools=[send_email, list_emails, read_email, reply_email, search_emails, archive_email, draft_email],
)
agent.start("Search my inbox for emails from Bob and archive the old ones")AgentMail Backend
export AGENTMAIL_API_KEY="am_..."
export AGENTMAIL_INBOX_ID="you@agentmail.to"
# Same code, uses AgentMail APIGmail / Outlook Backend
export EMAIL_ADDRESS="you@gmail.com"
export EMAIL_PASSWORD="xxxx xxxx xxxx xxxx"
# Same code, uses SMTP/IMAPShared Features
Both EmailBot and AgentMailBot share these capabilities:
- Auto-Reply Prevention: Detects
Auto-Submittedheaders and common bot addresses to prevent infinite loops. - Blocked Sender Filtering: Configurable via
BotConfig.blocked_users. - Email Address Extraction: Parses
"Name <email>"format consistently. - Session Isolation: Per-sender
AgentStatefor independent conversations. - Command Handling: Subject-based commands (e.g.,
START:,STOP:). - Thread Correlation:
In-Reply-ToandReferencesheaders maintained.
Capabilities Matrix
| Capability | EmailBot (IMAP) | AgentMailBot |
|---|---|---|
| Receive email (poll) | ✅ | ✅ |
| Receive email (WebSocket) | ❌ | ✅ |
| Receive email (webhook) | ❌ | ✅ |
| Receive email (hybrid) | ❌ | ✅ |
| Send / reply email | ✅ | ✅ |
| Sub-second latency | ❌ | ✅ (~250ms) |
| Auto-reconnect | ❌ | ✅ (1s→60s backoff) |
| No missed messages | ❌ (poll gaps) | ✅ (hybrid mode) |
| Webhook server + health check | ❌ | ✅ (GET /health) |
YAML mode: config | ❌ | ✅ |
| Graceful shutdown (all modes) | ⚠️ Poll only | ✅ All modes |
| Async client | ❌ | ✅ (lazy AsyncAgentMail) |
Configuration
Environment Variables
| Variable | Platform | Default | Description |
|---|---|---|---|
AGENTMAIL_API_KEY | AgentMail | — | AgentMail API key |
AGENTMAIL_INBOX_ID | AgentMail | — | Existing inbox to connect to |
AGENTMAIL_DOMAIN | AgentMail | — | Custom domain for new inboxes |
EMAIL_ADDRESS | IMAP/SMTP | — | Bot email address |
EMAIL_APP_PASSWORD | IMAP/SMTP | — | App Password (recommended) |
EMAIL_IMAP_SERVER | IMAP/SMTP | imap.gmail.com | IMAP server |
EMAIL_SMTP_SERVER | IMAP/SMTP | smtp.gmail.com | SMTP server |
EMAIL_POLL_INTERVAL | Both | 60 / 10 | Seconds between checks |
BotConfig Options
| Option | Type | Default | Description |
|---|---|---|---|
mode | str | "poll" | Reception mode: poll, ws, webhook, hybrid |
webhook_url | str | None | Public URL for webhook registration |
webhook_path | str | "/webhook" | Webhook endpoint path |
webhook_port | Optional[int] | None → 8080 | Standalone webhook server port. AgentMail always binds its own server (falls back to the historical 8080) and does not use Shared Webhook Ingress |
polling_interval | float | 1.0 | Seconds between polls |
send_message Options (AgentMail)
| Option | Type | Default | Description | |
|---|---|---|---|---|
channel_id | str | — | Recipient email address | |
content | `str \ | dict` | — | Body string, or {"subject": ..., "body": ...} |
reply_to | `str \ | None` | None | Message-ID to reply to |
thread_id | `str \ | None` | None | Thread ID for conversation tracking |
idempotency_key | `str \ | None` | auto-generated UUID | Stable key forwarded as AgentMail’s Idempotency-Key header. The same value is reused across retries of one logical send, so a timeout-after-send never duplicates the email. 1–256 chars; 24h key lifetime per AgentMail. |
Best Practices
Use Hybrid Mode for Production
mode: hybrid gives sub-second latency via WebSocket with a 60-second poll fallback to catch any missed messages. Best of both worlds.
Use Dedicated App Passwords
Never use your main account password. Generate a platform-specific App Password for the bot.
Configure Blocked Senders
Use BotConfig.blocked_users to exclude noreply addresses or known marketing domains.
Use AgentMail for Multi-Tenant
Need separate inboxes per customer? Use AgentMailBot.create_inbox() to provision on demand.