Skip to content

Odoo Chatter and message_post: Programmatic Messaging Guide

DeployMonkey Team · March 23, 2026 11 min read

The Chatter System

The chatter is Odoo's built-in communication layer. Any model that inherits mail.thread gets a message thread with comments, internal notes, status changes, and activity tracking. The message_post method is how you interact with it programmatically.

Basic message_post

# Simple comment visible to followers
record.message_post(
    body='Order has been shipped.',
    message_type='comment',
    subtype_xmlid='mail.mt_comment',
)

# Internal note (not sent to followers by email)
record.message_post(
    body='Customer called about delivery delay.',
    message_type='comment',
    subtype_xmlid='mail.mt_note',
)

Message Types and Subtypes

message_typeDescription
commentUser comment (chatter post)
notificationSystem notification
emailIncoming email
auto_commentAutomated comment (no notification)
subtype_xmlidEffect
mail.mt_commentVisible to all, sends email to followers
mail.mt_noteInternal note, no email notification
mail.mt_activitiesActivity-related message

Notifying Specific Partners

# Notify specific people (even if not followers)
record.message_post(
    body='Approval required for this order.',
    message_type='comment',
    subtype_xmlid='mail.mt_comment',
    partner_ids=[manager.partner_id.id, cfo.partner_id.id],
)

HTML-Formatted Messages

body = f"""
    <p>Invoice <strong>{invoice.name}</strong> has been created.</p>
    <ul>
        <li>Amount: {invoice.amount_total:,.2f} {invoice.currency_id.symbol}</li>
        <li>Due date: {invoice.invoice_date_due}</li>
        <li>Customer: {invoice.partner_id.name}</li>
    </ul>
"""
record.message_post(
    body=body,
    message_type='comment',
    subtype_xmlid='mail.mt_note',
)

Posting with Attachments

import base64

# Attach a file
attachment = self.env['ir.attachment'].create({
    'name': 'report.pdf',
    'datas': base64.b64encode(pdf_content),
    'res_model': record._name,
    'res_id': record.id,
})

record.message_post(
    body='Monthly report attached.',
    message_type='comment',
    subtype_xmlid='mail.mt_comment',
    attachment_ids=[attachment.id],
)

Field Tracking

Automatic change tracking logs field changes in the chatter. Declare tracked fields:

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    state = fields.Selection(tracking=True)
    user_id = fields.Many2one('res.users', tracking=True)
    amount_total = fields.Monetary(tracking=True)

When a tracked field changes, Odoo automatically posts a message like: "Stage changed from Draft to Confirmed. Salesperson changed from John to Jane."

Tracking with Custom Labels

priority = fields.Selection([
    ('0', 'Normal'),
    ('1', 'Important'),
    ('2', 'Urgent'),
], tracking=True, string='Priority')

Custom Subtypes

Create custom subtypes for filtering messages:

<record id="mt_order_shipped" model="mail.message.subtype">
    <field name="name">Order Shipped</field>
    <field name="res_model">sale.order</field>
    <field name="default">True</field>
    <field name="description">Order has been shipped to customer</field>
</record>

Use the custom subtype in message_post:

record.message_post(
    body='Your order has been shipped!',
    message_type='comment',
    subtype_xmlid='my_module.mt_order_shipped',
)

Followers can subscribe or unsubscribe from specific subtypes.

Managing Followers

# Subscribe partners as followers
record.message_subscribe(partner_ids=[partner1.id, partner2.id])

# Subscribe with specific subtypes
record.message_subscribe(
    partner_ids=[partner.id],
    subtype_ids=[subtype1.id, subtype2.id]
)

# Unsubscribe
record.message_unsubscribe(partner_ids=[partner.id])

# Get current followers
follower_partners = record.message_follower_ids.mapped('partner_id')

Sending Email via message_post

# Post and send email notification
record.message_post(
    body='Your order is ready for pickup.',
    message_type='comment',
    subtype_xmlid='mail.mt_comment',
    email_from='[email protected]',
    reply_to='[email protected]',
)
# Followers with email notifications enabled will receive the email

Using Email Templates

# Send using a predefined email template
template = self.env.ref('sale.email_template_edi_sale')
record.message_post_with_source(
    template,
    subtype_xmlid='mail.mt_comment',
)

Logging Without Notification

# Log a message without notifying anyone
record.message_post(
    body='System: automatic price adjustment applied.',
    message_type='notification',
    subtype_xmlid='mail.mt_note',
)

# Or use _message_log for pure logging
record._message_log(body='Background process completed.')

Performance Considerations

  • tracking_disable=True in context disables all tracking for bulk operations
  • mail_create_nolog=True skips the creation log message
  • mail_notrack=True skips field change tracking
  • message_post triggers mail sending — use auto_delete=True on templates to clean up
  • For bulk notifications, consider message_notify instead of message_post (no message stored)

Common Patterns

  • Post status change messages in workflow action methods
  • Use internal notes for CRM activity logging
  • Attach reports and documents to relevant records
  • Use custom subtypes to let followers filter notification types
  • Log automated operations with _message_log for audit trails