Skip to main content

How to Integrate Mailchimp with Odoo Marketing Automation

Sync Odoo CRM contacts to Mailchimp audience, trigger campaigns from Odoo events, push opens/clicks back. Webhook-driven, GDPR-compliant.

E
ECOSIRE Research and Development Team
|May 4, 20269 min read1.7k Words|

By the end of this recipe, your Odoo 19 contacts will sync to a Mailchimp audience with merge fields preserved, segments built from Odoo data (last purchase date, lifetime value, tag), Mailchimp opens and clicks pushed back to Odoo CRM as activities, and full GDPR-compliant unsubscribe handling.

Skill required: Odoo developer with REST API basics. Time required: 3 hours setup, 90 minutes testing. ECOSIRE has built this for B2B clients running drip campaigns and B2C clients running 50,000-recipient newsletters, and the recipe below is the playbook we ship.

The trap most teams fall into: bidirectional sync with no master. A contact updated in Odoo overwrites a Mailchimp-side update from a customer who unsubscribed via the email footer. Now Odoo thinks they're subscribed but Mailchimp refuses to email them. The recipe below makes Mailchimp the master for subscription state and Odoo the master for everything else.

What you will need

  • Mailchimp account with at least the Standard plan (Free plan blocks API access on most automation features).
  • Mailchimp API key: User > Account > Extras > API keys > Create.
  • Mailchimp audience (list) ID: Audience > Settings > Audience name and defaults > Audience ID (looks like 1a2b3c4d5e).
  • Odoo version: 17, 18, or 19. The OCA mail_mailchimp module is a starting point; for production we typically extend it.
  • Webhook endpoint on your Odoo (HTTPS).
  • Time: 3 hours setup, 90 minutes testing.

Step-by-step

1. Configure the Mailchimp backend in Odoo

Add a config record:

class MailchimpAccount(models.Model):
    _name = 'mailchimp.account'

    name = fields.Char()
    api_key = fields.Char()  # format: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us21
    audience_id = fields.Char()
    server_prefix = fields.Char(compute='_compute_server_prefix')

    @api.depends('api_key')
    def _compute_server_prefix(self):
        for r in self:
            r.server_prefix = r.api_key.split('-')[-1] if r.api_key else 'us1'

    def _api_url(self, path):
        return f"https://{self.server_prefix}.api.mailchimp.com/3.0{path}"

    def _request(self, method, path, **kwargs):
        return requests.request(method, self._api_url(path),
                                auth=('anystring', self.api_key), timeout=30, **kwargs)

The server prefix (us1, us21, etc.) is the suffix of the API key. Verification: a _request('GET', '/ping') returns Everything's Chimpy.

2. Push contacts from Odoo to Mailchimp

Map Odoo fields to Mailchimp merge fields:

Odoo fieldMailchimp merge tag
nameFNAME LNAME (split)
email(the email address itself)
country_id.nameCOUNTRY
total_spentLTV
last_order_dateLASTORDER
customer_rankTIER
def _upsert_member(self, partner):
    if not partner.email:
        return
    email_hash = hashlib.md5(partner.email.lower().encode()).hexdigest()
    fname, _, lname = (partner.name or '').partition(' ')
    payload = {
        'email_address': partner.email,
        'status_if_new': 'subscribed' if partner.email_optin else 'unsubscribed',
        'merge_fields': {
            'FNAME': fname,
            'LNAME': lname,
            'COUNTRY': partner.country_id.name or '',
            'LTV': partner.total_spent or 0,
            'TIER': partner.customer_rank or 0,
        },
        'tags': partner.category_id.mapped('name'),
    }
    self._request('PUT', f"/lists/{self.audience_id}/members/{email_hash}", json=payload)

Trigger this on res.partner write/create. Verification: edit a partner email in Odoo; Mailchimp audience refreshes within 5 seconds.

3. Configure webhook for Mailchimp > Odoo

Mailchimp can POST to your Odoo when a member subscribes, unsubscribes, profile updates, or cleans (bounces). In Mailchimp > Audience > Settings > Webhooks, set URL = https://your-odoo.com/mailchimp/webhook, tick all event types.

@http.route('/mailchimp/webhook', auth='public', csrf=False, methods=['POST', 'GET'])
def webhook(self, **kwargs):
    if request.httprequest.method == 'GET':
        return request.make_response('OK', status=200)  # Mailchimp's verification ping
    event_type = kwargs.get('type')
    data = kwargs.get('data', {})
    email = data.get('email')
    partner = request.env['res.partner'].sudo().search([('email', '=ilike', email)], limit=1)
    if not partner:
        return request.make_response('Not found', status=200)  # 200 not 404 - don't trigger Mailchimp retries
    if event_type == 'unsubscribe':
        partner.email_optin = False
    elif event_type == 'subscribe':
        partner.email_optin = True
    elif event_type == 'cleaned':
        partner.email_invalid = True
    return request.make_response('OK', status=200)

Verification: unsubscribe from a Mailchimp campaign using the footer link; the Odoo partner record flips email_optin=False within 30 seconds.

4. Build segments based on Odoo data

In Mailchimp > Audience > Segments, build dynamic segments using the merge fields you populated:

  • VIP customers: LTV > 5000 AND TIER >= 2
  • At-risk customers: LASTORDER < 90 days ago AND LTV > 1000
  • New US signups: COUNTRY = "United States" AND created_at < 30 days ago

These segments auto-refresh as Odoo pushes updates. Verification: a contact moves from one segment to another when their underlying Odoo data changes.

5. Trigger campaigns from Odoo events

Use the Mailchimp automations API to start an automation customer journey from an Odoo event:

def _trigger_journey(self, automation_id, member_email):
    self._request('POST',
        f"/automations/{automation_id}/emails/{email_id}/queue",
        json={'email_address': member_email}
    )

Wire to Odoo automated actions: when a customer abandons a cart, queue them into the "abandoned cart" automation. When they make their first purchase, queue them into the "welcome series".

Verification: a test customer trips an automation and receives the first email within 1 to 5 minutes.

6. Pull opens and clicks back to Odoo

For attribution, pull engagement events from Mailchimp's reports API and post them to the matching CRM lead/opportunity:

def _pull_campaign_engagement(self, campaign_id):
    resp = self._request('GET', f"/reports/{campaign_id}/email-activity?count=1000")
    for member in resp.json().get('emails', []):
        partner = self.env['res.partner'].sudo().search([
            ('email', '=ilike', member['email_address']),
        ], limit=1)
        if not partner:
            continue
        for activity in member.get('activity', []):
            if activity['action'] in ('open', 'click'):
                lead = self.env['crm.lead'].search([
                    ('partner_id', '=', partner.id),
                    ('stage_id.is_won', '=', False),
                ], limit=1)
                if lead:
                    lead.message_post(body=f"Mailchimp {activity['action']}: {activity.get('url') or campaign_id}")

Schedule daily. Verification: a recipient who clicks a campaign link sees that activity logged on their CRM lead.

7. Handle merge field updates dynamically

When Odoo schema changes (e.g., a new computed field on res.partner), update the Mailchimp merge fields list:

def _ensure_merge_fields(self):
    expected = {'FNAME', 'LNAME', 'COUNTRY', 'LTV', 'TIER', 'LASTORDER'}
    resp = self._request('GET', f"/lists/{self.audience_id}/merge-fields?count=100")
    existing = {f['tag'] for f in resp.json()['merge_fields']}
    for tag in expected - existing:
        self._request('POST', f"/lists/{self.audience_id}/merge-fields", json={
            'tag': tag,
            'name': tag.title(),
            'type': 'number' if tag in ('LTV', 'TIER') else 'text',
        })

Run on module install/upgrade. Verification: required merge fields exist on the Mailchimp side.

8. GDPR compliance: double opt-in and audit trail

For EU contacts, use double opt-in: status_if_new = pending (not subscribed). Mailchimp sends a confirmation email; the contact must click before Mailchimp marks them subscribed.

status = 'pending' if partner.country_id.code in EU_COUNTRIES else 'subscribed'

Keep an audit log on the Odoo side showing every opt-in source (form, import, manual) with timestamp.

Verification: a new EU contact is created with status pending; they receive the confirm email and click; status moves to subscribed; Odoo log shows the source.

Common mistakes

  • Bidirectional sync without a master. Pick: Mailchimp masters subscription, Odoo masters everything else.
  • Pushing without checking opt-in. GDPR fines start at €20 million.
  • Ignoring cleaned and bounced events. Hard bounces to a dead address tank your sender reputation.
  • Hardcoding the API server prefix. Different keys are on different DCs; always parse from the key suffix.
  • Importing a CSV of 50,000 contacts to Mailchimp without warmup. Mailchimp throttles cold imports; warm by sending small campaigns first.

Going further

Behavioral triggers: track Odoo events like "viewed product 3 times" and trigger highly targeted Mailchimp campaigns. Pair with Odoo website analytics to capture browse behavior and feed it as merge fields.

RFM segmentation: build Recency-Frequency-Monetary segments in Odoo, push the RFM cohort code as a merge field, run a different campaign per cohort. Champions (high R, high F, high M) get loyalty; At-Risk (low R, mid F, mid M) get win-back.

Multi-store / multi-brand: each brand gets its own Mailchimp audience; the connector pushes to the right audience based on partner.brand_id.

Postcards: Mailchimp also supports physical postcards via the same API. Trigger one when a high-value customer hits 90-day inactivity.

A/B subject line testing: Mailchimp's built-in A/B test runs 10% of the audience on each variant, picks the winner, sends to the rest. Pair with a custom merge field to track which variant a recipient saw.

Predicted send time: Mailchimp ML predicts the best time to send for each contact. Enable in audience settings.

Drip onboarding sequences: 7-email drip starting at signup. Each email triggers based on time elapsed AND completion of previous email's CTA. Mailchimp's Customer Journeys builder handles this visually.

Post-purchase journey: 5-email sequence after first purchase. Welcome > Product Tips > Cross-sell > Review Request > Loyalty Invite. Each step optional; skip based on customer behavior.

Re-engagement: customers who haven't opened in 90+ days get a "we miss you" campaign. Those who don't respond after 2 attempts get scrubbed (improves overall deliverability).

Sunset policy: hard-bounce or persistent non-engagement triggers automatic unsubscribe to maintain list hygiene. Mailchimp's Compliance section has this built in.

Holiday campaign automation: schedule a 12-month calendar of campaigns (Black Friday, Cyber Monday, Valentine's, etc.). Pair with category/product filters so each campaign features the relevant inventory.

Revenue attribution: tag every Mailchimp campaign with a UTM parameter that flows into the Odoo SO's source field. Compute revenue attributed to each campaign.

SMS + Email omnichannel: Mailchimp Premium includes SMS. Run synchronized campaigns where SMS and email reinforce each other within the same customer journey.

Transactional triggers via Mandrill: for transactional emails (order confirm, password reset), use Mandrill (Mailchimp's transactional service). Same audience, different sender reputation.

For full Mailchimp + Odoo deployment including custom segmentation logic, behavioral triggers, and revenue attribution, ECOSIRE custom Odoo development builds the entire stack. Pair this with how to integrate WhatsApp Business API with Odoo for omnichannel.

If your team sends through Brevo (formerly Sendinblue) rather than Mailchimp, ECOSIRE builds a Sendinblue (Brevo) connector for Odoo to order: Odoo email and SMS go out through Brevo, contacts sync both ways, and opens, clicks and bounces come back to Odoo.

Frequently Asked Questions

What does Mailchimp cost?

Free up to 500 contacts. Standard tier ($20/month) up to 1,500 contacts. Premium ($350/month) for 10,000+. Pricing scales with audience size, not message count.

Should I use Mailchimp or Odoo's native Email Marketing?

Odoo's native is great for transactional emails (order confirmations, invoices) and small marketing pushes. Mailchimp wins for large-list deliverability, A/B testing UI, and customer journey automations. We often run both: Odoo for transactional, Mailchimp for marketing.

Yes. Mailchimp's marketing_permissions field tracks consent per channel. Push the consent state from Odoo (partner.email_optin_at timestamp) so Mailchimp records the consent date.

How do I handle people who exist in two Odoo records with the same email?

Mailchimp keys off email — only one Odoo record can "own" each email. Build a deduplication script that merges duplicate res.partner records before sync.

For complex Mailchimp deployments including AI subject-line testing, predicted send times, and marketing-attributed pipeline reporting, ECOSIRE marketing services build the analytical layer. Or read how to integrate DocuSign with Odoo to round out the customer-journey toolkit.

E

Written by

ECOSIRE Research and Development Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

Transform Your Business with Odoo ERP

Expert Odoo implementation, customization, and support to streamline your operations.