Skip to main content

Response format

Every successful response has the same outer shape:
{
  "data": {
    "dataPoints": [
      {
        "startTimestamp": "2026-04-29T12:00:00.000Z",
        "endTimestamp": "2026-04-29T13:00:00.000Z",
        "total": 1234,
        "<aggregationKey>": 0,
        "<groupByKey>": "value-or-null"
      }
    ]
  }
}
  • total: implicit COUNT(*) for the row. Always present.
  • <aggregationKey>: one key per requested aggregation. The key is <type><Column> in camelCase (e.g. sumPotentialCostSavings, p99CacheLookupLatencyMs, sumCacheReadInputTokens).
  • <groupByKey>: one key per groupBy entry. The key is the lowerCamelCase form of the underlying column. Two special mappings:
    • userEmail and virtualaccount both map to createdBySubjectSlug in the response.
    • team maps to team (the value is a single unnested scalar, not an array). All other groupBy keys preserve their lowerCamelCase name.
  • startTimestamp: present only for timeseries responses. Bucket start as an ISO 8601 timestamp string (e.g. "2026-04-29T12:00:00.000Z"). Distribution responses omit it.
  • endTimestamp: present only for timeseries responses. Bucket end as an ISO 8601 timestamp string, equal to the next bucket’s startTimestamp (e.g. "2026-04-29T13:00:00.000Z"). Distribution responses omit it.
Virtual-model rows surface under the virtualModelName key in the response (not virtualModel), because the response key is the lowerCamelCase of the underlying database column.

Distribution response example

{
  "data": {
    "dataPoints": [
      {
        "cacheType": "semantic",
        "cacheNamespace": "prod-chat",
        "total": 4200,
        "sumPotentialCostSavings": 12.34,
        "sumCacheReadInputTokens": 480000,
        "p50CacheLookupLatencyMs": 8.2
      },
      {
        "cacheType": "semantic",
        "cacheNamespace": "prod-search",
        "total": 1850,
        "sumPotentialCostSavings": 4.81,
        "sumCacheReadInputTokens": 210000,
        "p50CacheLookupLatencyMs": 11.4
      }
    ]
  }
}

Timeseries response example

{
  "data": {
    "dataPoints": [
      {
        "startTimestamp": "2026-04-21T00:00:00.000Z",
        "endTimestamp": "2026-04-21T01:00:00.000Z",
        "cacheType": "semantic",
        "total": 180,
        "sumPotentialCostSavings": 0.52,
        "p99CacheLookupLatencyMs": 22.0
      },
      {
        "startTimestamp": "2026-04-21T01:00:00.000Z",
        "endTimestamp": "2026-04-21T02:00:00.000Z",
        "cacheType": "semantic",
        "total": 210,
        "sumPotentialCostSavings": 0.61,
        "p99CacheLookupLatencyMs": 24.5
      }
    ]
  }
}
If groupBy is empty or omitted, the response collapses to a single row (or one row per timeseries bucket) summarising every cache-eligible request inside the window. Note that the server always pins WHERE "CacheLookupStatus" IS NOT NULL, so non-cache rows are filtered out automatically.

Error responses

A malformed query returns 400 Bad Request:
{
  "statusCode": 400,
  "message": "Invalid query",
  "details": ["..."]
}
Common causes:
  • Operator not allowed on this field, for example, STRING_CONTAINS on cacheType (it supports only IN/NOT_IN).
  • Missing required value (or wrong shape, e.g. scalar where array is expected for IN / BETWEEN).
  • Unknown field name for the datasource.
  • Invalid interval format (compound expressions, unrecognised unit, non-positive integer).
  • Missing required interval for a timeseries query.
Other status codes:
  • 401 Unauthorized: missing or invalid bearer token.
  • 403 Forbidden: caller does not have permission for the requested scope.
  • 500 Internal Server Error: unexpected server error while executing the query.