Subscribe to Change Data Capture Event Notifications
This Page
All Documentation
Select a subscription method to track Change Data Capture (CDC) event notifications and set up a listener for real-time routing data. Event notifications provide real-time updates to the partner routing application for data synchronization and routing decisions.
You have two options to subscribe to CDC event notifications and listen to case routing data:
Subscribe using Pub/Sub API
Subscribe using Apex Triggers
Subscribe Using Pub/Sub API
With Pub/Sub API you can subscribe to event notifications in an external client to integrate your systems. You can create custom channels to receive event notifications specifically for create, update, and delete events necessary for external routing. To receive all updates about external routing:
Create a custom channel. The new channel’s name must follow this structure:
1ExternalRoutingChannel__chn.platformEventChannel
.
Define the channel members. This channel receives CDC event notifications for objects critical to external routing, such as PendingServiceRouting.
Configure the channel members. This example code provides a sample channel structure definition.
To receive updates only when the records are ready for external routing:
Create a custom channel. Define a channel member record that subscribes to the PendingServiceRoutingChangeEvent. Use this filename structure for the channel member metadata:
1ExtPSRChangeEvent.platformEventChannelMember
Define the channel members. The channel members must have (ExternalRoutingChannel__chn) as the destination channel , and PendingServiceRoutingChangeEvent as the monitored object.
Configure the channel members. This example code provides a sample channel filter logic:
This filter expression ensures that events are only delivered when the routing model is set to ExternalRouting and the IsReadyForRouting field is true.
Listen for event notifications.
To ensure the external routing system receives notificatons when a rep logs in, logs out, or changes status:
Create a custom channel. Define a channel member record that subscribes to the UserServicePresenceChangeEvent. Use this filename structure for the channel member metadata:
1ExtUSPUpdateEvent.platformEventChannelMember
Define the channel members. The channel members must have (ExternalRoutingChannel__chn) as the destination channel , and UserServicePresenceChangeEvent as the monitored object.
Configure the channel members. The channel member record should include this configuration, which is critical for allowing the external routing system to monitor rep availability:
To filter for updates to externally routed AgentWork records:
Create a custom channel. Define a channel member record that subscribes to the AgentWorkChangeEvent. Use this filename for the channel member metadata:
Define the channel members. The channel members must have (ExternalRoutingChannel__chn) as the destination channel , and AgentWorkChangeEvent as the monitored object.
Configure the channel members. The channel member record should include this configuration, which is necessary to monitor the real-time status and lifecycle of work items being managed by the partner routing application:
To receive the Pub/Sub API event notifications, the partner routing application must use the setting: /data/ExternalRoutingChannel_chn.
Subscribe Using Apex Triggers
You can use Apex Triggers to capture event notifications. With Apex Triggers, it isn’t possible to filter for specific events.
To implement a listener for real-time external routing, define an Apex trigger that runs whenever a change event is generated for a PSR record. This example shows an Apex trigger for thePendingServiceRoutingChangeEvent. This trigger identifies and prepares new work for processing by the partner routing application:
1trigger PSR_CDC_Trigger on PendingServiceRoutingChangeEvent (after insert) {23 // Iterate through each event message.4 for (PendingServiceRoutingChangeEvent event : Trigger.New) {5 // Get some event header fields6 EventBus.ChangeEventHeader header = event.ChangeEventHeader;7 // All the PSR records that were updated in this transaction. Could be multiple at once - e.g. when updating due to User going offline with multiple work items assigned.8 List<String> recordIds = event.ChangeEventHeader.getRecordIds();9 System.debug('Received change event for ' + header.entityName + ' for ' + header.recordIds + ' of type ' + header.changeType + ' operation.');10 if (header.changetype == 'CREATE')11 {12 // New PSR record - check model is of type external & isReadyForRouting = true before proceeding13 System.debug('event.IsReadyForRouting: ' + event.IsReadyForRouting);14 System.debug('event.RoutingModel: ' + event.RoutingModel);15 if (event.IsReadyForRouting && event.RoutingModel == 'ExternalRouting')16 {17 // Process new work - remember to iterate all record Ids18 // Likely want to send this to your routing engine to determine who to route it to.19 }20 }2122 // For update operations, we can get a list of changed fields23 else if (header.changetype == 'UPDATE') {24 // Updated record details. Updates we care about are:25 // if 'IsReadyForRouting' updated to true - means it is now ready for routing, and needs an owner. Edge case - this is only where custom routing is used26 // 'LastDeclinedAgentSession' is set/updated - indicates work was declined27 // if 'IsPushed' becomes true - means it was assigned. External system should know about this already as they create the AgentWork so can likely ignore.28 // if 'IsPushed' becomes false - means it was released by assigned Agent. Either due to a decline (manual or push timeout), or they went offline (unavailable).29 System.debug('List of all changed fields:');30 // Process the update based on what changed - remember to iterate all record Ids31 for (String field : header.changedFields) {32 if (null == event.get(field)) {33 System.debug('Deleted field value (set to null): ' + field);34 } else {35 System.debug('Changed field value: ' + field + '. New Value: '36 + event.get(field));37 }38 }39 }40 else if (header.changetype == 'DELETE') {41 // Indicates that either the work was accepted, or is no longer needed (i.e. canceled). Either way, no longer need to track this work42 // Remember to iterate all record Ids43 }44}45}
To allow the partner routing application to monitor agent status changes in real time, define an Apex trigger that executes automatically after an event is inserted into the event stream for the UserServicePresence object. This example shows an Apex trigger for the UserServicePresenceChangeEvent. This trigger monitors the presence status of reps and conveys them to the partner routing application:
1trigger USP_CDC_Trigger on UserServicePresenceChangeEvent (after insert) {23 // Iterate through each event message.4 for (UserServicePresenceChangeEvent event : Trigger.New) {5 // Get some event header fields6 EventBus.ChangeEventHeader header = event.ChangeEventHeader;7 // All the USP records that were updated in this transaction. Would only ever expect 1 per event8 List<String> recordIds = event.ChangeEventHeader.getRecordIds();9 System.debug('Received change event for ' + header.entityName + ' for ' + header.recordIds + ' of type ' + header.changeType + ' operation.');10 if (header.changetype == 'CREATE')11 {12 // New USP record - indicates either a first login to Omni-Channel or a Presence Status change to a new presence status13 // Will want to call out to the 3rd party service to let them know about the state of this user (e.g. Busy/Away v Online etc.)1415 }16 // For update operations, we can get a list of changed fields17 else if (header.changetype == 'UPDATE') {18 // Updated record. Updates we care about are:19 // IsCurrentState - Will get updated to false when the user changes status or logs out.20 // Will also get events about 'AverageCapacity' changes as work is accepted & completed, but isn't relevant for External Routing so can be ignored21 System.debug('List of all changed fields:');22 for (String field : header.changedFields) {23 if (null == event.get(field)) {24 System.debug('Deleted field value (set to null): ' + field);25 } else {26 System.debug('Changed field value: ' + field + '. New Value: '27 + event.get(field));28 }29 }30 }31 else if (header.changetype == 'DELETE') {32 // Should never get here for UserServicePresence3334 }35 }36}
To help synchronize the state of assigned work items between Salesforce and the partner routing application, define an Apex trigger to capture and process CDC events for the AgentWork object. This example shows an Apex trigger for the AgentWorkPresenceChangeEvent. This trigger identifies when a new AgentWork record has been created:
1trigger AgentWork_CDC_Trigger on AgentWorkChangeEvent (after insert) {2 // Iterate through each event message.3 for (AgentWorkChangeEvent event : Trigger.New) {4 // Get some event header fields5 EventBus.ChangeEventHeader header = event.ChangeEventHeader;6 // All the AgentWork records that were updated in this transaction. Could be multiple when updating due to User going offline with multiple work items assigned.7 List<String> recordIds = event.ChangeEventHeader.getRecordIds();8 System.debug('Received change event for ' +9 header.entityName +10 ' for ' + header.recordIds + ' of type ' + header.changeType + ' operation.');11 if (header.changetype == 'CREATE')12 {13 // New AgentWork record - only care about this if mixing External and Omni-Channel routed work, as for external work14 // the external 3rd party application will have created the AgentWork record so already knows about it.1516 }17 // For update operations, we can get a list of changed fields18 else if (header.changetype == 'UPDATE') {19 // Updated record. Updates we care about are:20 // Status - main updates you care about are: 'Opened' - means it was accepted. Any other state update is an end state and will indicate capacity has been released for the user.21 // If mixing External and Internal work, you will get here for both. Need to ensure it is an AgentWork Id that you care about22 // to determine that an externally routed work item is completed. If it is internally routed, you will need to query the DB23 // to get the capacity that the work item consumed and therefore released (both 'CapacityWeight' and 'CapacityPercentage').24 System.debug('List of all changed fields:');25 // Process work - remember to iterate all record Ids26 for (String field : header.changedFields) {27 if (null == event.get(field)) {28 System.debug('Deleted field value (set to null): ' + field);29 } else {30 System.debug('Changed field value: ' + field + '. New Value: '31 + event.get(field));32 }33 }34 }35 else if (header.changetype == 'DELETE') {36 // Shouldn't ever get here for AgentWork37 }38 }39}