Analytics Conversations Aggregates query returning empty results for custom intervals

Is it possible to filter by custom time intervals using the Analytics Conversations Aggregates endpoint? I am building a Chrome extension that overlays real-time queue metrics for our Toronto-based BPO team. The goal is to fetch conversation counts grouped by specific interval buckets (e.g., 15-minute chunks) rather than relying on pre-built reports.

I am sending a POST request to /api/v2/analytics/conversations/aggregates. The payload looks correct, but the response body always returns an empty array []. I have verified that the oauthToken is valid and has the analytics:reports:read scope.

Here is the JSON body I am sending:

{
 "interval": "PT15M",
 "metrics": ["conversationCount"],
 "groupBys": ["conversationType"],
 "filter": {
 "type": "and",
 "predicates": [
 {
 "type": "string",
 "path": "conversationType",
 "operator": "is",
 "value": "CALL"
 }
 ]
 }
}

Environment details:

  • Endpoint: POST /api/v2/analytics/conversations/aggregates
  • SDK: Custom fetch wrapper in Chrome Extension
  • Timezone: America/Toronto
  • Error: No HTTP error, but data array is empty

I suspect the filter syntax or the interval format is incorrect. The documentation mentions ISO 8601 durations, so PT15M should be valid. What is the correct payload structure to get non-empty results?

If I recall correctly, the interval parameter requires strict ISO 8601 duration formatting. Ensure your from and to timestamps align with your interval boundaries to avoid partial bucket aggregation errors in the response payload.

{
 "interval": "PT15M",
 "from": "2024-05-20T00:00:00Z",
 "to": "2024-05-20T23:59:59Z"
}

Note: Verify the granularity field matches your interval duration to prevent empty result sets.

Make sure you align your interval boundaries precisely. The suggestion above is correct regarding ISO 8601, but the Analytics engine is brutal about partial buckets. If your from timestamp is 2024-05-20T00:15:00Z and you request PT15M, the first bucket will be empty because the data aggregation starts at the hour mark. You must start at 00:00:00 for hourly or 00:00:00/00:15:00 etc. for sub-hourly intervals.

  1. Set from to the start of the desired interval block.
  2. Set to to the end of the block.
  3. Ensure interval matches the bucket size exactly.
{
 "interval": "PT15M",
 "from": "2024-05-20T00:00:00Z",
 "to": "2024-05-20T23:45:00Z",
 "group_by": ["interval"],
 "select": ["conversationCount"]
}

I see this often in webhook-driven dashboards. The server returns empty arrays when the math is off by even a second. Check your timezone offset. Berlin is UTC+2. If your backend generates timestamps in local time but the API expects UTC, you will miss data. Use UTC everywhere in the payload.

This looks like a timezone alignment issue common in desktop app integrations.

The analytics engine aggregates in UTC. Your Electron renderer likely sends local time strings via new Date().toISOString(), which is correct, but if you pass local offsets like -05:00 without converting to pure Z, the bucket boundaries shift. Ensure your from and to are strictly UTC Z suffixes.

"from": "2024-05-20T00:00:00.000Z",
"to": "2024-05-20T23:59:59.999Z",
"interval": "PT15M"

Check your divisionId filter too. If it’s null, the SDK might default to all_divisions but your user context lacks permission, returning empty arrays instead of 403s.

The root of the issue is likely a timezone mismatch in your Chrome extension’s date handling. The analytics engine strictly uses UTC for bucket aggregation. If your frontend constructs timestamps using local system time without explicit conversion to UTC, the bucket boundaries will misalign, resulting in empty results. I recently hit this with a dashboard script in Lagos. Using Python’s datetime module to force UTC conversion before sending the payload resolves this cleanly.

from datetime import datetime, timezone

# Force UTC to avoid local offset issues
now = datetime.now(timezone.utc)
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
end = now.replace(hour=23, minute=59, second=59, microsecond=999999)

payload = {
 "interval": "PT15M",
 "from": start.isoformat(),
 "to": end.isoformat()
}

Ensure your Chrome extension sends the from and to fields with the +00:00 or Z suffix. The API rejects local offsets for aggregation boundaries. This approach guarantees precise bucket alignment.