Skip to content
Dashboard

Drill into report rows

client.reports.drillIn(ReportDrillInParams { endDate, granularity, metric, 16 more } params, RequestOptionsoptions?): ReportDrillInResponse { availableColumns, defaultColumnIds, meta, 6 more }
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).

ParametersExpand Collapse
params: ReportDrillInParams { endDate, granularity, metric, 16 more }
endDate: string

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

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

Body param: Time bucket size for the returned time series.

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

Body param: Metric ID the drill-in belongs to.

minLength1
maxLength128
startDate: string

Body param: 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?: string

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

minLength1
maxLength256
columns?: Array<string>

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

dataPointFilters?: DataPointFilters

Body param: 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?: number

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

minimum1
maximum7
flowPathId?: string

Flow path ID — for sankey views.

maxLength128
groupValue?: string

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

maxLength255
hourOfDay?: number

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

minimum0
maximum23
segmentValue?: string

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

maxLength255
sourceNodeId?: string

Flow source node ID — for sankey views.

maxLength128
targetNodeId?: string

Flow target node ID — for sankey views.

maxLength128
timeBucket?: string

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

maxLength64
filters?: ReportFilterRule { fieldId, kind, operator, value } | ReportFilterExpression { children, kind, op }

Body param: 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 { 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" | "is_not" | "in" | 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?: string | number | boolean | Array<string | number | 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<string | number | boolean>
string
number
boolean
ReportFilterExpression { children, kind, op }
children: Array<ReportFilterRule { fieldId, kind, operator, value } | ReportFilterGroup { 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 { 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" | "is_not" | "in" | 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?: string | number | boolean | Array<string | number | 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<string | number | boolean>
string
number
boolean
ReportFilterGroup { children, kind, op }
children: Array<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" | "is_not" | "in" | 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?: string | number | boolean | Array<string | number | 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<string | number | boolean>
string
number
boolean
kind: "group"
op: "and" | "or"

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

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

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

One of the following:
"and"
"or"
groupBy?: string

Body param

maxLength512
officeHoursOnly?: boolean

Body param

page?: number

Body param: Page number (1-based).

minimum1
pageSize?: number

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

minimum1
maximum200
segmentBy?: string

Body param

maxLength512
selectedMetricId?: string

Body param: 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?: string

Body param: Stable metric-row ID selected within the launching chart.

minLength1
maxLength256
sort?: Array<Sort>

Body param

direction: "asc" | "desc"
One of the following:
"asc"
"desc"
fieldId: string

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

minLength1
maxLength512
timezone?: string

Body param

maxLength64
view?: "standard" | "hourly_heatmap" | "sankey"

Body param: 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"
featurebaseVersion?: "2026-01-01.nova" | "2025-12-12.clover"

Header param: API version for this request. Defaults to your organization’s configured API version if not specified.

One of the following:
"2026-01-01.nova"
"2025-12-12.clover"
ReturnsExpand Collapse
ReportDrillInResponse { availableColumns, defaultColumnIds, meta, 6 more }
availableColumns: Array<AvailableColumn>
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?: string
defaultColumnIds: Array<string>
meta: Meta { datasetId, metricId, rawMetricId, 6 more }
datasetId: string
metricId: string
rawMetricId: string
reconciliation: Reconciliation { kind, rowCount, status, 4 more }
kind: "rows" | "actions_across_conversations" | "active_hour_rate" | 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" | "available" | "denominator_unavailable"
One of the following:
"not_applicable"
"available"
"denominator_unavailable"
denominatorHours?: number
displayedRate?: number
eligibleDenominatorTotal?: number
rawNumeratorTotal?: number
rowGrain: string
rowSemantics: "metric_rows" | "numerator_rows"
One of the following:
"metric_rows"
"numerator_rows"
sourceFact: string
emptyReason?: "no_matching_rows" | "denominator_unavailable"
One of the following:
"no_matching_rows"
"denominator_unavailable"
metricRowId?: string
object: "report_drill_in_result"
page: number
pageSize: number
rows: Array<Record<string, string | number | boolean | 3 more | null>>

One record per underlying row, keyed by column ID.

One of the following:
string
number
boolean
Array<string>
ReportDrillInIdentityCell { id, kind, label, 3 more }
id: string
kind: "identity"
label: string
avatarUrl?: string
color?: string
provenance?: "current" | "historical" | "fallback" | "unassigned"
One of the following:
"current"
"historical"
"fallback"
"unassigned"
ReportDrillInSourceDetailsCell { items, kind }
items: Array<Item>
label: string
id?: string
type?: string
url?: string
formaturi
kind: "source_details"
timezone: string
total: number

Drill into report rows

import Featurebase from 'featurebase-node';

const client = new Featurebase({
  apiKey: process.env['FEATUREBASE_API_KEY'], // This is the default and can be omitted
});

const response = await client.reports.drillIn({
  endDate: '2026-07-15',
  granularity: 'day',
  metric: 'new_conversations',
  startDate: '2026-07-12',
});

console.log(response.availableColumns);
{
  "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
}