Query Widgets
Query Widgets let you write SQL for a dashboard widget. Use them when a normal widget calculation is too limited, or when you want to build a widget on top of a reusable Query Dataset.
A Query Widget has one source: a Dataset or a Query Dataset. Inside the Widget
Query, that source is always named source.
The full YAML field list, including every display option, is in
Query Widget YAML.
When to use a Query Widget
Section titled “When to use a Query Widget”Use a Query Widget when you need:
- A table, metric, bar chart, line chart, pie chart, gauge, or funnel from SQL
- Custom grouping or filtering that is easier to express in SQL
- A dashboard widget over a Query Dataset
- Filter Variables passed into SQL as safe parameters
- A small final query on top of a reusable source
Use a normal widget when the built-in calculation editor already covers the metric. Normal widgets are still easier for common counts, sums, averages, grouped charts, and simple table views.
Query Widget vs Query Dataset
Section titled “Query Widget vs Query Dataset”Query Widgets and Query Datasets work together, but they solve different problems.
| Use this | For this |
|---|---|
| Query Dataset | Joins, CTEs, subqueries, window functions, and reusable SQL-defined data |
| Query Widget | The final dashboard query, display settings, and Filter Variable binding |
| Normal widget | Standard chart, table, or KPI calculations without writing SQL |
| Modified Dataset | No-SQL data cleanup, filters, calculated columns, and aggregations |
| Joined Dataset | No-SQL joins and unions across datasets |
If your SQL needs a join, put that join in a Query Dataset first. Then create a Query Widget that selects from the Query Dataset.
A Query Widget source cannot be a Modified Dataset or Joined Dataset. Shape that data in a Query Dataset, then point the widget at the Query Dataset.
Creating a Query Widget
Section titled “Creating a Query Widget”- Open a dashboard.
- Click the Edit icon.
- Click + Widget.
- Choose New Query Widget.
- Select or paste a source in the YAML definition.
- Write the Widget Query.
- Configure the
displayblock. - Click Preview to test it.
- Click Save & Close.
The preview runs immediately in the editor. The saved dashboard result updates through the normal widget refresh worker.
Basic YAML structure
Section titled “Basic YAML structure”Keep query as the last top-level key.
schemaVersion: 1source: type: dataset source: 46b0bbe8-619a-4b23-bc44-f11842775640display: type: tabletimezone: America/Chicagoquery: |- select * from source limit 100Fields
Section titled “Fields”| Field | Required | What it does |
|---|---|---|
schemaVersion |
yes | Use 1 for the current Query Widget format |
source.type |
yes | dataset or queryDataset |
source.source |
yes | Source UUID copied from Browse Sources |
display |
yes | Controls how the result is rendered |
timezone |
no | IANA timezone. Datetime columns are localized before SQL |
parameters |
no | Filter Variable bindings (name + filterVariable UUID) |
drilldownQuery |
no | Optional SQL for on-demand detailed rows |
query |
yes | Read-only SQL that selects from the relation named source |
See Query Widget YAML for the complete field reference.
Widget Query rules
Section titled “Widget Query rules”A Widget Query can:
- Select from
source - Filter rows
- Group and aggregate
- Use
caseexpressions - Sort and limit rows
- Use declared Query Parameters such as
$board_values - Use dashboard date helpers such as
dashboard_date_filter(column)
A Widget Query cannot:
- Join another source
- Use CTEs
- Use subqueries
- Use unions
- Select from a source name or UUID directly
- Change data
- Read or write files
These rules keep saved widgets predictable. If you need joins or CTEs, create a Query Dataset first.
Result row limits
Section titled “Result row limits”Table displays (display.type: table) return at most 1,000 rows from the
main Widget Query in preview and on the dashboard. If the query would return
more, the result is truncated and the widget shows a limited-rows indicator.
Put larger detail sets in drilldownQuery — drilldown is not limited to 1,000
rows.
Non-table displays use a higher general result cap. Prefer filters, aggregates,
or an explicit LIMIT in table Widget Queries so the capped page is the set
you intend viewers to see.
Querying a Query Dataset
Section titled “Querying a Query Dataset”Most complex Query Widgets should point at a Query Dataset:
schemaVersion: 1source: type: queryDataset source: 5a242d39-195c-41da-a99b-93b402a4325bdisplay: type: tabletimezone: America/Chicagoquery: |- select * from source order by last_email_age_minutes desc limit 100Even though the source is a Query Dataset, the Widget Query still selects from
source. Do not select from the Query Dataset UUID.
Using Filter Variables
Section titled “Using Filter Variables”Query Widgets do not automatically inherit every dashboard filter. Add Query Parameters for the Filter Variables you want to use.
parameters: - name: board_values filterVariable: 32b69796-14b3-48d1-a9a7-d196b91752c1Then reference the parameter in SQL with $name:
select count(*) as ticket_countfrom sourcewhere board_name in $board_valuesUse in for multi-select Filter Variables. Use = for single-select Filter
Variables.
Parameters are sent to the query safely. They are not pasted into the SQL as text.
Using the dashboard date filter
Section titled “Using the dashboard date filter”Datetime columns from the source are converted into the widget timezone
before the Widget Query runs. Use an IANA name such as America/Chicago.
To use the active dashboard date filter, call dashboard_date_filter() with the
date or datetime column you want to filter:
select date_trunc('day', created_at) as created_day, count(*) as ticket_countfrom sourcewhere dashboard_date_filter(created_at)group by created_dayorder by created_dayIf no dashboard date filter is active, the helper allows all rows.
When the widget should fall back to a date range instead of matching all rows,
use dashboard_date_filter_or_default():
select date_trunc('day', created_at) as created_day, count(*) as ticket_countfrom sourcewhere dashboard_date_filter_or_default( created_at, runtime_start_date('this month'), runtime_end_date('this month'))group by created_dayorder by created_dayFor point-in-time calculations, use dashboard_start_date(default_date) or
dashboard_end_date(default_date). Each returns the active dashboard boundary
when a filter is set and the supplied default otherwise:
select account_name, sum(amount) as balancefrom sourcewhere posted_on <= dashboard_end_date(runtime_end_date('today'))group by account_nameChoose based on what the widget measures, not its dataset. Activity and flow
widgets use the selected date range. Point-in-time widgets use the selected end
date and can include relevant history before the selected start date. On a
dashboard, the widget identifies its interpretation as either the selected
range or As of <end date>.
Display examples
Section titled “Display examples”display is required. display.type must be one of bar, line, pie,
metric, table, gauge, or funnel.
Metric
Section titled “Metric”display: type: metric value: ticket_count rounding: 0query: |- select count(*) as ticket_count from sourcevalue is the numeric result column from the first row.
display: type: table columns: - ticket_number - ticket_subject - board_name - last_email_age_minutes columnFormats: last_email_age_minutes: rounding: 0 columnLinks: ticket_number: "https://psa.example.com/tickets/{{ ticket_number }}"query: |- select ticket_number, ticket_subject, board_name, last_email_age_minutes from source order by last_email_age_minutes desc limit 100Use columnLinks to turn matching table or drilldown cells into external
http(s) links. Templates support {{ column_name }} placeholders filled from
the row and {{ conn.variable_name }} placeholders filled from the source
integration config. See Query Widget YAML for
auto-fill and authoring rules. Unsafe schemes fall back to plain text.
Bar chart
Section titled “Bar chart”y is a list of numeric result column names, or a single column name. xLabel
and yLabel are axis titles on display.
display: type: bar x: board_name y: - ticket_count xLabel: Board yLabel: Tickets dataLabels: truequery: |- select board_name, count(*) as ticket_count from source group by board_name order by ticket_count descFor long-form data, set seriesBy to the category column that splits series.
seriesBy requires exactly one y column. Aggregate the Widget Query so each
(x, seriesBy) pair is unique.
display: type: bar x: facility y: - open_shift_pct seriesBy: shift_type yLabel: Uncovered shift rateWide data uses multiple y columns and omits seriesBy:
display: type: bar x: board_name y: - open_count - closed_count yLabel: TicketsLine chart
Section titled “Line chart”display: type: line x: created_day y: - ticket_count areaFill: truequery: |- select date_trunc('day', created_at) as created_day, count(*) as ticket_count from source where dashboard_date_filter(created_at) group by created_day order by created_dayPie chart
Section titled “Pie chart”display: type: pie label: status_name value: ticket_count donut: truequery: |- select status_name, count(*) as ticket_count from source group by status_namePie and funnel charts use label and value, not x and y.
See Query Widget YAML for gauge, funnel, metric comparisons, shared number formatting, and table column options.
Quoting columns with dots
Section titled “Quoting columns with dots”Some source columns include dots, such as board.name or company.identifier.
In SQL, a dot usually means a table or struct field. If the column name itself
contains a dot, wrap it in double quotes:
select count(*) as ticket_countfrom sourcewhere "board.name" = 'Service Board'Better yet, alias it to a simple name in a Query Dataset:
select tickets."board.name" as board_namefrom ticketsThen the Query Widget can use board_name.
Drilldown queries
Section titled “Drilldown queries”Use drilldownQuery when you want a Query Widget to provide detailed rows for a
modal or export. It follows the same source and parameter rules as the main
Widget Query.
drilldownQuery: |- select ticket_number, ticket_subject, board_name, last_email_age_minutes from source where board_name in $board_values order by last_email_age_minutes descquery: |- select board_name, count(*) as ticket_count from source where board_name in $board_values group by board_nameKeep the filters aligned so the detailed rows match the visible chart or
metric. For charts, you can filter the clicked category with {{ x }} (the
clicked display.x value).
Drilldown cells stay raw until display.columnFormats adds design cues for a
column. Invoice numbers and other IDs stay ungrouped. Set useGrouping: true
for counts, format: currency for money, percentSuffix: true or
format: percent for rates, and format: date / datetime / time with
dateStyle / timeStyle for timestamps. Chart-level rounding and
percentSuffix do not carry into the drilldown.
Opening drilldowns on a dashboard
Section titled “Opening drilldowns on a dashboard”How you open drilldown depends on the display type:
- Chart and metric Query Widgets: click the widget body.
- Table Query Widgets (
display.type: table): use the expand control in the widget footer (Open drilldown). Dashboard table rows do not open drilldown on click.
If a table Query Widget has no drilldownQuery, the expand control shows a
short message that no drilldown is available.
In the Query Widget editor preview (outside the dashboard), clicking a table
row can still open drilldown when a drilldownQuery is set.
Drilldowns in shared dashboards
Section titled “Drilldowns in shared dashboards”Viewers can use drilldowns through share links when the link has Allow drilldown into data enabled. The drilldown respects the link’s filter restrictions, so viewers only see rows within the allowed scope. This option requires a Professional-tier subscription or higher.
Shared viewers open table Query Widget drilldowns with the same expand control (Open drilldown) in the widget footer.
Refresh behavior
Section titled “Refresh behavior”Preview runs immediately. Saved widgets update through the dashboard refresh pipeline.
For a Dataset source, the widget refreshes when that source updates and the dashboard is eligible to refresh.
For a Query Dataset source, the widget watches all base datasets used by the Query Dataset. The refresh interval is based on the most frequently updated source.
Example:
- Tickets sync every 1 minute.
- Ticket audit trail syncs every 15 minutes.
- A Query Dataset joins both.
- A Query Widget uses that Query Dataset.
The Query Widget uses a 1-minute target interval, and it can update when either related source changes.
Dashboards that have not been viewed recently may refresh less often. Opening the dashboard again makes it eligible for normal updates.
Working with AI assistants
Section titled “Working with AI assistants”The Query Widget editor is friendly to AI-assisted drafting because the whole definition is YAML. Use Copy AI Instructions, then paste the current definition, relevant columns, and the preview error into a chat.
Useful prompt example:
I am editing a Resplendent Query Widget Definition.
Source:- type: queryDataset- source: 5a242d39-195c-41da-a99b-93b402a4325b
Columns:- board_name: String- ticket_number: Int64- ticket_subject: String- last_email_age_minutes: Float64
Goal:Create a table showing the 100 oldest tickets by last outgoing email age.Use the Query Widget boundary, so no joins or CTEs.If the assistant writes a join, ask it to move that logic into a Query Dataset.
Troubleshooting
Section titled “Troubleshooting”Preview works, but the saved widget keeps loading
Section titled “Preview works, but the saved widget keeps loading”Preview runs in the editor. Saved widgets render in the background. If a saved widget keeps loading:
- Confirm the widget was saved.
- Check that the source dataset or Query Dataset has synced.
- Wait for the dashboard refresh worker to process the queued update.
- Reopen the editor and preview again to check for definition errors.
Widget Query must select from exactly one source relation named source
Section titled “Widget Query must select from exactly one source relation named source”The SQL must use:
from sourceDo not use the source UUID or a Query Dataset alias in a Query Widget.
Joins are not allowed
Section titled “Joins are not allowed”Move the join into a Query Dataset, save it, then use that Query Dataset as the Query Widget source.
Referenced table "board" not found
Section titled “Referenced table "board" not found”Quote dotted column names:
"board.name"The display says a column is missing
Section titled “The display says a column is missing”Make sure the SQL result uses the exact column name referenced by display.
display: type: metric value: ticket_countselect count(*) as ticket_countfrom sourcedisplay.seriesBy requires exactly one display.y column
Section titled “display.seriesBy requires exactly one display.y column”Long-form charts need one numeric y column plus seriesBy. Either drop extra
y columns, or omit seriesBy and use multiple y columns for wide data.