Preview, recalculate, and export charts

Compute a throwaway chart preview, force-recalculate saved charts, and export chart data to CSV with previewChart, recalculateCharts, and exportChartCSV.


Three operations work over chart results rather than chart structure: previewChart computes a chart without saving it (for live editor previews), recalculateCharts forces saved charts to recompute, and exportChartCSV ships a chart’s underlying records to CSV. Charts are Chart objects in the API, and every chart belongs to a dashboard (Dashboard) — these operations are authorized through that dashboard.

To create, edit, or delete charts, see Create and manage charts. To read or subscribe to saved charts, see Query and subscribe to charts.

Results are asynchronous

Recalculation and CSV export do not return chart data. recalculateCharts republishes updated charts over the subscribeToChart subscription; exportChartCSV delivers the file out-of-band. Both return immediately.

previewChart

previewChart is a query, not a mutation, even though it accepts CreateChartInput. It computes a chart and returns it without persisting anything — no chart, segment, or segment value is written to the database. Use it to render a live preview in a chart editor before the user commits to saving.

The returned Chart is fabricated: it carries a throwaway id/uid, position is always 65535, and createdAt/updatedAt are the current time. Don’t store or re-reference these — call createChart to persist the chart for real.

A preview works two ways, mirroring the two kinds of chart:

  • Manual chart — pass chartSegments. Each segment’s formula is evaluated against its segment values (each value is an aggregate over one workspace’s records).
  • Auto-generated chart — omit chartSegments and pass metadata.barChart (an x-axis/y-axis spec) or metadata.pieChart (a group-by/value spec). Blue generates the segments for you.

If you supply neither chartSegments nor a metadata.barChart/metadata.pieChart block, the query fails.

Request

Preview an auto-generated bar chart that counts records per workspace, without saving it.

query PreviewBarChart {
  previewChart(
    input: {
      dashboardId: "dashboard_123"
      title: "Records by workspace"
      type: BAR
      metadata: {
        barChart: {
          xAxis: { type: PROJECT, title: "Workspace" }
          yAxis: { function: COUNT, title: "Records" }
        }
      }
    }
  ) {
    id
    title
    type
    position
    chartSegments {
      uid
      title
      formulaResult
    }
  }
}

Parameters

previewChart takes the same CreateChartInput as createChart. The fields relevant to a preview:

CreateChartInput

ParameterTypeRequiredDescription
dashboardIdString!YesDashboard the preview is scoped to. You must be its creator or an EDITOR. Nothing is written to it.
titleString!YesChart title. Echoed back on the preview.
typeChartType!YesSTAT, PIE, or BAR.
chartSegments[ChartSegmentInput!]NoManual segments. When present, the chart is manual and metadata is ignored.
metadataChartMetadataInputNoAuto-generation spec. Provide barChart or pieChart when chartSegments is omitted.
displayFormulaDisplayInputNoNumber/currency/percentage formatting for the displayed value.
positionFloatNoIgnored by the preview — the returned position is always 65535.

ChartType

ValueDescription
STATA single rolled-up number.
PIEA pie chart of grouped values.
BARA bar chart over an x-axis/y-axis.

ChartMetadataInput

ParameterTypeRequiredDescription
barChartBarChartMetadataInputNoBar-chart spec: an xAxis and a yAxis.
pieChartPieChartMetadataInputNoPie-chart spec: a groupBy and a value.

BarChartMetadataInput

ParameterTypeRequiredDescription
xAxisBarChartXAxisInput!YesWhat the bars are grouped by.
yAxisBarChartYAxisInput!YesWhat each bar measures.

BarChartXAxisInput

ParameterTypeRequiredDescription
typeBarChartXAxisType!YesDimension to group by (see values below).
titleStringNoAxis label.
intervalBarChartXAxisIntervalNoBucket size for date-based axes (DAYYEAR).
customFieldNameStringNoField name when type is CUSTOM_FIELD.
customFieldTypeCustomFieldTypeNoField type when type is CUSTOM_FIELD.
customFieldReferenceProjectIdStringNoReferenced workspace for a reference custom field.

BarChartYAxisInput

ParameterTypeRequiredDescription
functionChartSegmentValueFunctionsNoAggregate applied per bucket (COUNT, SUM, …).
filterTodoFilterInputNoRestrict the records each bar measures.
titleStringNoAxis label.
customFieldNameStringNoField name when measuring a custom field.
customFieldTypeCustomFieldTypeNoField type when measuring a custom field.
customFieldReferenceProjectIdStringNoReferenced workspace for a reference custom field.

PieChartMetadataInput is the same shape with groupBy (a PieChartGroupByInput, no interval) and value (a PieChartValueInput, identical to BarChartYAxisInput).

BarChartXAxisType

ValueGroups by
PROJECTWorkspace.
ASSIGNEEAssigned user.
TAGTag.
CUSTOM_FIELDA custom field’s value.
TODO_LISTList.
TODO_STATUSRecord completion status.
TODO_DUE_DATEDue date (bucketed by interval).
TODO_CREATED_ATCreation date (bucketed by interval).
TODO_UPDATED_ATLast-updated date (bucketed by interval).
TODO_COMPLETED_ATCompletion date (bucketed by interval).

ChartSegmentValueFunctions

ValueAggregate
COUNTCount of records with a numeric value.
COUNTACount of records with any non-empty value.
SUMSum of values.
AVERAGEMean of numeric values.
AVERAGEAMean treating non-numeric values as 0.
MINSmallest value.
MAXLargest value.
Two function enums, identical members

ChartSegmentValueFunctions (above) applies to a segment value or a metadata axis. The separate ChartFunction enum has the same member names but lives only on FormulaDisplayInput.function (the segment-level display rollup). They are distinct types — don’t substitute one for the other. The manual-chart inputs (chartSegments) are documented in full on Build chart segments and values.

Response

{
  "data": {
    "previewChart": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "title": "Records by workspace",
      "type": "BAR",
      "position": 65535,
      "chartSegments": [
        { "uid": "ck0seg1", "title": "Sales", "formulaResult": 128 },
        { "uid": "ck0seg2", "title": "Marketing", "formulaResult": 74 }
      ]
    }
  }
}

Returns

previewChart returns a Chart. Every field is computed in memory; the chart is not saved.

FieldTypeDescription
idID!Throwaway identifier. Do not persist or reuse it.
titleString!Echoes the input title.
typeChartType!STAT, PIE, or BAR.
positionFloat!Always 65535 for a preview.
chartSegments[ChartSegment!]!Computed segments. formulaResult carries each segment’s result.
displayFormulaDisplayThe formatting passed in (or empty).
metadataChartMetadataThe bar/pie spec used to generate the chart, when auto-generated.
createdAt / updatedAtDateTime!The current time — not a real persisted timestamp.

ChartSegment.formulaResult is the per-segment number; for a STAT chart there is typically one segment whose formulaResult is the headline value.

Errors

CodeWhen
DASHBOARD_NOT_FOUNDNo dashboard matches dashboardId for the caller, or the caller is neither its creator nor an EDITOR.

Two failure modes raise a generic error rather than a coded Blue error: a manual chartSegments value that references a workspace the caller doesn’t belong to (the preview throws Access denied to projects: …), and supplying neither chartSegments nor a metadata.barChart/metadata.pieChart block (there is nothing to compute).


recalculateCharts

recalculateCharts forces one or more saved charts to recompute their results immediately, then republishes them so subscribers see fresh numbers. Use it after data changes that wouldn’t otherwise trigger a recalculation, or to refresh a stale chart on demand.

The call is all-or-nothing on access: every id in chartIds must resolve to a chart the caller can view. If any one is inaccessible, the whole call is rejected with FORBIDDEN and no chart is recalculated.

Request

mutation RecalculateCharts {
  recalculateCharts(input: { chartIds: ["chart_123", "chart_456"] })
}

recalculateCharts returns a nullable Boolean (true on success), so it takes no sub-selection.

Parameters

RecalculateChartsInput

ParameterTypeRequiredDescription
chartIds[String!]!YesIDs of saved charts to recalculate. You must have view access to every id in the list.

Response

{
  "data": {
    "recalculateCharts": true
  }
}

Each recalculated chart is republished over subscribeToChart as a ChartSubscriptionPayload with mutation: UPDATED, carrying the recomputed isCalculating/needCalculation/formulaResult values on node. The mutation itself returns only the boolean — read the new numbers from the subscription or by re-querying chart/charts.

Errors

CodeWhen
FORBIDDENThe caller cannot view at least one chart in chartIds. The entire call is rejected and nothing recalculates.

An unknown id is treated the same as an inaccessible one — both reduce the viewable count below the requested count and trigger FORBIDDEN.


exportChartCSV

exportChartCSV enqueues an asynchronous CSV export of the records behind a chart and returns true immediately. The export runs as a background job; the finished CSV is delivered out-of-band (emailed / served from the calling host), not in the GraphQL response. Pass an optional filter to scope which records are exported.

A per-chart, per-user lock prevents duplicate exports: once you start an export, a second exportChartCSV call for the same chart by the same user fails with CHART_ALREADY_EXPORTING until the lock expires after 6 hours (21,600 seconds).

Request

Export every record behind a chart.

mutation ExportChartCSV {
  exportChartCSV(chartId: "chart_123")
}

exportChartCSV returns Boolean!, so it takes no sub-selection. true means the job was accepted — not that the file is ready.

Parameters

ParameterTypeRequiredDescription
chartIdID!YesChart whose records to export. Must belong to a dashboard in your organization.
filterTodoFilterInputNoRestrict the exported rows. Omit to export all records behind the chart.

TodoFilterInput is the standard record filter used across the API; see List records for its full field set.

Response

{
  "data": {
    "exportChartCSV": true
  }
}

The boolean confirms the job was enqueued. The CSV arrives separately once the worker finishes; there is no operation that returns the file URL synchronously.

Full example

Export only the records that match a filter — here, records on a specific list.

mutation ExportFilteredChartCSV {
  exportChartCSV(chartId: "chart_123", filter: { todoListIds: ["list_123"] })
}

Errors

CodeWhen
CHART_NOT_FOUNDNo chart matches chartId, or the chart’s dashboard is in an organization the caller is not a member of.
CHART_ALREADY_EXPORTINGAn export for the same chart by the same user is already in flight. The lock clears 6 hours after the export started.
{
  "errors": [
    {
      "message": "Chart is already being exported.",
      "extensions": { "code": "CHART_ALREADY_EXPORTING" }
    }
  ]
}
The export lock is per user, per chart, for 6 hours

The dedupe lock is keyed on chartId + the calling user and lives in Redis for 21,600 seconds. A second export of the same chart by the same user is blocked for up to 6 hours; a different user, or a different chart, is unaffected. There is no operation to release the lock early.

Permissions

All three operations derive their permissions from the chart’s parent dashboard. There is no organization- or workspace-level chart access — a chart is reachable only through a dashboard you can see.

OperationAccess required
previewChartCreator of the target dashboard, or an EDITOR on it. A VIEWER cannot preview.
recalculateChartsView access (creator or any dashboard user — EDITOR or VIEWER) to every chart’s dashboard.
exportChartCSVMembership in the organization that owns the chart’s dashboard.

See Dashboards for how dashboard creators and dashboardUsers (with EDITOR/VIEWER roles) are managed.