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.

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.query (what to group by, what to measure). Blue generates the segments for you.

If you supply neither chartSegments nor a metadata.query 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: {
        query: {
          dimensions: [{ type: PROJECT, title: "Workspace" }]
          metrics: [{ 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 a query 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
queryChartQueryInputNoWhat to group by and what to measure.
presentationChartPresentationInputNoHow the result is dressed. Never the query.

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.
TODOThe record itself, one bucket each.
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.

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.query 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 keeps its established Boolean response for compatibility. 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.
autoBooleanNoSet to true for an automatic background refresh. The server skips charts that are not stale or are still inside their cooldown, so the returned list can be shorter.

For automatic refresh coordination, call scheduleChartRecalculations with the same input. It returns ScheduleChartRecalculationsResult!, containing the chart IDs that the server scheduled and the configured retry delay. It also skips charts whose last computation exceeded the cost limit.

mutation ScheduleChartRecalculations {
  scheduleChartRecalculations(input: { chartIds: ["chart_123", "chart_456"], auto: true }) {
    scheduledChartIds
    retryAfterSeconds
  }
}

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. Read the new numbers from the subscription or by re-querying chart/charts. scheduleChartRecalculations is the automatic-refresh variant; it returns the IDs that passed the stale, cost, and cooldown guards.

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 you can view.
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 caller has no view access to the chart’s dashboard.
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" }
    }
  ]
}

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.
exportChartCSVDashboard creator or EDITOR; a VIEWER also needs allowViewerChartData enabled.

An export’s rows are read as the dashboard’s creator, which is the same scope the chart’s numbers were computed as — so the CSV matches the card. The dashboard creator can turn viewer access to those rows on or off with allowViewerChartData. Editors always retain access.

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