Postmark Inbound
Receive incoming email in Odoo through a Postmark inbound stream. The webhook hands every message to Odoo's own mail router, so aliases are resolved, replies are threaded onto the record they answer, and attachments are kept, exactly as with an IMAP mailbox.
Why route inbound mail this way
No open port 25, no IMAP
Many hosts block outbound and inbound SMTP. Postmark accepts the mail and posts it to Odoo over HTTPS, so no mailbox has to be polled and no MTA has to run next to Odoo.
One provider for both directions
If outgoing mail already leaves through Postmark, incoming mail can take the same path, with the same domain, the same DNS records and one place to look at the logs.
Nothing to teach Odoo
The module does not know about helpdesk, projects or CRM. It calls
mail.thread.message_process, so whatever your aliases already do keeps
working.
Replies stay on the record
The original headers are preserved, so the References chain still points at
the ticket or task the customer answered instead of opening a duplicate.
Attachments survive
Files come across as attachments on the message, whether the stream sends raw MIME or Postmark's parsed JSON parts.
Closed by default
The route compares a shared secret in constant time and refuses every call while that secret is unset, so an installed but unconfigured module is not an open endpoint.
Installation
- Copy postmark_inbound into your addons path.
- Go to Apps, click Update Apps List, then search for Postmark.
- The module is a technical add-on rather than an application, so clear the Apps filter to see it in the list.
- Click Activate. The only dependency is Discuss (
mail), which every Odoo database already has.
Configuration
1) Set the shared secret in Odoo
Enable developer mode, then go to Settings → Technical → System Parameters and create:
| Key | postmark_inbound.webhook_token |
|---|---|
| Value | a long random string, for example 32 hex characters |
Until this parameter exists the webhook answers 403 to every request and writes the reason to the server log. That is deliberate: an unconfigured install is never an open door.
2) Point the Postmark inbound stream at Odoo
In the Postmark account, open the server, then Inbound → Settings, and set the inbound webhook URL to:
https://your-odoo-host/postmark/inbound?token=the same secret
The token may also be sent as HTTP basic auth credentials if you prefer to keep it out of the URL. Both forms are accepted.
Switch on Include raw email content in JSON payload on the same page. With it, Odoo reads the original MIME source and keeps every header it routes on. Without it the module rebuilds the message from Postmark's parsed fields, which still routes and still threads, but anything Postmark did not parse out is lost.
3) Send the mail to Postmark
Point the MX record of the receiving domain at the inbound host Postmark shows for the stream, or forward selected mailboxes to the stream's inbound address. Mail routing is per domain, so a domain that already has MX records elsewhere cannot deliver part of its traffic here.
What arrives in Odoo
A message addressed to a team alias becomes whatever that alias creates. In the example
below, mail to support@acme-services.com lands as a task in the Customer
Support project, with the sender resolved to a contact and the body kept as the
description.
- Aliases are matched by Odoo, including the fallback to
Delivered-Towhen the visible recipient is a forwarding address. - Replies are threaded onto the same record through the
In-Reply-ToandReferencesheaders. - Attachments are stored on the message, ready to open from the chatter.
- Contacts are matched or created by the standard Odoo rules, not by this module.
Messages that no alias matches, or that answer a record which no longer exists, are answered with HTTP 200 and a short reason. Postmark then stops retrying, because a retry would fail the same way, and the reason is written to the Odoo log.
Reference
Endpoint
| Route | POST /postmark/inbound |
|---|---|
| Auth | ?token= query parameter or HTTP basic auth password |
| Secret | postmark_inbound.webhook_token |
| Body | the Postmark inbound JSON payload |
Answers
| 200 ok | routed, with the ids of the records the message landed on |
|---|---|
| 200 ignored | no alias matched or the target record is gone, do not retry |
| 400 | the body is not valid JSON, or carries no message at all |
| 403 | the token is missing, wrong, or not configured in Odoo |
Notes and tips
- Requires Odoo 19, Community or Enterprise. A matching 18.0 branch is available.
- Treat the token as a password. Rotate it by changing the system parameter and the Postmark webhook URL together, ideally in that order.
- Serve Odoo over HTTPS. The secret travels in the request, so plain HTTP would put it on the wire.
- Nothing is queued. Odoo processes the message during the request, so a webhook answer means the message was handled.
- Bounces and outgoing mail are untouched. This module only receives. Outgoing mail keeps using whatever mail server Odoo is configured with.
- Alias domains matter. An alias only resolves when its alias domain matches the address the mail was sent to. Check Settings → Technical → Email → Alias Domains when a message is reported as not routable.
Credits
Author
- Bojan Nišević <bojan@wolfinne.com>
Maintainer
Maintained by Wolfinne IT, wolfinne.com.
For support and customizations: hello@wolfinne.com.
Postmark and the Postmark logo are trademarks of ActiveCampaign. This module is an independent integration and is not affiliated with or endorsed by ActiveCampaign.
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
Discuss (mail)
|
| Lines of code | 118 |
| Technical Name |
postmark_inbound |
| License | LGPL-3 |
| Website | https://wolfinne.com |
Please log in to comment on this module