NotificationActionHandler Interface

Implement this interface to execute an action on a custom notification.

Namespace

Messaging

NotificationActionHandler Methods

The following are methods for NotificationActionHandler.

executeAction(actionableNotification)

Executes the actionable notification.

Signature

public Messaging.ActionResult executeAction(Messaging.ActionableNotification actionableNotification)

Parameters

actionableNotification
Type: Messaging.ActionableNotification
An actionable custom notification.

Return Value

Type: Messaging.ActionResult

NotificationActionHandler Example Implementation

This is an example implementation of the Messaging.NotificationActionHandler interface.
1global class CaseNotificationActionHandler implements Messaging.NotificationActionHandler {
2    
3    private static final String ACTION_REASSIGN_TO_QUEUE = 'reassignToQueue';
4   private static final String DEFAULT_QUEUE_NAME = 'Queue_Exec'; // Default queue name, can be customized
5    
6    global Messaging.ActionResult executeAction(Messaging.ActionableNotification actionableNotification) {
7        try {
8            String actionIdentifier = actionableNotification.getActionIdentifier();
9            String targetId = actionableNotification.getTargetId();
10            
11            if (String.isBlank(actionIdentifier) || String.isBlank(targetId)) {
12                return new Messaging.ActionResult.Builder()
13                    .withSuccess(false)
14                    .withMessage('Action identifier and target ID are required')
15                    .withErrorCode(Messaging.ActionError.INVALID_ACTION_PARAMETERS)
16                    .build();
17            }
18            
19            // Validate that targetId is a valid Case ID
20            if (!targetId.startsWith('500')) {
21                return new Messaging.ActionResult.Builder()
22                    .withSuccess(false)
23                    .withMessage('Target ID must be a valid Case ID')
24                    .withErrorCode(Messaging.ActionError.INVALID_ACTION_PARAMETERS)
25                    .build();
26            }
27            
28            switch on actionIdentifier {
29                when 'reassignToQueue' {
30                    return reassignCaseToQueue(targetId);
31                }
32                when else {
33                    return new Messaging.ActionResult.Builder()
34                        .withSuccess(false)
35                        .withMessage('Unsupported action identifier: ' + actionIdentifier)
36                        .withErrorCode(Messaging.ActionError.ACTION_NOT_IMPLEMENTED)
37                        .build();
38                }
39            }
40        } catch (Exception e) {
41            return new Messaging.ActionResult.Builder()
42                .withSuccess(false)
43                .withMessage('An unexpected error occurred: ' + e.getMessage())
44                .withErrorCode(Messaging.ActionError.INTERNAL_ERROR)
45                .build();
46        }
47    }
48    
49    private Messaging.ActionResult reassignCaseToQueue(String caseId) {
50        try {
51            // Query the case to ensure it exists
52            List<Case> cases = [SELECT Id, CaseNumber, OwnerId FROM Case WHERE Id = :caseId LIMIT 1];
53            
54            if (cases.isEmpty()) {
55                return new Messaging.ActionResult.Builder()
56                    .withSuccess(false)
57                    .withMessage('Case not found with ID: ' + caseId)
58                    .withErrorCode(Messaging.ActionError.INVALID_ACTION_PARAMETERS)
59                    .build();
60            }
61            
62            Case caseToUpdate = cases[0];
63            
64            // Query for the queue to assign the case to
65            List<Group> queues = [SELECT Id, Name FROM Group WHERE Type = 'Queue' AND DeveloperName = :DEFAULT_QUEUE_NAME LIMIT 1];
66            
67            if (queues.isEmpty()) {
68                return new Messaging.ActionResult.Builder()
69                    .withSuccess(false)
70                    .withMessage('Queue not found: ' + DEFAULT_QUEUE_NAME)
71                    .withErrorCode(Messaging.ActionError.INVALID_STATE)
72                    .build();
73            }
74            
75            // Assign the case to the queue
76            caseToUpdate.OwnerId = queues[0].Id;
77            update caseToUpdate;
78            
79            return new Messaging.ActionResult.Builder()
80                .withSuccess(true)
81                .withMessage('Case ' + caseToUpdate.CaseNumber + ' successfully assigned to queue: ' + DEFAULT_QUEUE_NAME)
82                .build();
83                
84        } catch (DmlException e) {
85            return new Messaging.ActionResult.Builder()
86                .withSuccess(false)
87                .withMessage('Failed to update case: ' + e.getMessage())
88                .withErrorCode(Messaging.ActionError.ACCESS_DENIED)
89                .build();
90        } catch (Exception e) {
91            return new Messaging.ActionResult.Builder()
92                .withSuccess(false)
93                .withMessage('Error reassigning case to queue: ' + e.getMessage())
94                .withErrorCode(Messaging.ActionError.INTERNAL_ERROR)
95                .build();
96        }
97    }
98}

The following example tests the implementation:

1@IsTest
2global class TestNotificationActionHandler {
3    @IsTest
4    static void testActionHandler() {
5
6        //Set up the data, for example creating a case
7        Case newCase = new Case(
8            Subject = 'Important Case',
9            Status = 'New',
10            Priority = 'High'
11        );
12        insert newCase;
13
14        //Set up Actionable Notification data
15        Messaging.ActionableNotification notification =
16            new Messaging.ActionableNotification.Builder()
17                .withNotificationTypeId('0MLXXXXXXXXXXXX4AC')
18                .withActionIdentifier('testAction')
19                .withRecipientId(UserInfo.getUserId())
20                .withSenderId(UserInfo.getUserId())
21                .withTargetId(newCase.Id)
22                .withTargetPageRef('/lightning/r/Case/' + newCase.Id + '/view')
23                .build();
24
25
26        Messaging.ActionResult result = Test.testNotificationActionHandler(new CaseNotificationActionHandler(), notification);
27
28        //Insert assert statements here to verify your action
29    }
30}