For Developers

Track first response time on Cases using Entitlements

4 min read
CloudAnswers photo

When working with Cases in Salesforce, a common question is "How much time does it take our team to respond to incoming customer cases?" Often, customers have specific SLAs on required response times, and team managers want to understand how efficient their team is at addressing new issues. In this blog post, we are going to cover how we can use Entitlement Management with Cases to track this metric.

Here's a step-by-step walk through of how to set this up in Salesforce:

Enable Entitlement management

Go to Setup -> Entitlement Management -> Entitlement Settings

On this page, you will see a checkbox called Enable Entitlement Management. Check this and click Save. Once Entitlement Management is enabled, you will see other options on this page like Entitlement-Related Lookup Filters on Case Fields, Milestone Feed Items, Milestone Tracker Time Settings and Milestone Time Settings. These options aren't relevant to this setup, but you can learn more about them from Salesforce's Help Docs.

Setup milestones and entitlement process

Now we need to create the first response Milestone, create the Entitlement process and add the milestone to the Entitlement process.

Go to Setup -> Entitlement Management -> Milestones -> Click New Milestone button

Screen Shot 2018-05-21 at 12.51.45 pm.png

Set Name = First Response, Recurrence Type = No Recurrence and click Save to create the new milestone.

Go to Setup -> Entitlement Management -> Entitlement Processes, create a new Entitlement process for Case. We are going to use a basic Entitlement process to demonstrate here:

Screen Shot 2018-05-21 at 12.58.20 pm.png

Next, add the Milestone to an Entitlement process.

In this example, we are assuming that the first response should be sent within 4 hours of the arrival of the case. So, we have added a Milestone to our Entitlement process with the number of Minutes to Complete Milestone value.

Screen Shot 2018-05-21 at 1.01.53 pm.png

Setup trigger to assign entitlement process to incoming cases

If you are using Email-to-Case functionality, the Entitlement from Account won’t be automatically assigned to the incoming cases. Therefore, we need to add a trigger to associate an active Entitlement from an Account to the incoming Case.

Here is the sample code for this:

// trigger on Case object

trigger CaseTrigger on Case (before insert, before update) { if(Trigger.isBefore){ if(Trigger.isInsert || Trigger.isUpdate){ CaseEntitlementHelper.applyEntitlements(; } } }

// helper class

public with sharing class CaseEntitlementHelper {

/** * Apply entitlements from contact or account. */ public static void applyEntitlements(List cases){ List contactIds = new List(); List acctIds = new List(); for (Case c : cases){ if (c.EntitlementId == null && c.ContactId != null && c.AccountId != null){ contactIds.add(c.ContactId); acctIds.add(c.AccountId); } } if(!contactIds.isEmpty() || !acctIds.isEmpty()){ List entitlementContacts = [ select e.EntitlementId, e.ContactId, e.Entitlement.AssetId from EntitlementContact e where e.ContactId in :contactIds and e.Entitlement.EndDate >= TODAY and e.Entitlement.StartDate <= TODAY ]; if(!entitlementContacts.isEmpty()){ for(Case c : cases){ if(c.EntitlementId == null && c.ContactId != null){ for(EntitlementContact ec : entitlementContacts){ if(ec.ContactId==c.ContactId){ c.EntitlementId = ec.EntitlementId; if(c.AssetId==null && ec.Entitlement.AssetId!=null) c.AssetId = ec.Entitlement.AssetId; break; } } } } } else { List entitlements = [ select e.StartDate, e.Id, e.EndDate, e.AccountId, e.AssetId from Entitlement e where e.AccountId in :acctIds and e.EndDate >= TODAY and e.StartDate <= TODAY ]; if(!entitlements.isEmpty()){ for(Case c : cases){ if(c.EntitlementId == null && c.AccountId != null){ for(Entitlement e : entitlements){ if(e.AccountId == c.AccountId){ c.EntitlementId = e.Id; if(c.AssetId==null && e.AssetId!=null) c.AssetId=e.AssetId; break; } } } } } } } } }

Setup apex triggers to mark milestones as completed

We are going to use triggers on EmailMessage and CaseComment to mark the Milestone when it is completed.

Here is the sample code:

// email message trigger trigger EmailMessageTrigger on EmailMessage (before insert) { if(Trigger.isInsert || Trigger.isAfter){ if (UserInfo.getUserType() == 'Standard'){ // complete milestone on valid first response EmailMessageHelper.handleFirstResponse(; } } }

// email message trigger helper class public with sharing class EmailMessageHelper { public static void handleFirstResponse(List emailMessages){ DateTime completionDate =; Map<Id, String> emIds = new Map<Id, String>(); for (EmailMessage em : emailMessages){ if(em.Incoming == false) emIds.put(em.ParentId, em.ToAddress); } if(emIds.isEmpty() == false){ Set emCaseIds = new Set(); emCaseIds = emIds.keySet(); List caseList = [ select Id, ContactId, Contact.Email, OwnerId, Status, EntitlementId, SlaStartDate, SlaExitDate from Case where Id IN :emCaseIds ]; if(!caseList.isEmpty()){ List casesToUpdate = new List(); for (Case caseObj : caseList) { // consider an outbound email to the contact on the case a valid first response if (emIds.get(caseObj.Id) == caseObj.Contact.Email && caseObj.EntitlementId != null && caseObj.SlaStartDate <= completionDate && caseObj.SlaStartDate != null && caseObj.SlaExitDate == null) { casesToUpdate.add(caseObj.Id); } } if(!casesToUpdate.isEmpty()) { MilestoneHelper.completeMilestone(casesToUpdate, 'First Response', completionDate); } } } } }

// case comment trigger trigger CaseCommentTrigger on CaseComment (after insert) { if(UserInfo.getUserType() == 'Standard'){ CaseCommentHelper.handleFirstResponse(; } }

// case comment trigger helper class public with sharing class CaseCommentHelper { public static void handleFirstResponse(List comments){ DateTime completionDate =; List caseIds = new List(); for (CaseComment cc : comments){ // Only public comments qualify if(cc.IsPublished == true) caseIds.add(cc.ParentId); } if(caseIds.isEmpty() == false){ List caseList = [ select Id, ContactId, Contact.Email, OwnerId, Status, EntitlementId, SlaStartDate, SlaExitDate from Case where Id IN :caseIds ]; if(caseList.isEmpty() == false){ List updateCases = new List(); for (Case caseObj : caseList) { if (caseObj.EntitlementId != null && caseObj.SlaStartDate <= completionDate && caseObj.SlaStartDate != null && caseObj.SlaExitDate == null) { updateCases.add(caseObj.Id); } } if(updateCases.isEmpty() == false) { MilestoneHelper.completeMilestone(updateCases, First Response', completionDate); } } } } }

// milestone helper class public with sharing class MilestoneHelper {

public static void completeMilestone(List caseIds, String milestoneName, DateTime completionDate) { List toUpdate = [ select Id, CompletionDate from CaseMilestone cm where CaseId IN :caseIds and cm.MilestoneType.Name = :milestoneName and CompletionDate = null limit 1 ]; if(!toUpdate.isEmpty()){ for (CaseMilestone cm : toUpdate){ cm.CompletionDate = completionDate; } update toUpdate; } } }

Create a report to view milestone violations

The last step is to create a report on Cases with Case Milestones to view a list of cases which didn’t meet the first response milestone. Now you can see the cases that are overdue and flag them for followup easily.

Questions? Ask us in the comments!

Happy coding!

CloudAnswers photo

About CloudAnswers

Salesforce apps, powerful components, custom development, and consulting. Our experienced team helps you to create and modify workflow processes in salesforce.

Related Articles

For Developers

Designing User Security and Visibility in Salesforce

Trust and security are at the top of Salesforce's priority list. The platform has everything you need if you're looking to construct a robust user security paradigm. However, this security approach has flaws that an attacker can exploit to gain access to your data. The Salesforce Architect has the duty to ensure that these features are set up correctly.

March 16, 2022

7 Min Read

For Developers

Batch Apex Error Event - CloudAnswers Hackathon

A hackathon is an event usually put together by a tech organization. The event brings programmers together over a specific period to collaborate on a project.

June 28, 2021

5 Min Read

For Developers

Save DOM Element As Image Attachment In Salesforce

Use a dom-to-image Javascript library that can turn arbitrary DOM nodes into a vector (SVG) or raster (PNG or JPEG) image in Salesforce.

April 8, 2021

3 Min Read