Analytics API interval report 400 Bad Request on custom aggregates

Having some config trouble here…

I am building a custom interval report via the /api/v2/analytics/conversations/aggregates endpoint. My JSON payload specifies a groupBy on user.id and a custom metric, but I receive a 400 Bad Request.

{"interval":"PT1H","groupBy":["user.id"],"metrics":[{"id":"custom.metric.id"}]}

The error states “Invalid metric ID”. How should I structure the metrics array for custom aggregates?

Thank you.

It depends, but generally you’re hitting a scope wall because the Guest API doesn’t support custom aggregate metrics like that.

  • Switch to a standard OAuth token with analytics:reports:read.
  • Use the platformClient.analytics SDK method instead of raw fetch.
  • Verify the metric ID exists in your org’s analytics/metrics endpoint first.
const analytics = platformClient.analytics;
const response = await analytics.conversationsAggregatesPost({
 interval: 'PT1H',
 groupBy: ['user.id'],
 metrics: ['wrapup.code'] // Use a standard metric first to test
});

The quickest way to solve this is to stop guessing the metric ID and explicitly fetch the available custom metrics from your organization before constructing the payload. The 400 Bad Request with “Invalid metric ID” almost always means the string you passed does not exist in the analytics/metrics catalog for your specific org scope.

In my ServiceNow integrations, I always validate the metric existence first. You cannot just invent a custom.metric.id. It has to match the exact id field returned by the GET request to /api/v2/analytics/metrics. If you are using the Node.js SDK, here is the pattern I use to ensure the metric is valid before sending it to the aggregates endpoint:

const { platformClient } = require('@gencloud/genesyscloud');

async function validateAndQueryMetric(metricName) {
 // 1. Fetch all metrics to find the exact ID
 const metrics = await platformClient.analytics.getAnalyticsMetrics();
 const targetMetric = metrics.entities.find(m => m.name === metricName);

 if (!targetMetric) {
 throw new Error(`Metric '${metricName}' not found in org.`);
 }

 // 2. Use the confirmed ID in the query
 const query = {
 interval: "PT1H",
 groupBy: ["user.id"],
 metrics: [{ id: targetMetric.id }]
 };

 return await platformClient.analytics.postAnalyticsConversationsAggregates(query);
}

Also, double-check your OAuth scopes. As mentioned in the previous post, you need analytics:reports:read. If you are using a service account with limited permissions, it might see the metric but fail to aggregate it, or worse, throw a confusing 400 instead of a 403. I’ve seen this happen when the service account lacks access to the specific data sources referenced by the custom metric.

One more thing: ensure the interval aligns with your data retention policy. If you are querying for PT1H intervals on data older than 30 days, you might hit a different error, but the “Invalid metric ID” is strictly a catalog lookup failure. Verify the ID first.

This issue stems from the analytics API rejecting metric IDs that haven’t been explicitly registered in your org’s custom metrics catalog. You need to fetch the valid list first using platformClient.AnalyticsApi.getAnalyticsMetrics() before constructing your aggregate request payload.