Analytics API Service Level calculation discrepancy with interval data

Trying to calculate Service Level (SL) percentage using the raw /api/v2/analytics/conversations/queries/summary endpoint. The goal is to get the exact same SL% shown in the Performance app.

I’m fetching interval data for a specific queue. The payload looks like this:

{
 "interval": "5m",
 "dateFrom": "2023-10-27T00:00:00Z",
 "dateTo": "2023-10-27T23:59:59Z",
 "groupBy": "queueId",
 "filter": {
 "queueId": "eq:{{queue_id}}"
 },
 "metrics": [
 "conversation/count",
 "conversation/wait",
 "conversation/abandoned"
 ]
}

The response returns a list of intervals. For each interval, I have conversation.count and conversation.abandoned. I don’t see a direct serviceLevel metric in the response. The usual formula is (Answered within threshold / Total Offered) * 100.

My current logic:

  1. Total Offered = conversation.count (answered) + conversation.abandoned.
  2. Answered within SL = I’m assuming all conversation.count are within SL because I can’t filter by wait time in the summary query easily.

The numbers don’t match the UI. The UI shows 85% SL. My calculation shows 92%. I’m missing something about how CXone counts ‘Offered’ or handles answered calls that exceeded the threshold. Is there a specific metric I’m missing? Or do I need to use the detail endpoint instead of summary to get individual wait times?

The issue usually isn’t the raw metric values themselves but how you are aggregating them across intervals. The Performance app calculates Service Level as a weighted average, not a simple arithmetic mean of the interval percentages. If you just sum the serviceLevelPercent from each 5-minute bucket and divide by the count, you’ll get a number that doesn’t match the UI, especially if some intervals had zero volume.

You need to weight each interval’s SL by the number of conversations handled in that same interval. Here is the logic you should apply after fetching the data:

  1. Extract totalHandled and serviceLevelPercent for every interval where totalHandled > 0.
  2. Calculate weightedSL = (serviceLevelPercent / 100) * totalHandled for each interval.
  3. Sum all weightedSL values.
  4. Sum all totalHandled values.
  5. Final SL% = (Sum of weightedSL / Sum of totalHandled) * 100.

Here is a quick Python to illustrate the aggregation:

def calculate_weighted_sl(intervals):
 total_weighted_sl = 0
 total_handled = 0
 
 for interval in intervals:
 handled = interval['metrics']['totalHandled']
 sl_percent = interval['metrics']['serviceLevelPercent']
 
 if handled > 0:
 # Convert percent to decimal for weighting, then back later
 total_weighted_sl += (sl_percent / 100) * handled
 total_handled += handled
 
 if total_handled == 0:
 return 0
 
 return (total_weighted_sl / total_handled) * 100

Make sure you are filtering out intervals with zero handled calls before this calculation. An interval with 0 calls and 0% SL will skew the average down if included in a simple mean. Also, check your dateFrom and dateTo boundaries. The API returns data in UTC, so if your London timezone shifts a conversation’s end time into the next day, it might fall outside your query window or into a different interval bucket than expected. This misalignment often causes small discrepancies in daily totals.

Double check the serviceLevelGoal metric in the response too. If the goal changes mid-day, the calculation might treat intervals differently depending on whether they met the specific threshold for that moment. It’s a bit tedious to get right manually, but this weighted approach is the only way to replicate the dashboard exactly.