Skip to content

Dashboard spec (v1)

Every field of the Dashies spec v1 YAML document: types, bounds, defaults, and the combinations the schema refuses outright.

The spec is a YAML document you hand to publish_dashboard. The server compiles it into HTML, validates it, runs each dataset's SQL once to seed the data island, and stores the manifest that later refreshes read.

The machine-readable schema is published at https://dashies.xyz/schema/dash/v1.json. This page is that schema in prose, and a build guard fails this site when a field or a tile type exists in the schema with no entry here.

Everything on this page is the shape contract. The rules about whether the numbers are right live in Measure correctness and Designing the cube.

Top-level fields

FieldTypeRequiredBounds and notes
dashiesintegeryesAlways 1. The format version.
titlestringyes1 to 120 characters. The dashboard heading.
sourceobjectyesWhere the SQL runs and how often. See Source.
datasetsmapyes1 to 8 entries. Keys match ^[a-z][a-z0-9_]{0,31}$. See Datasets.
tilesarrayone of1 to 64 tiles. See Tile types. Mutually exclusive with look.
lookobjectone ofYour own HTML instead of generated tiles. Mutually exclusive with tiles. See Look.
slugstringno1 to 64 characters matching ^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$. Defaults to the slug in the publish path.
descriptionstringnoUp to 4,000 characters of Markdown, rendered under the title.
intentstringnoUp to 2,000 characters. Notes for whoever edits this spec next. Never rendered.
layoutobjectnocolumns (always 12) and max_width (640 to 1920). Refused alongside look.
themeobjectnoSee Theme. Refused alongside look.

Exactly one of tiles or look is required. Declaring look also forbids theme and layout, because in look mode the markup is yours and there is no generated chrome for either to style.

Source

FieldTypeRequiredBounds and notes
connectionstringyesself, or the UUID of a warehouse connection you may use.
schedulestringyesOne of manual, hourly, daily, weekly, monthly.
timezonestringno1 to 64 characters. An IANA zone name, checked against the database's zone table.

self is the built-in no-PII Dashies metrics view and needs no setup. A warehouse connection is created in the Dashies web app, never through MCP, and requires a paid plan. Resolution rules and the refusals are in Shared rules.

timezone anchors the schedule, not the SQL. Nothing propagates it into your query. Bucket your dates in your business time zone inside the SQL, and never rely on a session time zone: see SQL dialect notes.

Datasets

A dataset is one SQL statement plus the declaration of what its output columns mean. One to eight per dashboard.

FieldTypeRequiredBounds and notes
sqlstringyes8 to 100,000 characters. One read-only SELECT.
dimensionsmapyes1 to 12 entries. Keys match ^[a-z][a-z0-9_]{0,63}$.
measuresmapyes1 to 24 entries. Same key pattern.
modestringnocube, lattice, hybrid or rows. Resolved for you when absent.
rows_sqlstringyes for hybrid8 to 100,000 characters. Refused unless mode: hybrid is declared explicitly.
rows_windowintegerno1 to 8,000,000. Refused unless mode is explicitly rows or hybrid.
dataobjectno{ mode: inline } or { mode: parquet }. Refused unless mode: rows is declared explicitly.
intentstringnoUp to 2,000 characters. Notes for the next editor. Never rendered.

Each key of dimensions and measures must equal an output column name of the SQL. Letter-case differences are reconciled for you; two output columns that differ only by case are refused, naming both.

How mode is resolved when you omit it

The resolver only ever picks cube or lattice. Both ship aggregates. rows and hybrid ship row-level data to every viewer, so they are always an explicit opt-in and are never chosen for you.

ConditionResolved mode
Every measure is a sum, count, min, max or a ratiocube
A non-additive measure, every dimension bounded, and no multi or range filter over this datasetlattice
A non-additive measure with an unbounded dimensionrefused: declare mode: rows, or bound the dimensions
A multi or range filter over a non-composable measurerefused: declare mode: hybrid with rows_sql, or drop that filter

Because the three row-level fields above are refused when mode is absent, a dataset that never declares a mode cannot become rows, hybrid or Parquet by accident.

The trade-offs between the four modes are in Datasets and the four modes.

Dimensions

FieldTypeRequiredBounds and notes
typestringnocategory or date. Defaults to a category.
labelstringnoUp to 80 characters. Display name.
domainsarraysee below1 to 200 unique strings, numbers or booleans. Refused on a date dimension.
bucketsintegersee below1 to 1,000. Refused on anything but a date dimension.
intentstringnoUp to 1,000 characters. Never rendered.

On a lattice or hybrid dataset every dimension must be bounded: a category dimension needs domains, a date dimension needs buckets. That declaration is what the 50,000-cell estimate is computed from, so an unbounded dimension there is refused rather than guessed at.

Measures

A measure is either an aggregate or a ratio. The two shapes are exclusive.

An aggregate measure:

FieldTypeRequiredBounds and notes
aggstringyesSee the table of allowed aggregates below.
columnstringnoThe output column to read, when it differs from the measure key. Refused on a cube dataset.
percentilenumbernoStrictly between 0 and 1. Refused unless agg is percentile_cont or percentile_disc.
stockbooleannoDeclares a point-in-time level rather than a flow.
labelstringnoUp to 80 characters.
unitobjectnoSee Units.
intentstringnoUp to 1,000 characters. Never rendered.

A ratio measure:

FieldTypeRequiredBounds and notes
ratioobjectyes{ num, den }, each naming another measure key. Optional num_scope: all and den_scope: all compute that side over the unfiltered dataset.
labelstringnoUp to 80 characters.
unitobjectnoSee Units.
intentstringnoUp to 1,000 characters. Never rendered.

A ratio is the correct way to express an average, a rate or a share. Store the numerator and the denominator as additive measures and let the runtime divide under whatever filter is on screen; a stored average cannot be re-derived once the filter changes.

The schema declares twelve aggregates, and the mode decides which are legal:

aggcubelattice and hybridrows
sum, count, min, maxyesyesyes
avg, count_distinct, median, percentile_contnoyesyes
percentile_disc, stddev, variance, modenoyesno

stock: true marks a measure that is a level rather than a flow, so it is never summed across periods. Omitting it on a level is the single most expensive authoring mistake in this product: see Measure correctness.

Units

FieldTypeRequiredBounds and notes
kindstringyescurrency, percent, count or number.
scalestringsee belowcents or units for currency; fraction or points for percent. Required for both, and refused for count and number.
currencystringnoThree upper-case letters, an ISO 4217 code. Refused unless kind is currency.
decimalsintegerno0 to 6.
compactbooleannoRenders 1,200,000 as 1.2M.

scale says how the raw number is stored. A currency measure holding whole currency units declares scale: units; a percent measure holding 0.25 declares scale: fraction.

`cents` and `points` are refused at publish today

The schema accepts all four values, but the display divisor that would rescale them is not live, so the compiler blocks any measure declaring scale: cents or scale: points on every dataset mode. The refusal reads:

scale "cents" requires the display-divisor runtime (not yet live): divide in SQL (sum(amount_cents)/100.0 as revenue) and declare scale: units

It names cents in both cases. Divide in the SQL, and declare scale: units for currency or scale: fraction for percent. That is one line in your query and it is the only shape that publishes.

Look

look replaces the generated tiles with your own markup.

FieldTypeRequiredBounds and notes
htmlstringone of1 to 4,194,304 characters. Your complete dashboard body.
fromstringone of1 to 64 characters matching the slug pattern. Reuse the markup of another dashboard of yours.

Exactly one of the two. Every data-dash binding in your markup must resolve against the datasets you declared, or the publish is refused; the roles and attributes are in Runtime attributes.

Declaring look forbids tiles, theme and layout.

Theme

FieldTypeRequiredBounds and notes
accentstringnoA six-digit hex colour, #RRGGBB.
fontstringnosans, serif or mono.
densitystringnocompact, comfortable or spacious.
modestringnolight, dark or auto.
cssstringnoUp to 50,000 characters, injected as a stylesheet.

css is the escape hatch for anything the four knobs above do not cover. The custom properties it should target are in Theme tokens.

Check it worked

Publish with dry_run: true. The pipeline compiles, validates and seeds exactly as a real publish would, writes nothing, and reports the resolved mode for every dataset, the compiled byte size against the publish cap, and the rows each dataset seeded.