From odoo-crm-toolkit
Create email and SMS mailing campaigns (mailing.mailing) in Odoo 18 via XML-RPC API. Use this skill when the user asks to "create a mailing campaign", "create email campaign", "create SMS campaign", "new newsletter", "create a mass email", "set up email blast", "vytvoř mailing kampaň", "vytvoř emailovou kampaň", "vytvoř SMS kampaň", "nový newsletter", "hromadný email", "rozesílka", "kampaň", or any request involving creating or managing mailing.mailing campaigns in Odoo 18.
How this skill is triggered — by the user, by Claude, or both
Slash command
/odoo-crm-toolkit:mailing-campaignsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Create and manage email and SMS mailing campaigns in Odoo 18 via XML-RPC API.
Create and manage email and SMS mailing campaigns in Odoo 18 via XML-RPC API.
Before executing any API calls, read the shared reference at ${CLAUDE_PLUGIN_ROOT}/references/xmlrpc-api.md for connection setup.
Same environment variables: ODOO_URL, ODOO_DB, ODOO_API_KEY (UID se zjistí automaticky)
model_ids = models.execute_kw(DB, UID, KEY, 'ir.model', 'search', [
[['model', '=', 'mailing.contact']]
])
mailing_model_id = model_ids[0]
mailing_id = models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'create', [{
'subject': 'Email Subject Line',
'mailing_type': 'mail',
'body_html': '''<div style="margin: 0 auto; max-width: 600px; font-family: Arial, sans-serif;">
<h1>Headline</h1>
<p>Email body content...</p>
<a href="https://michalvarys.eu/page" style="display: inline-block; padding: 12px 24px; background: #2563eb; color: white; text-decoration: none; border-radius: 6px;">CTA Button</a>
</div>''',
'mailing_model_id': mailing_model_id,
'contact_list_ids': [(6, 0, [list_id])], # target mailing lists
'state': 'draft',
'email_from': 'Michal Varyš <[email protected]>',
'reply_to': '[email protected]',
'keep_archives': True,
}])
mailing_id = models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'create', [{
'subject': 'SMS Campaign Name',
'mailing_type': 'sms',
'body_plaintext': 'Text SMS zprávy. Odkaz: https://michalvarys.eu/page',
'mailing_model_id': mailing_model_id,
'contact_list_ids': [(6, 0, [list_id])],
'state': 'draft',
}])
<div style="margin: 0 auto; max-width: 600px; font-family: Arial, Helvetica, sans-serif; color: #333;">
<!-- Header with logo -->
<div style="text-align: center; padding: 20px 0;">
<img src="/web/image/company_logo" alt="Michal Varyš" style="max-height: 60px;"/>
</div>
<!-- Main content -->
<div style="padding: 20px; background: #ffffff;">
<h1 style="font-size: 24px; color: #1a1a1a; margin-bottom: 16px;">Headline</h1>
<p style="font-size: 16px; line-height: 1.6; color: #555;">
Content paragraph...
</p>
<!-- CTA Button -->
<div style="text-align: center; padding: 24px 0;">
<a href="https://michalvarys.eu/page"
style="display: inline-block; padding: 14px 28px;
background-color: #2563eb; color: #ffffff;
text-decoration: none; border-radius: 8px;
font-weight: bold; font-size: 16px;">
Zobrazit nabídku
</a>
</div>
</div>
<!-- View in browser link -->
<div style="text-align: center; padding: 16px; font-size: 12px; color: #999;">
<a href="/mailing/${mailing_id}/view" style="color: #999;">
Pokud se email nezobrazuje správně, otevřete v prohlížeči
</a>
</div>
<!-- Footer -->
<div style="text-align: center; padding: 20px; font-size: 12px; color: #999; border-top: 1px solid #eee;">
<p>Michal Varyš | michalvarys.eu</p>
<a href="/mailing/${mailing_id}/unsubscribe" style="color: #999;">Odhlásit se</a>
</div>
</div>
Odoo automatically generates a web version of each mailing. The link format:
${object.mailing_id.mailing_url_view} — in Jinja templates/mailing/<mailing_id>/view — direct URL patternUse this in the SMS template to link to the full email view.
When sending to a specific prospect (not a mailing list), target res.partner directly with mailing_domain. This avoids the need to create mailing.contact and mailing.list records.
partner_model_id = models.execute_kw(DB, UID, KEY, 'ir.model', 'search', [
[['model', '=', 'res.partner']]])[0]
mailing_id = models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'create', [{
'subject': 'Email Subject',
'mailing_type': 'mail',
'body_arch': email_html, # QWeb editable template
'body_html': email_html, # Converted HTML for sending
'mailing_model_id': partner_model_id,
'mailing_domain': f'["&", ("is_blacklisted", "=", False), ("id", "=", {partner_id})]',
'state': 'draft',
'email_from': 'Michal Varys <[email protected]>',
'reply_to': '[email protected]',
'keep_archives': True,
}])
sms_mailing_id = models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'create', [{
'subject': 'SMS Campaign Name',
'mailing_type': 'sms',
'body_plaintext': f'SMS text. Link: https://michalvarys.eu/{page_slug}',
'mailing_model_id': partner_model_id,
'mailing_domain': f'["&", ("phone_sanitized_blacklisted", "=", False), ("id", "=", {partner_id})]',
'state': 'draft',
}])
o_mail_snippet_general classes. See the email-templates SKILL for full block structure documentation.o_layout, o_mail_snippet_general, oe_structure), blocks appear greyed out in editor with "this block cannot be dropped anywhere"/mailing/{email_mailing_id}/view (shows full email with web preview link)mailings = models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'search_read', [
[['state', '=', 'draft']]
], {'fields': ['subject', 'mailing_type', 'state', 'create_date']})
models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'write', [
[mailing_id],
{'schedule_date': '2026-03-15 09:00:00'}
])
models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'action_send_mail', [[mailing_id]])
Odoo's mailing module automatically appends UTM parameters to all links in emails:
utm_source — mailing nameutm_medium — email or smsutm_campaign — campaign name# Assign UTM campaign + source + medium to mailing
models.execute_kw(DB, UID, KEY, 'mailing.mailing', 'write', [[mailing_id], {
'campaign_id': campaign_id, # utm.campaign ID
'source_id': source_id, # utm.source ID
'medium_id': medium_id, # utm.medium ID (4=Email, 11=SMS, 1=Website)
}])
For tracking clicks on web preview pages (outside emails), use link.tracker:
tracker_id = models.execute_kw(DB, UID, KEY, 'link.tracker', 'create', [{
'url': f'https://www.michalvarys.eu/contactus?partner={slug}',
'title': f'CTA - {company_name}',
'campaign_id': campaign_id,
'source_id': source_id,
'medium_id': 1, # Website
'label': 'Mam zajem',
}])
tracker = models.execute_kw(DB, UID, KEY, 'link.tracker', 'read', [[tracker_id]], {'fields': ['short_url']})
short_url = tracker[0]['short_url'] # e.g. https://michalvarys.eu/r/bPY
Use the short_url in web preview CTA buttons instead of mailto: links. Every click is recorded with IP/timestamp under the campaign.
Use the SAME utm.campaign across all touchpoints:
link.tracker with medium_id=1 (Website)mailing.mailing.campaign_id with medium_id=4 (Email)mailing.mailing.campaign_id with medium_id=11 (SMS)This gives unified analytics across all channels in Odoo.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub michalvarys/claude-plugins --plugin odoo-crm-toolkit