Subscribe to Change Data Capture Event Notifications

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.

    1<?xml version="1.0" encoding="UTF-8"?>
    2<PlatformEventChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    3    <channelType>data</channelType>
    4    <label>External Routing Channel</label>
    5</PlatformEventChannel>
  • Listen for event notifications.

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:

    1<?xml version="1.0" encoding="UTF-8"?> <PlatformEventChannelMember xmlns="http://soap.sforce.com/2006/04/metadata"> <eventChannel>ExternalRoutingChannel__chn</eventChannel> <selectedEntity>PendingServiceRoutingChangeEvent</selectedEntity> <filterExpression>RoutingModel = 'ExternalRouting' AND IsReadyForRouting = true</filterExpression> </PlatformEventChannelMember>

    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:

    1<?xml version="1.0" encoding="UTF-8"?>
    2<PlatformEventChannelMember xmlns="http://soap.sforce.com/2006/04/metadata">
    3    <eventChannel>ExternalRoutingChannel__chn</eventChannel>
    4    <selectedEntity>UserServicePresenceChangeEvent</selectedEntity>
    5    <filterExpression>ChangeEventHeader.changeType = 'CREATE' OR (IsCurrentState = false)</filterExpression>
    6</PlatformEventChannelMember>
  • Listen for event notifications.

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:

    1ExtAgentWorkChangeEvent.platformEventChannelMember
  • 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:

    1<?xml version="1.0" encoding="UTF-8"?>
    2<PlatformEventChannelMember xmlns="http://soap.sforce.com/2006/04/metadata">
    3    <eventChannel>ExternalRoutingChannel__chn</eventChannel>
    4    <selectedEntity>AgentWorkChangeEvent</selectedEntity>
    5    <filterExpression>ChangeEventHeader.changeType = 'UPDATE' AND RoutingModel = 'ExternalRouting'</filterExpression>
    6</PlatformEventChannelMember>

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) {
2
3  	// Iterate through each event message.
4  	for (PendingServiceRoutingChangeEvent event : Trigger.New) {
5    // Get some event header fields
6        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 proceeding
13            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 Ids
18                // Likely want to send this to your routing engine to determine who to route it to.
19            }
20        }
21        
22        // For update operations, we can get a list of changed fields
23        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 used
26            // 		'LastDeclinedAgentSession' is set/updated - indicates work was declined
27            // 		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 Ids
31            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 work
42			// Remember to iterate all record Ids
43        }
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) {
2
3  	// Iterate through each event message.
4  	for (UserServicePresenceChangeEvent event : Trigger.New) {
5    // Get some event header fields
6        EventBus.ChangeEventHeader header = event.ChangeEventHeader;
7        // All the USP records that were updated in this transaction. Would only ever expect 1 per event
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 USP record - indicates either a first login to Omni-Channel or a Presence Status change to a new presence status
13            // 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.)
14
15        }
16        // For update operations, we can get a list of changed fields
17        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 ignored
21            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 UserServicePresence
33            
34        }
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 fields
5        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 work
14            // the external 3rd party application will have created the AgentWork record so already knows about it.
15			
16        }
17        // For update operations, we can get a list of changed fields
18        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 about
22            // to determine that an externally routed work item is completed. If it is internally routed, you will need to query the DB
23            // 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 Ids
26            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 AgentWork
37        }
38  	}
39}