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
      }
    }
  }
}
todoFilter recalculates — it does not return final numbers synchronously

When you pass filter.todoFilter, charts does not return the saved results. It triggers a fresh recalculation of every listed chart under that record filter, returns each item flagged isCalculatingWithFilter: true, and publishes the recomputed values over subscribeToChart. Subscribe to the dashboard with the same todoFilter to receive the final numbers — they do not arrive in the charts response itself.

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.

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.cc/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
    }
  }
}
todoFilter must match exactly to receive filtered events

The subscription’s todoFilter is matched against the todoFilter a chart was recomputed under, by exact JSON equality. An event is delivered only when both sides carry the same todoFilter, or when neither does. If one side has a todoFilter and the other doesn’t, the event is dropped. To follow the recalculations triggered by a filtered charts query, open the subscription with the identical todoFilter you passed to charts.

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 a union — ChartMetadataBarChart for BAR charts and ChartMetadataPieChart for PIE charts — so select fields through an inline fragment:

query GetChartMetadata {
  chart(id: "chart_123") {
    id
    type
    metadata {
      ... on ChartMetadataBarChart {
        barChart {
          xAxis {
            title
            type
            interval
          }
          yAxis {
            title
            function
          }
        }
      }
      ... on ChartMetadataPieChart {
        pieChart {
          groupBy {
            title
            type
          }
          value {
            title
            function
          }
        }
      }
    }
  }
}

For BAR, barChart.xAxis carries the grouping (type is a BarChartXAxisType, with an optional interval for date axes) and barChart.yAxis carries the aggregate (function is a ChartSegmentValueFunctions). For PIE, pieChart.groupBy and pieChart.value mirror those roles. The axis input shapes are documented in Create and manage charts.

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 that doesn’t exist, or whose dashboard you can’t access.
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.