BFBambooForge Labs

Listmonk Connector

Two-way subscription and opt-out sync between Odoo and Listmonk, so an unsubscribe on either side is honoured on both. Import lists too.

Buy on the Odoo Apps StoreOpen the live demoMarketing€104Community & Enterprise

Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0. Technical name bambooforge_listmonk_connector.

Odoo 16.0Odoo 17.0Odoo 18.0Odoo 19.0
Full walkthrough on a live Odoo 19.0 database, with subtitles. It ends with what this app deliberately does not do.

Listmonk Connector

A read-only bridge from Listmonk into Odoo 18's Email Marketing: import your Listmonk lists as Odoo mailing lists and your Listmonk subscribers as Odoo mailing contacts — over the official Listmonk REST API, with a resilient job queue, dry-run safety, validation and rollback so you stay in control.

This page is the complete manual. If you follow it top to bottom you can install, connect, run your first import, automate it, and fix the common issues without contacting support.

Overview

The connector pulls newsletter data from Listmonk into Odoo over the Listmonk JSON REST API. It is import-only: data flows Odoo ← Listmonk and the connector never writes back to Listmonk. It covers two entities:

  • Listsmailing.list — each Listmonk list becomes an Odoo mailing list. The list name is imported, and the raw Listmonk metadata is kept on the record so the source of truth is auditable: external id, uuid, list type (public/private), opt-in mode (single/double) and the last observed subscriber count.

  • Subscribersmailing.contact — each Listmonk subscriber becomes an Odoo mailing contact (name + email). The raw Listmonk status (enabled / disabled / blocklisted) is kept, and the contact's list_ids are resolved from the subscriber's Listmonk lists against the lists you already imported.

Because list links are resolved by lookup, lists are always imported before subscribers, so each subscriber's list membership lands correctly.

Direction of sync: Odoo ← Listmonk (pull-only). Every remote call goes through a queue: nothing is written to Odoo until a job runs, jobs retry with back-off on transient failures, and a circuit breaker pauses an instance that keeps failing.

Requirements

  • Odoo: 18.0, Community or Enterprise. The connector depends on Email Marketing (mass_mailing), which installs automatically.

  • Listmonk: a reachable Listmonk server. Works with both Listmonk v2 (config admin username + password) and v3+ (API user name + token) — both are Basic-style credentials (see Step 1).

  • Python: no extra libraries beyond a standard Odoo 18 install.

  • Odoo access: any internal user can use the connector screens. The API token/password is stored in a system-only field, so only the Settings / Administration user can read or change it.

  • Network: outbound HTTPS from Odoo to your Listmonk server. By default the connector refuses internal/loopback/private hosts as an SSRF safeguard (see Safety features).

Installation

  1. Copy bambooforge_listmonk_connector into your Odoo addons path.

  2. Restart the Odoo service.

  3. Open Apps, click Update Apps List, search for Listmonk, and press Activate / Install. The dependency Email Marketing installs automatically.

No Listmonk server is required to evaluate the connector: a mock Listmonk REST API ships inside the module, so you can install, explore and run the full import flow against sample data before pointing it at a real server. To use it, set the instance Base URL to <your-odoo-url>/listmonk/mock_api with API Path api and any Basic-auth user/token.

Step 1 — Set up API access on Listmonk

The connector authenticates with HTTP Basic auth, sent on every request as the Authorization: Basic base64(user:token) header. There is no OAuth and no API-key query parameter — just a username and a secret.

Listmonk v3 and newer (recommended):

  1. Sign in to your Listmonk admin UI as a super-admin.

  2. Go to Settings ▸ Users ▸ API users and create an API user.

  3. Give it a role that can read lists and subscribers (read access is all the connector needs — it never writes to Listmonk).

  4. Copy the API user name and the generated token. The token is shown once.

  5. In Odoo, enter the API user name as API User and the token as API Token / Password.

Listmonk v2:

  1. Use your Listmonk config admin username and admin password.

  2. In Odoo, enter the admin username as API User and the admin password as API Token / Password.

In both cases the connector sends the pair as HTTP Basic over the wire, so use HTTPS in production to keep the credential confidential.

Step 2 — Create the connection in Odoo

Open Listmonk Connector ▸ Configuration ▸ Instances and create a record.

Listmonk instance / connection cockpit

Key fields:

Field

What to enter

Name

A label for this server, e.g. Production Listmonk.

Base URL

Your Listmonk server root, e.g. https://lists.example.com (or http://localhost:9000 for a local server, with Allow internal host on).

Authentication

HTTP Basic (user + token) — the only mode. Sent as the Authorization: Basic header on every request.

API User

The Listmonk Basic-auth username — the API user name (v3+) or the config admin username (v2).

API Token / Password

The Listmonk Basic-auth secret — the API user token (v3+) or the admin password (v2). Visible to administrators only.

API Path

The Listmonk REST API path. Leave the default api unless your server differs.

Verify SSL

Keep on for production. Turn off only for self-signed test certificates.

Allow internal host

Off by default. Turn on only to reach a Listmonk server on localhost or a private network (lowers the SSRF guard — see Safety features).

Then click Test Connection. The connector calls GET /api/lists?per_page=1 with your credentials. A green Connected state means the URL and Basic credentials are correct; the connector also seeds the default subscriber-status mappings on first connect. If it fails, the exact error is recorded on the form and in Logs (see Troubleshooting).

Step 3 — First import (dry-run, then live)

New instances start with Dry-run ON. In dry-run, import jobs simulate writes: instead of creating Odoo records they produce Validation Results you can review under Listmonk Connector ▸ Operations ▸ Validation Results. This lets you confirm what would happen before anything is written.

To run a first import:

  1. On the instance, click Import Lists first, then Import Subscribers. Importing lists first lets each subscriber's list membership resolve. These buttons enqueue jobs; they do not block the UI.

  2. Jobs are processed by the Listmonk Queue Processor scheduled action (every minute), or immediately if you run the processor manually from Operations ▸ Queue Jobs.

  3. Review Validation Results while still in dry-run.

  4. When satisfied, open the instance, turn Dry-run OFF, and run the imports again to write the records for real.

Imported records land in the standard Email Marketing app: lists under Email Marketing ▸ Mailing Lists, contacts under Email Marketing ▸ Mailing List Contacts. Each carries its Listmonk external id, so re-imports update the same record instead of duplicating it.

Field mapping & customization

  • Field Mappings (Configuration) map Listmonk fields to Odoo fields per entity. Use Generate suggested mappings on the instance to seed the business-critical ones, then adjust.

  • Schema Fields lists the discovered fields per entity. Run Schema Introspection on the instance to refresh it: the connector flattens a live Listmonk sample record (falling back to the bundled mock when the server is unreachable) and lists the Odoo target fields beside it.

  • Subscriber Status Mappings (seeded on first connect) record how each raw Listmonk subscriber status (enabled / disabled / blocklisted) is interpreted. The raw status is always kept verbatim on the contact in listmonk_status.

  • Listmonk metadata is preserved verbatim on each imported record (external id, uuid, list type, opt-in mode, subscriber count on lists; uuid and status on contacts) so you can audit exactly what came from Listmonk.

Automation (scheduled actions)

The module ships these scheduled actions (Settings ▸ Technical ▸ Scheduled Actions):

Scheduled action

Default

Purpose

Listmonk Queue Processor

every 1 min

Processes queued import jobs.

Listmonk Reconciliation

every 15 min

Pulls recent remote lists/subscribers for enabled entities and re-queues anything missing or previously failed.

Listmonk Maintenance

every 1 hr

Recovers stale/locked jobs and trims old jobs and logs.

Listmonk Flow Scheduler

every 5 min

Runs scheduled import flows.

Listmonk Flow Metrics

every 1 hr

Aggregates flow-run metrics.

Listmonk Auto Recover

every 15 min

Reopens a tripped circuit breaker once the cooldown passes and no blocking validation issues remain.

Turn on Auto import / Auto reconcile for lists and/or subscribers on the instance to let the scheduled actions keep things in sync hands-free.

Webhooks: Listmonk does not push entity webhooks for list/subscriber imports, so this connector is pull-only — it imports on the schedule above. A /listmonk/webhook route exists but is a neutralized placeholder: it acknowledges with HTTP 200 and imports nothing, so existing subscriptions or health checks do not 404. Do not rely on it to import data; use the scheduled import / reconcile flows instead. Inbound webhooks are a roadmap item.

Safety features

  • Dry-run mode (Dry-run, ON by default) — simulate writes and review Validation Results before going live.

  • Business validation profiles (Business validation profile: Minimal / Standard / Strict) — gate risky writes; Standard is the default.

  • Resilient queue — every remote action is a job. API calls retry with exponential back-off (API retry limit / backoff / cap), and failed jobs retry up to Retry limit before moving to the dead-letter state.

  • Circuit breaker (Circuit state) — after repeated failures, or any auth/configuration failure, the instance auto-pauses (Tripped). The Auto Recover action reopens it after a cooldown once no blocking issues remain; or click Resume.

  • Rollback snapshots (Rollback enabled, on by default) — imports can capture a snapshot so you can undo a batch from Operations ▸ Rollback Snapshots.

  • SSRF guard — the connector refuses internal/loopback/private/link-local/ reserved hosts unless Allow internal host is explicitly enabled.

Troubleshooting

Symptom

Cause and fix

Test Connection fails with 401/403

Wrong Basic credentials, or the Listmonk user lacks read access. Re-check Step 1: API User must be the API user name (v3+) or admin username (v2), and API Token / Password the matching token/password. An auth failure also trips the circuit breaker, so click Resume after fixing.

"Base URL ... is not allowed because it resolves to the non-public address"

Base URL points at localhost or a private/loopback IP. Enable Allow internal host on the instance (self-hosted/test Listmonk only).

"the URL must start with http:// or https://"

The Base URL is missing its scheme. Enter the full https://lists.example.com.

SSL errors on Test Connection

Self-signed certificate. Use a valid cert, or turn off Verify SSL for testing only.

Jobs stay in Pending

The Queue Processor is off, the instance is paused, or the circuit breaker is tripped. Check Scheduled Actions is active, that the instance is not Paused, and Resume if needed.

Instance shows Tripped

Circuit breaker tripped after repeated failures (or an auth/config error). Fix the server/credentials; Auto Recover reopens it after the cooldown, or click Resume.

Subscribers import but have no lists

Subscriber list links resolve against lists already in Odoo. Run Import Lists first, then Import Subscribers again.

Nothing happens after Import

You are in Dry-run. Review Validation Results, then turn dry-run off and re-run.

Records imported twice

Imports are keyed by the Listmonk external id, so this should not happen. If it does, check that two instances do not point at the same Listmonk server.

"Listmonk rate limit reached" / server errors

Listmonk returned 429 or a 5xx. The client already retries with back-off; if it persists, lower the import limits or batch size and retry later.

For anything else, Operations ▸ Logs records every API call, payload and error with a timestamp.

Frequently asked questions

Which versions are supported? Odoo 18.0 on the Odoo side. On the Listmonk side, both v2 (config admin username + password) and v3+ (API user name + token) work, because the connector uses HTTP Basic for both. Validate your exact build with the bundled mock first.

Do I need a Listmonk server to evaluate it? No. A mock Listmonk REST API ships inside the module. Point the instance Base URL at <your-odoo-url>/listmonk/mock_api (API Path api, any Basic credentials) to run the full import flow against sample data.

How does authentication work? HTTP Basic only. The connector sends Authorization: Basic base64(user:token) on every request. Use HTTPS so the credential is not exposed in transit.

Does the connector write anything back to Listmonk? No. It is strictly read-only / import-only. It only reads lists and subscribers from Listmonk and creates/updates the matching Odoo mailing lists and contacts.

Is there real-time sync via webhooks? Not in v1. Listmonk does not push entity webhooks for list/subscriber imports, so the connector pulls on a schedule (every minute for the queue, every 15 minutes for reconciliation). The /listmonk/webhook route is a placeholder only. Inbound webhooks are on the roadmap.

Is it safe to run against production data? Yes — Dry-run is ON by default, validation gates risky writes, and rollback snapshots let you undo a batch. You decide when to go live. And because the connector never writes to Listmonk, your Listmonk data is never at risk.

What support and refund policy do I get? Every request is answered within 24 hours, setup help included. If you report a bug within 2 months of purchase and it is not resolved within 15 days, you are entitled to a full money-back refund.

Data, privacy & limits

  • The connector reads lists and subscribers from your Listmonk server and writes the corresponding Odoo mailing lists and mailing contacts. It never writes to Listmonk — there is no export and no two-way sync.

  • The API token/password is stored in a system-only (admin-only) field.

  • In scope today: list import (→ mailing.list), subscriber import (→ mailing.contact) with resolved list membership and preserved Listmonk metadata, scheduled import + reconciliation, dry-run, validation and rollback.

  • Out of scope / roadmap: write-back / export to Listmonk; inbound real-time webhooks; incremental list filtering by modified date (v1 lists by page size / offset).

Support & updates

  • Support: support@bambooforge.dev — answered within 24 hours, setup help included.

  • Refund: report a bug within 2 months of purchase; if unresolved within 15 days, full refund.

  • Full source is included. Updates track the supported Odoo 18 / Listmonk REST API line.

Upgrading & version compatibility

This build targets Odoo 18.0. Each Odoo major series (17.0, 18.0, 19.0) has its own dedicated build of this module — always install the build that matches your Odoo version. Mixing a build with a different Odoo series is not supported.

Patch upgrades (same series, e.g. 18.0.1.0.0 → later)

  1. Back up your database and filestore first.

  2. Replace the module folder with the newer build.

  3. Restart Odoo with the module updated:

    ./odoo-bin -c your.conf -u bambooforge_listmonk_connector -d your_db
  4. Odoo applies any schema/data changes automatically. Your existing records and configuration are preserved.

Cross-version migration (e.g. Odoo 17 → 18)

Upgrading Odoo itself is a database migration handled by Odoo's standard upgrade tooling. When you migrate the database to the next Odoo series, install the matching build of this module for that series. Data created by this module carries over with the database migration.

After any upgrade the module's scheduled actions resume on their normal cadence — no manual re-activation is required.

Uninstallation

You can remove this module at any time from Apps → (this module) → Uninstall, or from the command line. Uninstalling is clean and reversible by reinstalling — but note what is and is not deleted.

What is removed

  • The module's own tables and every record in them (22 models, prefixed listmonk.*) — this is the data this module created.

  • The menus, actions, views and reports this module installed.

  • Its scheduled actions (cron jobs) — they stop immediately on uninstall.

  • Connection records, credentials, field mappings, queue jobs and sync logs stored in Odoo.

What is preserved

  • Your remote platform is never touched. Uninstalling only removes the Odoo-side connector; products, customers and orders on the external store/service are untouched.

  • Records already imported into standard Odoo models (e.g. contacts, products, sales orders) remain — they are ordinary Odoo records once created.

  • Attachments and chatter messages on standard records are kept.

As always, take a database backup before uninstalling in production.

Changelog

18.0.1.0.0

Current release for Odoo 18.0. This build includes:

  • Two-way subscription & opt-out sync between Odoo 18 and Listmonk: unsubscribe/blocklist <-> mail.blacklist + mailing.contact opt-out (compliance-safe), plus Lists & Subscribers import.

  • Resilient queue, dry-run safety, auditable source.

Feature additions and fixes ship as new builds on the Odoo Apps store; this page and the module's version reflect the current published release. Always keep the build matched to your Odoo series (see Upgrading & version compatibility).

Screens

01 connection cockpit - bambooforge_listmonk_connector
01 connection cockpit
Video poster operations - bambooforge_listmonk_connector
Video poster operations
Video poster - bambooforge_listmonk_connector
Video poster