Skip to content
Dashboard

Drill into report rows

POST/v2/reports/drill-in

Returns the paginated row-level records behind a metric — either the whole reporting window, or one specific data point from a previous POST /v2/reports/query response.

Send the same metric, date range, granularity, filters, groupBy / segmentBy and view as the query you’re drilling into, plus dataPointFilters selecting the data point:

  • timeBucket — a date from the time series
  • groupValue / segmentValue — a group / segment value from grouped data
  • dayOfWeek + hourOfDay — a heatmap cell (hourly_heatmap view)
  • flowPathId or sourceNodeId + targetNodeId — a flow edge (sankey view)

Pass dataPointFilters: {} to list all rows behind the metric for the window.

Results are paginated with page / pageSize (max 200 per page); availableColumns describes every column the dataset can return and defaultColumnIds the recommended subset. Rows are keyed by column ID; identity cells (teammates, contacts) are objects with id + label.

Version Availability

This endpoint is only available in API version 2026-01-01.nova and newer, and only for workspaces with the Reports product enabled (404 otherwise).

Header ParametersExpand Collapse
"Featurebase-Version": optional "2026-01-01.nova" or "2025-12-12.clover"
One of the following:
"2026-01-01.nova"
"2025-12-12.clover"
Body ParametersJSONExpand Collapse
endDate: string

End of the reporting window (ISO 8601 date or datetime, inclusive). Windows ending before 2026-07-12 are rejected.

minLength10
maxLength64
granularity: "hour" or "day" or "week" or "month"

Time bucket size for the returned time series.

One of the following:
"hour"
"day"
"week"
"month"
metric: string

Metric ID the drill-in belongs to.

minLength1
maxLength128
startDate: string

Start of the reporting window (ISO 8601 date or datetime, inclusive). Reporting data is available from 2026-07-12; crossing windows are clamped to that boundary.

minLength10
maxLength64
chartEditorId: optional string

Stable editor ID of the saved chart that launched this drill-in.

minLength1
maxLength256
columns: optional array of string

Column IDs to include in each returned row. Omit for legacy full-width static rows; pass an empty array to use defaultColumnIds.

dataPointFilters: optional object { dayOfWeek, flowPathId, groupValue, 5 more }

Narrows the drill-in to one data point from a previous query (time bucket, group value, heatmap cell, or flow edge). Pass {} to list all underlying rows.

dayOfWeek: optional number

Day of week (1 = Monday … 7 = Sunday) — for hourly_heatmap views.

minimum1
maximum7
flowPathId: optional string

Flow path ID — for sankey views.

maxLength128
groupValue: optional string

Group value to drill into (a group value from groupedData).

maxLength255
hourOfDay: optional number

Hour of day (0-23) — for hourly_heatmap views.

minimum0
maximum23
segmentValue: optional string

Segment value to drill into (a segment value from segment data).

maxLength255
sourceNodeId: optional string

Flow source node ID — for sankey views.

maxLength128
targetNodeId: optional string

Flow target node ID — for sankey views.

maxLength128
timeBucket: optional string

Time bucket to drill into (a date value from the query time series).

maxLength64
filters: optional ReportFilterRule { fieldId, kind, operator, value } or ReportFilterExpression { children, kind, op }

Filter expression: a single rule, or an and/or group combining rules and nested groups. Attribute IDs and their allowed operators come from GET /v2/reports/datasets.

One of the following:
ReportFilterRule object { fieldId, kind, operator, value }
fieldId: string

Attribute ID to filter on (e.g. conversation.channel). Discover attribute IDs via GET /v2/reports/datasets.

minLength1
maxLength512
kind: "rule"
operator: "is" or "is_not" or "in" or 10 more

Comparison operator. Each attribute supports a subset of operators — see the allowedOperators field in the GET /v2/reports/datasets catalog.

One of the following:
"is"
"is_not"
"in"
"not_in"
"contains"
"not_contains"
"gte"
"lte"
"between"
"exists"
"not_exists"
"is_member_of"
"is_not_member_of"
value: optional string or number or boolean or array of string or number or boolean

Value to compare against. Scalar for is, is_not, contains, not_contains, gte, lte (ISO date strings for date attributes); non-empty array for in, not_in, is_member_of, is_not_member_of; 2-element array for between; omit for exists / not_exists.

One of the following:
string
number
boolean
array of string or number or boolean
One of the following:
string
number
boolean
ReportFilterExpression object { children, kind, op }
children: array of ReportFilterRule { fieldId, kind, operator, value } or object { children, kind, op }

Rules and/or nested groups of rules. Groups may nest up to 6 levels at runtime; 100 rules max per expression.

One of the following:
ReportFilterRule object { fieldId, kind, operator, value }
fieldId: string

Attribute ID to filter on (e.g. conversation.channel). Discover attribute IDs via GET /v2/reports/datasets.

minLength1
maxLength512
kind: "rule"
operator: "is" or "is_not" or "in" or 10 more

Comparison operator. Each attribute supports a subset of operators — see the allowedOperators field in the GET /v2/reports/datasets catalog.

One of the following:
"is"
"is_not"
"in"
"not_in"
"contains"
"not_contains"
"gte"
"lte"
"between"
"exists"
"not_exists"
"is_member_of"
"is_not_member_of"
value: optional string or number or boolean or array of string or number or boolean

Value to compare against. Scalar for is, is_not, contains, not_contains, gte, lte (ISO date strings for date attributes); non-empty array for in, not_in, is_member_of, is_not_member_of; 2-element array for between; omit for exists / not_exists.

One of the following:
string
number
boolean
array of string or number or boolean
One of the following:
string
number
boolean
ReportFilterGroup object { children, kind, op }
children: array of ReportFilterRule { fieldId, kind, operator, value }
fieldId: string

Attribute ID to filter on (e.g. conversation.channel). Discover attribute IDs via GET /v2/reports/datasets.

minLength1
maxLength512
kind: "rule"
operator: "is" or "is_not" or "in" or 10 more

Comparison operator. Each attribute supports a subset of operators — see the allowedOperators field in the GET /v2/reports/datasets catalog.

One of the following:
"is"
"is_not"
"in"
"not_in"
"contains"
"not_contains"
"gte"
"lte"
"between"
"exists"
"not_exists"
"is_member_of"
"is_not_member_of"
value: optional string or number or boolean or array of string or number or boolean

Value to compare against. Scalar for is, is_not, contains, not_contains, gte, lte (ISO date strings for date attributes); non-empty array for in, not_in, is_member_of, is_not_member_of; 2-element array for between; omit for exists / not_exists.

One of the following:
string
number
boolean
array of string or number or boolean
One of the following:
string
number
boolean
kind: "group"
op: "and" or "or"

Group operator: and (all match) or or (any match).

One of the following:
"and"
"or"
kind: "group"
op: "and" or "or"

Group operator: and (all match) or or (any match).

One of the following:
"and"
"or"
groupBy: optional string
maxLength512
officeHoursOnly: optional boolean
page: optional number

Page number (1-based).

minimum1
pageSize: optional number

Rows per page (1-200, default 25).

minimum1
maximum200
segmentBy: optional string
maxLength512
selectedMetricId: optional string

A metric ID from the datasets catalog. Use when the originating chart aggregates several metrics to pick which one the drill-in follows. Defaults to metric.

minLength1
maxLength128
selectedMetricRowId: optional string

Stable metric-row ID selected within the launching chart.

minLength1
maxLength256
sort: optional array of object { direction, fieldId }
direction: "asc" or "desc"
One of the following:
"asc"
"desc"
fieldId: string

Column ID to sort by (an attribute ID or metric.value).

minLength1
maxLength512
timezone: optional string
maxLength64
view: optional "standard" or "hourly_heatmap" or "sankey"

Result shape. standard returns a time series (plus grouped/segment data when requested), hourly_heatmap buckets by day-of-week × hour-of-day, sankey returns conversation flow data. Each metric lists its supportedViews in the GET /v2/reports/datasets catalog.

One of the following:
"standard"
"hourly_heatmap"
"sankey"
ReturnsExpand Collapse
availableColumns: array of object { id, name, supportsSort, 2 more }
id: string

Column ID (an attribute ID or metric.value).

name: string
supportsSort: boolean

Whether this column may be used in the drill-in sort request.

valueType: string
category: optional string
defaultColumnIds: array of string
meta: object { datasetId, metricId, rawMetricId, 6 more }
datasetId: string
metricId: string
rawMetricId: string
reconciliation: object { kind, rowCount, status, 4 more }
kind: "rows" or "actions_across_conversations" or "active_hour_rate" or 2 more
One of the following:
"rows"
"actions_across_conversations"
"active_hour_rate"
"positive_ratings_over_eligible_ratings"
"missed_over_evaluated"
rowCount: number
status: "not_applicable" or "available" or "denominator_unavailable"
One of the following:
"not_applicable"
"available"
"denominator_unavailable"
denominatorHours: optional number
displayedRate: optional number
eligibleDenominatorTotal: optional number
rawNumeratorTotal: optional number
rowGrain: string
rowSemantics: "metric_rows" or "numerator_rows"
One of the following:
"metric_rows"
"numerator_rows"
sourceFact: string
emptyReason: optional "no_matching_rows" or "denominator_unavailable"
One of the following:
"no_matching_rows"
"denominator_unavailable"
metricRowId: optional string
object: "report_drill_in_result"
page: number
pageSize: number
rows: array of map[string or number or boolean or 3 more]

One record per underlying row, keyed by column ID.

One of the following:
string
number
boolean
array of string
ReportDrillInIdentityCell object { id, kind, label, 3 more }
id: string
kind: "identity"
label: string
avatarUrl: optional string
color: optional string
provenance: optional "current" or "historical" or "fallback" or "unassigned"
One of the following:
"current"
"historical"
"fallback"
"unassigned"
ReportDrillInSourceDetailsCell object { items, kind }
items: array of object { label, id, type, url }
label: string
id: optional string
type: optional string
url: optional string
formaturi
kind: "source_details"
timezone: string
total: number

Drill into report rows

curl https://do.featurebase.app/v2/reports/drill-in \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $FEATUREBASE_API_KEY" \
    -d '{
          "endDate": "2026-07-15",
          "granularity": "day",
          "metric": "new_conversations",
          "startDate": "2026-07-12",
          "view": "standard"
        }'
{
  "availableColumns": [
    {
      "id": "id",
      "name": "name",
      "supportsSort": true,
      "valueType": "valueType",
      "category": "category"
    }
  ],
  "defaultColumnIds": [
    "string"
  ],
  "meta": {
    "datasetId": "datasetId",
    "metricId": "metricId",
    "rawMetricId": "rawMetricId",
    "reconciliation": {
      "kind": "rows",
      "rowCount": 0,
      "status": "not_applicable",
      "denominatorHours": 0,
      "displayedRate": 0,
      "eligibleDenominatorTotal": 0,
      "rawNumeratorTotal": 0
    },
    "rowGrain": "rowGrain",
    "rowSemantics": "metric_rows",
    "sourceFact": "sourceFact",
    "emptyReason": "no_matching_rows",
    "metricRowId": "metricRowId"
  },
  "object": "report_drill_in_result",
  "page": 0,
  "pageSize": 0,
  "rows": [
    {
      "foo": "string"
    }
  ],
  "timezone": "timezone",
  "total": 0
}
Returns Examples
{
  "availableColumns": [
    {
      "id": "id",
      "name": "name",
      "supportsSort": true,
      "valueType": "valueType",
      "category": "category"
    }
  ],
  "defaultColumnIds": [
    "string"
  ],
  "meta": {
    "datasetId": "datasetId",
    "metricId": "metricId",
    "rawMetricId": "rawMetricId",
    "reconciliation": {
      "kind": "rows",
      "rowCount": 0,
      "status": "not_applicable",
      "denominatorHours": 0,
      "displayedRate": 0,
      "eligibleDenominatorTotal": 0,
      "rawNumeratorTotal": 0
    },
    "rowGrain": "rowGrain",
    "rowSemantics": "metric_rows",
    "sourceFact": "sourceFact",
    "emptyReason": "no_matching_rows",
    "metricRowId": "metricRowId"
  },
  "object": "report_drill_in_result",
  "page": 0,
  "pageSize": 0,
  "rows": [
    {
      "foo": "string"
    }
  ],
  "timezone": "timezone",
  "total": 0
}