Report on Abandoned Conversations with Salesforce Channels
One important piece of information for Service centers is related to abandoned conversations – that is how many people that really wanted to talk to you, gave up while they were waiting for a rep to talk to them. This primarily applies to real-time communication channels like web chat and voice calls, where a customer may abandon the interaction while waiting in queue before being connected to a service rep. This could also include after being handed off from a Bot or AI Agent conversation, to connect with a human.
Why is Abandoned Reporting Important?
This is a hugely important metric, but you probably know that already, that’s why you’re here. But just as a reminder, here are a few key reasons why:
- Understanding Customer Frustration: High abandonment rates can indicate long wait times or other issues causing customer frustration.
- Optimizing Resource Allocation: Tracking abandonments helps determine staffing levels and potential bottlenecks in customer service.
- Improving Customer Experience: Identifying reasons for abandonments leads to process improvements and better overall customer experience.
Great – we know it’s a gap, we know it’s important, how do we solve it?
How to report on Abandoned
First – we are planning an out of the box solution for this (on Enhanced Omni only) where we will write a new ‘AgentWork’ record, with the ‘Status’ of ‘Abandoned’ – but ahead of that change coming in the Winter ‘26 release (October 2025), here are some ways to report on it.
I will go through a simple option, followed by a more complex one the captures more scenarios, but is a lot more difficult to implement and track. The scope of this is to solve it for Enhanced Chat (formally known as Messaging for In-app and Web) and Voice calls.
Step 1 – Add new fields to the objects
Both options involve adding these 3 fields to both the ‘MessagingSession’ and ‘VoiceCall’ records:
- Num Times queued (Number) – default 0
- Num Agent accepts (Number) – default 0
- Was abandoned (Formula (Checkbox)). This formula compares the two values above, plus that the conversation was ended by the end customer/visitor, as opposed to an agent or the application:
- For MessagingSession:
ISPICKVAL(EndedByType, "End User") && ( Num_Times_queued__c > Num_agent_accepts__c) - For VoiceCall:
DisconnectReason = "CUSTOMER_DISCONNECT" && ( Num_Times_queued__c > Num_Agent_accepts__c)
- For MessagingSession:
Step 2 – Update on each accept
Now we need to increment the two counters in the appropriate places – for the number of accepts that is relatively straightforward:
- Create a Record Triggered Flow on ‘AgentWork’, and configure the start conditions in this way:
- Trigger is ‘A record is created or updated’
- Trigger condition for ‘Status’ equals ‘Opened’ and ‘Bot ID’ is Null equals ‘true’ (ignore any time the bot accepts work)
- Ensure you select ‘Only when a record is updated to meet the condition requirements’
- Add a decision node to check the type of work that was accepted. That decision node should check the ‘Starts With’ of the ‘WorkItemId’ matches the appropriate prefix for the objects we care about:
- MessagingSession: 0Mw
- VoiceCall: 0LQ
- Within each outcome, you’ll add 3 elements
- Get Records – load the appropriate record
- Assignment – ‘Add’ 1 to the ‘Num agent accepts’ field
- Update Records – save the updated value
Step 3 – Update on each routing request
Now we get to the trickier part – updating the record to count each time a routing request was made for the record. For efficiency and ensuring that the work gets delivered, we prevent a lot of automation on the ‘Pending Service Routing’ object, as any errors could cause systems to get out of sync, calls dropped or work not actioned as expected. However, that creates some issues when trying to catch a new routing request of a record – so I have 1 simple, and 1 more complex solution for this.
Simple solution
This involves simply updating the record each time you attempt to route it to a human service rep (don’t do this when routing to a bot or Agentforce Agent) – in the most basic case, this is incrementing the counter within the Omni Flow that you route the object in, similar to the screen shot on the right (this one assumes we’ve previously loaded the Voice Call record in the Flow).
Assuming you know all the places that you are routing Voice Calls and Messaging Sessions, and when you transfer between users you are using an Omni Flow, then this should lead to an accurate count of routing requests, and therefore abandonments.
More complex solution
A more complex solution is to use ‘Change Data Capture’. You can enable this in Setup, and add the ‘Pending Service Routing’ object to the ‘Selected Entities’ – you can only enable 5 entitles without purchasing an add-on for more. This will then allow you access to the ‘PendingServiceRoutingChangeEvent’, which you can add an Apex Trigger to – yep we’re going to need to write Apex for this one, hence the added complexity.
trigger PSR_CDC_Trigger on PendingServiceRoutingChangeEvent (after insert) {
// Iterate through each event message.
for (PendingServiceRoutingChangeEvent event : Trigger.New) {
// Get some event header fields
EventBus.ChangeEventHeader header = event.ChangeEventHeader;
List<String> recordIds = event.ChangeEventHeader.getRecordIds();
System.debug('Received change event for ' + header.entityName + ' for ' + header.recordIds + ' of type ' + header.changeType + ' operation.');
if (header.changetype == 'CREATE')
{
// New PSR record created - check the object type for the work item
System.debug('event.WorkItemId: ' + event.WorkItemId);
// Ensure we aren't routing to a Bot/AI Agent
if (event.BotId == null) {
for (String recordId : recordIds) {
if (event.WorkItemId.getSobjectType() == Schema.VoiceCall.SObjectType)
{
// VoiceCall - load record and increment num times queued.
System.debug('PSR for a Voice Call created.');
VoiceCall call = [SELECT Id, Num_Times_queued__c from VoiceCall where Id = :event.WorkItemId LIMIT 1];
call.Num_Times_queued__c++;
update call;
}
else if (event.WorkItemId.getSobjectType() == Schema.MessagingSession.SObjectType)
{
// MessagingSession - load record and increment num times queued.
System.debug('PSR for a Messaging Session created.');
MessagingSession ms = [SELECT Id, Num_Times_queued__c from MessagingSession where Id = :event.WorkItemId LIMIT 1];
ms.Num_Times_queued__c++;
update ms;
}
}
}
}
}
}
This is an example of the Apex Trigger that would increment the value – you can copy and paste this into the ‘Developer Console’ after going to ‘File’ → ‘New’ → ‘Apex Trigger’. Name it ‘PSR_CDC_Trigger’, and select the ‘PendingServiceRoutingChangeEvent’ object.
That should capture all creations of PSR records, and increment the routing requests as appropriate.
Step 4 – Create the report
To keep this post a reasonable length, I’m not going to go into details on how to create reports, but it should be pretty straightforward to create a report for each object to report on this, and there is lots of helpful content out there. Follow the usual reporting steps to create a ‘Report Type’, ensuring it has the ‘Was Abandoned’ field included, and then create a report using the report type. You will need to create two separate reports, 1 per object – but I suggest dropping them both onto a single dashboard. You likely also want an abandonment rate – you can use a summary level formula to calculate this in standard reporting for a time period – below is an example of how you might do this, if you grouped by date:

Limitations
There will be a few limitations to this solution, particularly with regards to the Voice channel:
- The ‘Disconnected Reason’ needs to come from the telephony system. With the ‘AWS Connect’ integration, this can take a while after the call disconnects before it is populated. It may also be populated differently (or not at all) by other telephony providers.
- To use the Omni Flow solution, you will need to be calling Omni-Flow from your telephony provider.
- Transfers may need some thought, and you’ll need to decide if you want to count conversations that drop when being transferred between teams or service reps as abandoned or not.
- Only unauthenticated Messaging Sessions can be ended/abandoned, so may not totally cover all abandoned scenarios related to web chats.
- There will be some edge cases, error scenarios etc. that may not be captured by this setup
Conclusion
While out of the box Salesforce does not track abandoned yet, this custom solution can fill the gap. By implementing appropriate methods and metrics, organizations can effectively measure and analyze abandonments in both Messaging and Voice channels, leading to improved customer service and operational efficiency. Let me know how you get on, and if there are any issues or remaining gaps this misses.


