Email Routing in Odoo¶
How Odoo routes incoming emails to the correct records (Purchase Orders, Sales Orders, Helpdesk Tickets, etc.).
Overview¶
Odoo uses a sophisticated email routing system that automatically attaches incoming emails to the correct records. When you send an email from a Purchase Order (PO0001) and the vendor replies, that reply is automatically attached to PO0001's chatter.
How It Works¶
Data Flow¶
┌──────────────────────────────────────────────────────────────────┐
│ EMAIL ROUTING FLOW │
└──────────────────────────────────────────────────────────────────┘
YOU (Odoo) VENDOR
│ │
│ Send RFQ Email from PO0001 │
│ Message-ID: <purchase.order-123-xxx@domain> │
│────────────────────────────────────────────────►│
│ │
│ │
│◄────────────────────────────────────────────────│
│ Vendor Reply │
│ In-Reply-To: <purchase.order-123-xxx@domain> │
│ │
▼
┌──────────────────┐
│ Mail Server │
│ (Gmail/Office) │
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌─────────────────────────────────┐
│ Fetchmail Cron │────►│ message_route() │
│ (every 5 min) │ │ │
└──────────────────┘ │ 1. Parse In-Reply-To header │
│ 2. Find: purchase.order, id=123│
│ 3. Attach to PO0001 chatter │
└─────────────────────────────────┘
Step 1: Sending Email from Odoo¶
When you send an email from a Purchase Order, Odoo automatically adds special headers:
Message-ID: <purchase.order-123-1702500000@yourdomain.com>
References: <purchase.order-123-1702500000@yourdomain.com>
Reply-To: "YourCompany" <catchall@yourdomain.com>
The Message-ID format encodes the record information:
For example:
- purchase.order-123 → Purchase Order with ID 123
- sale.order-456 → Sales Order with ID 456
- helpdesk.ticket-789 → Helpdesk Ticket with ID 789
Step 2: Vendor/Customer Replies¶
When someone replies to the email, their email client automatically includes:
In-Reply-To: <purchase.order-123-1702500000@yourdomain.com>
References: <purchase.order-123-1702500000@yourdomain.com>
These headers reference the original Message-ID.
Step 3: Fetchmail Cron Job¶
The fetchmail module runs a scheduled action that:
- Connects to your incoming mail server (IMAP/POP3)
- Downloads new emails
- Passes each email to
mail.thread.message_process()
Scheduled Action: "Mail: Fetchmail Service"
Model: fetchmail.server
Method: _fetch_mails()
Default Interval: 5 minutes
Step 4: Email Routing Logic¶
The core routing happens in mail.thread.message_route():
def message_route(self, message, message_dict, model=None, thread_id=None):
"""
Route incoming email to the correct record.
Routing priority:
1. Check 'In-Reply-To' header → find original message → get record
2. Check 'References' header → find original message → get record
3. Check email 'To' address → match mail.alias → create/find record
4. Fallback to catchall alias
"""
Routing Priority:
| Priority | Method | Description |
|---|---|---|
| 1 | In-Reply-To | Parse header, find original message, get record |
| 2 | References | Same as above, fallback if In-Reply-To fails |
| 3 | Mail Alias | Match To address to mail.alias records |
| 4 | Catchall | Route to default model or reject |
Configuration¶
1. Incoming Mail Server (Fetchmail)¶
Navigate to: Settings → Technical → Email → Incoming Mail Servers
| Field | Description |
|---|---|
| Name | Descriptive name |
| Server Type | IMAP (recommended) or POP |
| Server Name | mail.yourdomain.com |
| Port | 993 (IMAP SSL) or 995 (POP SSL) |
| SSL/TLS | Should be enabled |
| Username | Email address or username |
| Password | Email password or app password |
| Active | Must be checked |
Test Connection
Always click "Test Connection" after configuring to verify settings.
2. Fetchmail Scheduled Action¶
Navigate to: Settings → Technical → Scheduled Actions
Search for: "Mail: Fetchmail Service"
| Field | Recommended Value |
|---|---|
| Active | Checked |
| Interval Number | 5 |
| Interval Unit | Minutes |
| Number of Calls | -1 (unlimited) |
3. Alias Domain¶
Navigate to: Settings → General Settings → Discuss
Set Alias Domain to your email domain (e.g., yourdomain.com)
This is used for generating reply-to addresses like purchase+123@yourdomain.com.
4. Outgoing Mail Server¶
Navigate to: Settings → Technical → Email → Outgoing Mail Servers
| Field | Description |
|---|---|
| SMTP Server | smtp.yourdomain.com |
| SMTP Port | 587 (TLS) or 465 (SSL) |
| Connection Security | TLS or SSL |
| Username | Your email address |
| Password | Your password |
Database Tables¶
| Table | Purpose |
|---|---|
fetchmail_server |
Incoming mail server configuration |
mail_alias |
Email aliases for routing |
mail_message |
All messages with Message-ID |
mail_mail |
Outgoing email queue |
ir_cron |
Scheduled actions including fetchmail |
Check Settings via SQL¶
-- Check if fetchmail cron is active
SELECT name, active, interval_number, interval_type, nextcall
FROM ir_cron
WHERE name ILIKE '%fetchmail%';
-- Check incoming mail servers
SELECT name, server, state, is_ssl, active
FROM fetchmail_server;
-- Check mail alias domain
SELECT key, value
FROM ir_config_parameter
WHERE key ILIKE '%alias%';
-- Check recent incoming messages
SELECT id, message_id, model, res_id, date, subject
FROM mail_message
WHERE message_type = 'email'
ORDER BY date DESC
LIMIT 20;
Troubleshooting¶
Email Not Routing to Record¶
Symptoms: Vendor replies don't appear in PO chatter
Check:
-
Fetchmail cron is active:
-
Incoming mail server is connected:
-
Check Odoo logs:
-
Run fetchmail manually:
- Go to Scheduled Actions
- Find "Mail: Fetchmail Service"
- Click "Run Manually"
Common Issues¶
| Issue | Cause | Solution |
|---|---|---|
| Cron not running | Disabled after update | Re-enable in Scheduled Actions |
| Connection failed | Password changed | Update incoming mail server password |
| SSL error | Certificate issue | Check SSL/TLS settings |
| Emails not found | Wrong server/folder | Verify IMAP folder settings |
| Routing to wrong record | Header mismatch | Check original Message-ID |
Check Logs for Errors¶
# Check fetchmail activity
sudo grep -i "fetchmail" /var/log/odoo/odoo.log | tail -20
# Check mail routing
sudo grep -i "mail.*route\|routing" /var/log/odoo/odoo.log | tail -20
# Check for errors
sudo grep -i "error\|failed" /var/log/odoo/odoo.log | tail -50
Models That Support Email Routing¶
Any model that inherits mail.thread supports email routing:
| Model | Description |
|---|---|
purchase.order |
Purchase Orders |
sale.order |
Sales Orders |
crm.lead |
CRM Leads/Opportunities |
helpdesk.ticket |
Helpdesk Tickets |
project.task |
Project Tasks |
account.move |
Invoices/Bills |
hr.applicant |
Job Applications |
Example Model Definition¶
from odoo import models
class PurchaseOrder(models.Model):
_name = 'purchase.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
# The model now supports:
# - Chatter (message log)
# - Email sending
# - Email routing (incoming)
# - Activities
Mail Aliases¶
Mail aliases allow routing emails to create new records or find existing ones.
View Aliases¶
Navigate to: Settings → Technical → Email → Aliases
| Alias | Model | Description |
|---|---|---|
sales |
sale.order | Create new quotation |
purchase |
purchase.order | Create new RFQ |
support |
helpdesk.ticket | Create new ticket |
How Aliases Work¶
When an email arrives at support@yourdomain.com:
- Fetchmail downloads the email
- Odoo parses the
Toaddress:support@yourdomain.com - Finds
mail.aliaswithalias_name = 'support' - Creates new
helpdesk.ticketor routes to existing one
Required Modules¶
| Module | Purpose |
|---|---|
mail |
Core mail functionality, mail.thread |
fetchmail |
Incoming mail fetching |
| Model-specific | e.g., purchase, sale, helpdesk |
Check Installed Modules¶
SELECT name, state
FROM ir_module_module
WHERE name IN ('mail', 'fetchmail', 'purchase', 'sale')
ORDER BY name;
Best Practices¶
-
Use IMAP over POP3 - IMAP allows Odoo to mark emails as read without deleting them
-
Set reasonable fetch interval - 5 minutes is usually sufficient, lower intervals increase server load
-
Use dedicated email account - Create a separate email for Odoo (e.g.,
odoo@yourdomain.com) rather than using personal email -
Monitor fetchmail logs - Regularly check logs for connection issues
-
Test after updates - Verify fetchmail cron is still active after Odoo updates
-
Use app passwords - For Gmail/Office 365, use app-specific passwords instead of main password
Gmail/Google Workspace Setup¶
For Gmail, you need:
- Enable "Less secure app access" OR use App Password
- Enable IMAP in Gmail settings
- Use these settings:
| Setting | Value |
|---|---|
| Server | imap.gmail.com |
| Port | 993 |
| SSL | Yes |
| Username | your-email@gmail.com |
| Password | App Password (16 characters) |
Office 365 Setup¶
For Office 365:
| Setting | Value |
|---|---|
| Server | outlook.office365.com |
| Port | 993 |
| SSL | Yes |
| Username | your-email@yourdomain.com |
| Password | App Password |
OAuth2
Microsoft is deprecating basic auth. You may need OAuth2 authentication module for Office 365.