Query and subscribe to charts

Read a single chart by id, list every chart on a dashboard, and subscribe to real-time chart create, update, and delete events.


Use the chart query to read a single chart by id, the charts query to list every chart on a dashboard, and the subscribeToChart subscription to receive real-time create, update, and delete events. Charts are Chart objects in the API; they always live inside a parent dashboard (a Dashboard object), so reads are scoped by the chart’s dashboard, not by an organization or workspace.

A chart is one of three card types — STAT, PIE, or BAR. Manual charts carry chartSegments (each a Formula over named ChartSegmentValue aggregates); auto-generated charts instead carry metadata describing a bar or pie spec over records. Both shapes come back from these queries; see Build chart segments and values and Create and manage charts for how each is constructed.

Query a single chart

Use the chart query to fetch one chart by its id.

Request

query GetChart {
  chart(id: "chart_123") {
    id
    title
    type
    position
    isCalculating
    needCalculation
    chartSegments {
      id
      uid
      title
      color
      formulaResult
    }
    createdAt
    updatedAt
  }
}

Parameters

ArgumentTypeRequiredDescription
idString!YesID of the chart to read.

Response

{
  "data": {
    "chart": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "title": "Revenue by stage",
      "type": "STAT",
      "position": 1024,
      "isCalculating": false,
      "needCalculation": false,
      "chartSegments": [
        {
          "id": "clm4n8qwx000108l0a1b2c3d4",
          "uid": "seg_a1b2c3",
          "title": "Closed won",
          "color": "#2563eb",
          "formulaResult": 48250
        }
      ],
      "createdAt": "2026-04-12T09:30:00.000Z",
      "updatedAt": "2026-05-28T14:05:11.000Z"
    }
  }
}

List charts on a dashboard

Use the charts query to list every chart on a dashboard. The filter requires a dashboardId — there is no organization- or workspace-level chart listing. Results are paginated with offset-based skip/take and ordered by sort (default [position_ASC], the card order on the dashboard).

Request

The smallest valid call passes a filter with the required dashboardId:

query ListCharts {
  charts(filter: { dashboardId: "dashboard_123" }) {
    items {
      id
      title
      type
      position
      isCalculating
      needCalculation
    }
    pageInfo {
      totalItems
      totalPages
      page
      perPage
      hasNextPage
      hasPreviousPage
    }
  }
}

Parameters

ArgumentTypeRequiredDescription
filterChartFilterInput!YesScopes the listing to one dashboard, optionally under a record filter.
sort[ChartSort!]NoOrdering applied to the results. Defaults to [position_ASC].
skipIntNoNumber of charts to skip before returning results. Defaults to 0.
takeIntNoNumber of charts to return per page. Defaults to 20.

ChartFilterInput

FieldTypeRequiredDescription
dashboardIdString!YesID of the dashboard whose charts to list.
todoFilterTodoFilterInputNoRecalculates each chart’s results against this record filter instead of returning the saved values. See note below.

ChartSort

sort takes an array of these enum values; later values break ties from earlier ones.

ValueDescription
position_ASCDashboard card order, first to last (default).
position_DESCDashboard card order, last to first.
title_ASCTitle, A to Z.
title_DESCTitle, Z to A.
createdBy_ASCCreator’s first name, A to Z.
createdBy_DESCCreator’s first name, Z to A.
updatedAt_ASCLeast recently updated first.
updatedAt_DESCMost recently updated first.

Response

{
  "data": {
    "charts": {
      "items": [
        {
          "id": "clm4n8qwx000008l0g4oxdqn7",
          "title": "Revenue by stage",
          "type": "STAT",
          "position": 1024,
          "isCalculating": false,
          "needCalculation": false
        },
        {
          "id": "clm4n8qwx000208l0e5f6g7h8",
          "title": "Records by list",
          "type": "BAR",
          "position": 2048,
          "isCalculating": false,
          "needCalculation": false
        }
      ],
      "pageInfo": {
        "totalItems": 2,
        "totalPages": 1,
        "page": 1,
        "perPage": 20,
        "hasNextPage": false,
        "hasPreviousPage": false
      }
    }
  }
}

Filtered listing

Pass a todoFilter to recompute the dashboard’s charts against an ad-hoc record filter — for example, only records updated this quarter:

query ListChartsFiltered {
  charts(
    filter: {
      dashboardId: "dashboard_123"
      todoFilter: { projectIds: ["project_123"], showCompleted: false }
    }
    sort: [position_ASC]
    take: 50
  ) {
    items {
      id
      title
      type
      isCalculatingWithFilter
    }
    pageInfo {
      totalItems
      hasNextPage
    }
  }
}

todoFilter is a TodoFilterInput — the same record-filter shape used across the API. See List records for its full field set.

Read the records behind a chart

Use the chartData query to list the records a chart’s numbers are made of. It answers the question “which records is this bar?” — and answers it so the two reconcile exactly.

Rows are contributions, not records. A record carrying two tags is behind both tag bars and appears under each, so the row count adds up to the chart rather than to a de-duplicated list of records. Both totals come back: contributionCount and uniqueRecordCount.

query ChartData {
  chartData(input: { chartId: "chart_123", take: 50 }) {
    contributionCount
    uniqueRecordCount
    hasMore
    calculatedAt
    buckets {
      key
      label
      value
      listable
      gap
    }
    rows {
      bucketKey
      bucketLabel
      seriesLabel
      record {
        id
        title
      }
    }
  }
}

Parameters

All parameters live on ChartDataInput.

ParameterTypeRequiredDescription
chartIdID!YesThe chart to read.
filterTodoFilterInputNoThe dashboard filter the chart was rendered with. Sending a different one answers a different question from the card.
bucketKeyStringNoNarrow to one bucket, using a key from buckets. Omit for every contribution the chart counts.
metricKeyStringNoWhich metric to list, on a chart plotting more than one. Defaults to the first.
takeIntNoRows per page. Defaults to 50, capped at 100.
skipIntNoRows to skip. Defaults to 0.

Buckets, keys, and labels

buckets lists every bucket the chart drew, with the value it drew. A bucket’s key is its identity and its label is what the chart printed — they are not the same thing, and only the key round-trips: two people share a first name and group separately while rendering identically, and a date bucket prints as Week 09 of 2026.

Some buckets cannot be turned back into a set of records, and say so rather than guessing. Those come back with listable: false and a gap:

gapMeaning
FOLDED_TAILThe Other band of a breakdown — the slices ranked past the top eight, summed. No single predicate describes it.
BREAKDOWN_NOT_REVERSIBLEA country breakdown, whose slices are labelled with names over grouped alpha-2 codes.
SEGMENT_PREDATES_BUCKET_KEYThe segment was computed before bucket keys were stored. The chart’s next recalculation fixes it.
SOURCE_HAS_NO_WORKSPACEA manual chart’s source with no workspace to read records from.
TOO_MANY_SOURCESMore segments than one query can read at once. Reported for the whole chart; ask for a single bucketKey instead.

Columns and formula breakdown

columns is the set of columns a drill-down table can offer, merged across the chart’s workspaces by field name and type — the same merge reports use. A field of the same name and a different type stays two columns, because it renders two different ways. suggested: true marks the ones the chart itself implies. A merged column whose workspaces disagree on currency carries a warning rather than picking one.

formulaBreakdown is populated for manual charts: one entry per segment value, with the source’s own result alongside the segmentResult its formula produced. It is empty for auto-generated charts, whose numbers come from one grouped query rather than from combined sources.

Subscribe to chart changes

Use the subscribeToChart subscription to receive real-time events when charts on a dashboard are created, updated, or deleted — including the recomputed values that follow a recalculation. Subscriptions run over the WebSocket transport at wss://api.blue.app/graphql.

Request

subscription OnChartChange {
  subscribeToChart(filter: { dashboardId: "dashboard_123" }) {
    mutation
    node {
      id
      title
      type
      position
      isCalculating
      needCalculation
      chartSegments {
        id
        uid
        formulaResult
      }
    }
    previousValues {
      id
      title
    }
  }
}

Parameters

ArgumentTypeRequiredDescription
filterSubscribeToChartFilterInput!YesThe dashboard to watch, optionally under a record filter.

SubscribeToChartFilterInput

FieldTypeRequiredDescription
dashboardIdString!YesID of the dashboard to receive CHART_CREATED / CHART_UPDATED / CHART_DELETED events for.
todoFilterTodoFilterInputNoRestricts the stream to events recomputed under this exact record filter. See the gating note below.

Event payload

Each event delivers a ChartSubscriptionPayload.

FieldTypeDescription
mutationMutationType!The kind of change: CREATED, UPDATED, or DELETED.
nodeChartThe chart’s new state. Populated for CREATED and UPDATED; null for DELETED.
previousValuesChartThe chart’s prior state. Populated for DELETED (and updates); use it to identify the removed chart.

Example event

{
  "data": {
    "subscribeToChart": {
      "mutation": "UPDATED",
      "node": {
        "id": "clm4n8qwx000008l0g4oxdqn7",
        "title": "Revenue by stage",
        "type": "STAT",
        "position": 1024,
        "isCalculating": false,
        "needCalculation": false,
        "chartSegments": [
          {
            "id": "clm4n8qwx000108l0a1b2c3d4",
            "uid": "seg_a1b2c3",
            "formulaResult": 48250
          }
        ]
      },
      "previousValues": null
    }
  }
}

Return types

Chart

FieldTypeDescription
idID!Unique identifier for the chart.
titleString!Display name of the chart card.
positionFloat!Sort position within the dashboard. Lower values appear first.
typeChartType!The card type: STAT, PIE, or BAR.
chartSegments[ChartSegment!]!The manual segments of the chart. Empty for auto-generated (metadata-backed) charts.
displayFormulaDisplayHow the chart’s rolled-up value is formatted (number, currency, or percentage) and rolled up.
metadataChartMetadataThe auto-generated bar or pie spec. null for manual (segment-backed) charts.
isCalculatingBooleantrue while a recompute of the saved results is in progress.
isCalculatingWithFilterBooleantrue on items returned by charts when a todoFilter was supplied; the recomputed values arrive via the subscription.
needCalculationBooleantrue when the saved results are stale and a recalculation is pending.
createdAtDateTime!When the chart was created.
updatedAtDateTime!When the chart was last modified.

ChartSegment

FieldTypeDescription
idID!Unique identifier for the segment.
uidString!Stable public identifier for the segment.
titleStringThe segment’s label.
colorStringThe segment’s display color (hex string).
chartSegmentValues[ChartSegmentValue!]!The named aggregates the segment’s formula draws on.
formulaFormula!The formula combining the segment’s values. logic.text / logic.html reference value UIDs.
formulaResultFloatThe computed result of the formula, or null until calculated.
createdAtDateTime!When the segment was created.
updatedAtDateTime!When the segment was last modified.

ChartSegmentValue

FieldTypeDescription
idID!Unique identifier for the value.
uidString!The identifier a segment’s formula.logic references this value by.
titleString!The value’s label.
disabledBoolean!Whether the value is excluded from its segment’s formula.
projectIdString!ID of the workspace whose records this value aggregates over.
customFieldIdStringID of the custom field being aggregated, when the function targets one.
functionChartSegmentValueFunctionsThe aggregate function: COUNT, COUNTA, SUM, AVERAGE, AVERAGEA, MIN, or MAX.
filterTodoFilterThe record filter scoping which records are aggregated.
createdAtDateTime!When the value was created.
updatedAtDateTime!When the value was last modified.

ChartMetadata

metadata is one object shape for every chart — no inline fragment needed. query is present on an auto-generated chart and null on a manual one; presentation carries settings that never affect which records the chart covers.

query GetChartMetadata {
  chart(id: "chart_123") {
    id
    type
    displayType
    metadata {
      query {
        dimensions {
          title
          type
          interval
          customFieldName
          customFieldType
          order {
            by
            direction
            metricKey
          }
          limit
        }
        metrics {
          key
          title
          function
          customFieldName
          customFieldType
        }
        breakout {
          title
          type
        }
        filters {
          showCompleted
        }
      }
      presentation {
        stackMode
        direction
        target {
          mode
          value
          segmentUid
        }
        bands {
          atRisk
          onTrack
        }
      }
    }
  }
}

query.dimensions[0] carries the grouping (type is a BarChartXAxisType, with an optional interval for date dimensions) and query.metrics carries the aggregates (function is a ChartSegmentValueFunctions; omit it for a plain record count). Bar and pie are the same query — they differ only in displayType. query is null on a manual chart. The full shapes are documented in Create and manage charts.

A dimension also carries how its buckets are arranged. order is null when the chart takes the order the aggregate produced, and limit is null when the chart draws every bucket. Select both if you rebuild a chart’s configuration from a read — they are the only record of a saved order and bucket limit, and the segments in chartSegments are returned in full whether or not a limit is set.

PageInfo

FieldTypeDescription
totalItemsIntTotal number of charts on the dashboard, across all pages.
totalPagesIntTotal number of pages at the current take.
pageIntThe current page number (1-based), derived from skip and take.
perPageIntNumber of items per page (mirrors take).
hasNextPageBoolean!Whether another page follows the current one.
hasPreviousPageBoolean!Whether a page precedes the current one.

PageInfo also exposes startCursor and endCursor, but both are deprecated — charts are not cursor-paginated. Walk pages with skip/take.

Errors

CodeWhen
UNAUTHENTICATEDThe request has no valid credentials.
CHART_NOT_FOUNDchart(id:) references a chart you cannot access, or chartData is disabled for the caller.
RATE_LIMITEDA chartData query for this user is still running. One runs at a time.
DASHBOARD_NOT_FOUNDcharts (or subscribeToChart) references a dashboard that doesn’t exist, or that you can’t access.

Permissions

You must be authenticated. Charts inherit their access from the parent dashboard: you can read a dashboard’s charts if you created the dashboard or have been added to it as a DashboardUser (any role — VIEWER is sufficient for reads). The subscribeToChart stream applies the same check and silently drops events for dashboards you can’t access. Writing charts requires the dashboard owner or the EDITOR role — see Create and manage charts.

chartData always allows the dashboard creator and editors. A viewer can use it only when the dashboard’s allowViewerChartData setting is on. A chart’s numbers are computed as the dashboard’s creator so that every viewer sees the same figure, and chartData reads its rows the same way — otherwise a viewer with narrower workspace access would be shown twelve records under a bar labelled seventeen.