I was setting up Odoo for an organization that uses Unosend as their email provider. Unosend has a generous free plan — 5,000 emails/month — and I'd already verified the domain, set up DKIM and SPF. The REST API works fine.
The SMTP relay didn't.
SMTP wouldn't authenticate
Odoo sends email through SMTP by default. I configured the Unosend SMTP settings in Odoo and started seeing this in the logs:
WARNING: No mail server matches the from_filter
WARNING: Ocurrió un error al enviar el correo electrónico
I tested the credentials directly:
import smtplib
server = smtplib.SMTP('smtp.unosend.co', 587)
server.starttls()
server.login('unosend', 'un_XXXX...') # 535 Authentication Failed
535 Authentication Failed, every time. I tried different API keys and created one specifically for SMTP. Same result.
Unosend's docs say the SMTP password is your API key. But their SMTP backend runs on Zoho (mx.zohomail.com), and Zoho rejects the auth. Likely a bug on their side or a temporary limitation. The HTTP API (POST /api/v1/emails) works every time.
What I did: intercept the email at the right layer
Odoo sends email through ir.mail_server, specifically the send_email() method. It takes a built email.message.Message (RFC 2822) and sends it via SMTP.
I created an Odoo module that inherits ir.mail_server and overrides send_email() to route through Unosend's HTTP API instead of SMTP.
The flow:
[mail module] → ir_mail_server.send_email()
│
├── Unosend enabled? → parse message → POST /api/v1/emails
│ │
│ ├── Success → return Message-ID
│ └── Fail → log + SMTP fallback
│
└── No → original SMTP
The module works like this:
- Adds fields in Settings → Email (API key, default FROM address)
- Every time Odoo sends an email, the module checks: "is there a Unosend API key?"
- It parses the RFC 2822 message and extracts FROM, TO, CC, Subject, HTML/plain text
- It POSTs to
https://www.unosend.co/api/v1/emailswith the API key - If the API call fails, it falls back to SMTP (optional, configurable)
The module doesn't replace the SMTP server. It complements it. The SMTP server still handles the from_filter and acts as a fallback.
Why ir_mail_server.send_email() and not mail.mail._send()
mail.mail._send() iterates over email batches, manages states (outgoing → exception → sent), and calls ir_mail_server.send_email() for each one. Overriding _send() is riskier because there's state logic and notification handling around it. The right interception point is right before the SMTP connection opens.
Why I'm sharing this
This isn't unique to Unosend. Plenty of email providers have modern HTTP APIs but flaky SMTP setups. SendGrid, Mailgun, Postmark, Resend — they all offer both, but SMTP doesn't always work in every environment (corporate firewalls, proxies, containers with port restrictions).
The module is designed to be generic. Change the base URL and the payload format, and it works with any email API.
Where to find it
The code is in a public repo:
GitLab: gitlab.com/zczoft-odoo/unosend-mail
Repo still needs creation. Leave a comment if you need it and I'll polish it for publication.
The structure:
unosend_mail/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── ir_mail_server.py # The core: send_email override
│ └── res_config_settings.py # Configuration fields
├── views/
│ └── res_config_settings_views.xml
└── SPECIFICATION.md
Requires Odoo 18+ and the mail module. Contributions welcome.
What's next
- Support attachments (currently skipped)
- Map Unosend's open/click tracking back into Odoo
- Make it compatible with Odoo 16+ (shouldn't need major changes)
- Add automated tests
What I learned
- Don't assume SMTP works just because the API works. Zoho (Unosend's backend) has its own auth rules.
- Override at the right point.
ir_mail_server.send_email()is high enough to skip state logic, low enough to catch all outbound traffic. - Always leave a fallback. APIs can fail (rate limits, maintenance, version changes). The SMTP that "doesn't work" today might save you tomorrow.
- Vendor docs don't always match reality. Unosend documents SMTP with API key as password, but it doesn't work in practice. Always test.
Had the same issue with Unosend or another provider? Got an Odoo module for sending via HTTP API? Leave a comment or contribute to the repo.