When to use /api/v2/conversations vs /api/v2/analytics/conversations

I need real-time call data for a supervisor dashboard widget. The docs for /api/v2/conversations show active sessions, but /api/v2/analytics/conversations has the historical metrics I want. Trying to pull current state from analytics returns empty sets. Should I be polling /api/v2/conversations for live status and only hit analytics for wrap-up codes? Or is there a way to get both in one shot?

You are mixing two different data models here. The /api/v2/conversations endpoint is for real-time state management. It tells you what is happening right now. The analytics endpoint is for historical reporting. It aggregates data after the fact. You can’t get live status from analytics because the data isn’t there yet. It’s not a performance issue. It’s a design choice.

If you need a supervisor dashboard that shows both live agents and recent history, you have to poll both endpoints separately. Don’t try to merge them on the server side in real-time. It will break.

Here is how I structure the fetch logic in our WFM tools. We use a simple interval to keep the live data fresh.

async function updateDashboard() {
 // 1. Get live conversations (active, queued, wrapping)
 const liveResponse = await fetch('/api/v2/conversations?filter=state:active,queued,wrapping', {
 headers: { 'Authorization': `Bearer ${token}` }
 });
 const liveData = await liveResponse.json();

 // 2. Get recent analytics (last 15 minutes for trend context)
 const now = new Date();
 const startTime = new Date(now.getTime() - 15 * 60 * 1000).toISOString();
 
 const analyticsPayload = {
 interval: 'PT15M',
 from: startTime,
 to: now.toISOString(),
 groupBy: ['conversation.mediaType'],
 query: {
 select: ['conversation.summary.duration', 'conversation.summary.waitTime']
 }
 };

 const analyticsResponse = await fetch('/api/v2/analytics/conversations/summary', {
 method: 'POST',
 headers: { 
 'Authorization': `Bearer ${token}`,
 'Content-Type': 'application/json' 
 },
 body: JSON.stringify(analyticsPayload)
 });
 const analyticsData = await analyticsResponse.json();

 // Merge in your UI layer
 renderDashboard(liveData, analyticsData);
}

// Poll every 10 seconds for live data
setInterval(updateDashboard, 10000);

The key is to keep the intervals different. Poll the live endpoint frequently. Hit analytics less often. The analytics endpoint has a delay. Usually 5 to 10 minutes. If you query it for “now”, you get nothing. That’s why your set is empty.

Also check your OAuth scopes. You need analytics:read for the second call and conversation:view for the first. If you miss one, the whole thing fails silently or with a 403.

One thing to watch out for. If you have a lot of agents, the analytics query can get heavy. Use the groupBy parameter to limit the dimensions. Don’t ask for every single metric. Just the ones you display. The API will throttle you if you are too aggressive.

I stick to this pattern because it keeps the UI responsive. The live data updates fast. The history updates slower. It feels natural to the user.

Make sure you handle the case where the analytics request times out. It happens. Especially during peak hours. Just show a loading spinner for the history part. Don’t block the live view.