---
schema_version: "1"
id: 81f9a9793b8e3972178e0b3335705df574c9e043dc30f680422b58a5c728b45b
title: Moderate Chatter Private Messages with Triggers
collection: legacydeveloper
version: current
locale: en-us
portal_url: https://docs.salesforce.com/legacydeveloper/current/en-us/apexcode/apex_chattermessage_triggers.md
canonical_url: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_chattermessage_triggers.htm
source_site: developer.salesforce.com
content_type: documentation
content_hash: 6b3877fec9b55bc9a34bbfb19f2231faf7d456d941da37052dfe670d9e31816f
description: Write a trigger for ChatterMessage to automate the moderation of private messages in an org or Experience Cloud site. Use triggers to ensure that messages conform to your company’s messaging policies and don’t contain blocklisted words.
---
# Moderate Chatter Private Messages with Triggers

Write a trigger for ChatterMessage to automate the moderation of private messages in an org or Experience Cloud site. Use triggers to ensure that messages conform to your company’s messaging policies and don’t contain blocklisted words.

<table class="editionTable" border="0" summary="Salesforce editions needed for the documented functionality" width="100%"><tbody class="tbody"><tr><td class="subHead">Available in: Salesforce Classic</td></tr><tr><td class="subHead">Available in: <span class="ph" id="Chatter_message_trigger_editions_text"><a name="Chatter_message_trigger_editions_text"></a><strong class="ph b">Enterprise</strong>, <strong class="ph b">Performance</strong>, <strong class="ph b">Unlimited</strong>, and <strong class="ph b">Developer</strong> Editions</span></td></tr></tbody></table>

  

<table class="permTable sort_table" border="0" summary="Permissions needed for the documented functionality" width="100%">


          <tbody class="tbody">
            <tr>
              <td class="permRowLeft">User permissions needed to save Apex triggers for ChatterMessage:</td>

              <td class="permRowRight">Author Apex <p class="p">AND</p>
                <p class="p">Manage Chatter Messages and
                  Direct Messages</p>
              </td>

            </tr>

          </tbody>

        </table>

Write an Apex _before insert_ trigger to review the private message body and information about the sender. You can add validation messages to the record or the Body field, which causes the message to fail and an error to be returned to the user.

Although you can create an _after insert_ trigger, ChatterMessage is not updatable, and consequently any _after insert_ trigger that modifies ChatterMessage will fail at run time with an appropriate error message.

To create a trigger for private messages from Setup, enter ChatterMessage Triggers in the Quick Find box, then select **ChatterMessage Triggers**. Alternatively, you can create a trigger from the Developer Console by clicking **File** | **New** | **Apex Trigger** and selecting ChatterMessage from the **sObject** drop-down list.

This table lists the fields that are exposed on ChatterMessage.

<table class="featureTable sort_table" summary="">
          <caption><span class="tablecap">Table 1. Available Fields in ChatterMessage</span></caption>



          <thead class="thead sorted" align="left">
            <tr>
              <th class="featureTableHeader  " id="d101167e164">Field</th>

              <th class="featureTableHeader  " id="d101167e167">Apex Data Type</th>

              <th class="featureTableHeader  " id="d101167e170">Description</th>

            </tr>

          </thead>

          <tbody class="tbody">
            <tr>
              <td class="entry" headers="d101167e164" data-title="Field">Id</td>

              <td class="entry" headers="d101167e167" data-title="Apex Data Type">ID</td>

              <td class="entry" headers="d101167e170" data-title="Description">Unique identifier for the Chatter message</td>

            </tr>

            <tr>
              <td class="entry" headers="d101167e164" data-title="Field">Body</td>

              <td class="entry" headers="d101167e167" data-title="Apex Data Type">String</td>

              <td class="entry" headers="d101167e170" data-title="Description">Body of the Chatter message as posted by the sender</td>

            </tr>

            <tr>
              <td class="entry" headers="d101167e164" data-title="Field">SenderId</td>

              <td class="entry" headers="d101167e167" data-title="Apex Data Type">ID</td>

              <td class="entry" headers="d101167e170" data-title="Description">User ID of the sender</td>

            </tr>

            <tr>
              <td class="entry" headers="d101167e164" data-title="Field">SentDate</td>

              <td class="entry" headers="d101167e167" data-title="Apex Data Type">DateTime</td>

              <td class="entry" headers="d101167e170" data-title="Description">Date and time that the message was sent</td>

            </tr>

            <tr>
              <td class="entry" headers="d101167e164" data-title="Field">SendingNetworkId</td>

              <td class="entry" headers="d101167e167" data-title="Apex Data Type">ID</td>

              <td class="entry" headers="d101167e170" data-title="Description">Network (site) in which the message was sent. <p class="p">This field is visible only if
                  digital experiences is enabled and Private Messages is enabled in at least one
                  site. </p>
              </td>

            </tr>

          </tbody>

        </table>

This example shows a _before insert_ trigger on ChatterMessage that is used to review each new message. This trigger calls a class method, moderator.review(), to review each new message before it is inserted.

trigger PrivateMessageModerationTrigger on ChatterMessage (before insert) {
    ChatterMessage\[\] messages = Trigger.new;
    
    // Instantiate the Message Moderator using the factory method
    MessageModerator moderator = MessageModerator.getInstance();
    
    for (ChatterMessage currentMessage : messages) {
        moderator.review(currentMessage);    
    }
}

If a message violates your policy, for example when the message body contains blocklisted words, you can prevent the message from being sent by calling the Apex addError method. You can call addError to add a custom error message on a field or on the entire message. The following snippet shows a portion of the reviewContent method that adds an error to the message Body field.

          if (proposedMsg.contains(nextBlockListedWord)) {
             theMessage.Body.addError(
                 'This message does not conform to the acceptable use policy');
             System.debug('moderation flagged message with word: ' 
                 + nextBlockListedWord);
             problemsFound=true;
             break;
          }

The following is the full MessageModerator class, which contains methods for reviewing the sender and the content of messages. Part of the code in this class has been deleted for brevity.

public with sharing class MessageModerator {
   private Static List<String\> blocklistedWords=null;
   private Static MessageModerator instance=null;
   
   /\*\*
     Overall review includes checking the content of the message,
     and validating that the sender is allowed to send messages.
   \*\*/
   public void review(ChatterMessage theMessage) {
    reviewContent(theMessage);
    reviewSender(theMessage);
   }
   
   /\*\*
     This method is used to review the content of the message. If the content
     is unacceptable, field level error(s) are added.
   \*\*/
   public void reviewContent(ChatterMessage theMessage) {
      // Forcing to lower case for matching
      String proposedMsg=theMessage.Body.toLowerCase();  
      boolean problemsFound=false; // Assume it's acceptable
      // Iterate through the blocklist looking for matches
      for (String nextBlockListedWord : blocklistedWords) {
          if (proposedMsg.contains(nextBlockListedWord)) {
             theMessage.Body.addError(
                 'This message does not conform to the acceptable use policy');
             System.debug('moderation flagged message with word: ' 
                 + nextBlockListedWord);
             problemsFound=true;
             break;
          }
         }
         
       // For demo purposes, we're going to add a "seal of approval" to the 
       // message body which is visible.
       if (!problemsFound) {
         theMessage.Body = theMessage.Body + 
             ' \*\*\* approved, meets conduct guidelines';
       }
         
    }
   
   /\*\*
     Is the sender allowed to send messages in this context?
     -- Moderators -- always allowed to send
     -- Internal Members -- always allowed to send
     -- Site Members -- in general only allowed to send if they have 
           a sufficient Reputation
     -- Site Members -- with insufficient reputation may message the 
           moderator(s)
   \*\*/
   public void reviewSender(ChatterMessage theMessage) {
      // Are we in a Site Context?
      boolean isSiteContext = (theMessage.SendingNetworkId != null);
 
      // Get the User
      User sendingUser = \[SELECT Id, Name, UserType, IsPortalEnabled 
                          FROM User where Id = :theMessage.SenderId  WITH USER\_MODE\];  
      // ...          
   }   
   
   /\*\*
     Enforce a singleton pattern to improve performance
   \*\*/
   public static MessageModerator getInstance() {
     if (instance==null) {
        instance = new MessageModerator();
     }
     return instance;
   }
   

   /\*\*
     Default contructor is private to prevent others from instantiating this class 
     without using the factory.
     Initializes the static members.
   \*\*/
   private MessageModerator() {
      initializeBlockList();
   }
   /\*\* 
     Helper method that does the "heavy lifting" to load up the dictionaries 
     from the database.  
     Should only run once to initialize the static member which is used for 
     subsequent validations.
   \*\*/
   private void initializeBlockList() {
      if (blocklistedWords==null) {
          // Fill list of blocklisted words
          // ...
      }
   }
}