Skip to content

Lifecycle: export, import & restore

Dashboards, data models and governed metrics can leave a workspace as definitions — portable JSON artifacts with no data and no credentials in them — and be imported into another workspace. Alongside that, dashboards carry immutable version snapshots, a field-level diff, and a restore. Together they cover the dev → prod path: build in one workspace, export, import, promote, and roll back if it goes wrong.

Every list page has an export affordance:

Where Control
Dashboards list a download icon on the card (hover) — accessible name Export <name>
Data Models list a download icon on the row (hover) — accessible name Export <name>
Metrics hub an Export button on the row (hover)

You get a lowercase, filesystem-safe name with spaces and punctuation replaced by hyphens: <name>.<assetType>.json, for example q3-revenue.dashboard.json.

This is the part that matters most, so be precise about it. An exported artifact contains no data, no credentials, and no workspace IDs. Every binding to something workspace-local is replaced by a symbolic reference:

Reference Means
$model:0, $source:0, $metric:0 a positional reference — you map it to a real object at import
$field:{tableAlias}.{fieldName} a model field, resolved by name in the target model
$table:{tableAlias} a model table, resolved by alias
$measure:{name} a SquareX measure, resolved by name

The positional references are listed in the artifact’s references block with their original names, so the import screen can show you “Orders Model” rather than a bare token.

Serialization is deterministic — sorted keys, no timestamps, and symbolic references instead of workspace-local UUIDs — so the same unchanged asset exports byte-identical every time. That is what makes these files git-diffable: commit them, and a pull request shows exactly what changed.

Dashboard artifacts also embed definitions for dashboard-bound saved queries (name, SQL, description, and a symbolic source reference). Import recreates those queries with fresh IDs before it creates the cards, so saved-query cards do not arrive as “Not configured.”

Click Import on the Dashboards, Data Models or Metrics list. The Import definition dialog opens.

  1. Paste the JSON into Exported artifact (paste the JSON, or choose a file), or pick the file with Artifact file. Invalid JSON shows “Not valid JSON yet.”
  2. As soon as the JSON parses, the Map references to this workspace section appears — one row per positional reference, labelled with its original name (e.g. Orders Model (model), Prod Postgres (source)). Pick the matching object in this workspace from each dropdown (they start at choose…).
  3. Optionally press Check — a dry run that resolves everything without writing, and reports anything it couldn’t match.
  4. Press Import.

Import stays disabled until every required reference is mapped. Mapped IDs are validated against the current workspace, so you cannot graft an asset onto another tenant’s model.

The API has an allowUnresolvedFields escape hatch, but the web dialog does not expose it. In the UI, unresolved $field, $table, or $measure references block the real import after Check reports them.

Import creates a new asset. It does not update, merge into, or overwrite an existing dashboard, model or metric. Treat the artifact as the source of truth and the import as a deploy of a new object, not an in-place upgrade.

Metric names are unique per workspace, so a collision is different: Check reports the conflict and the dialog proposes <name> (imported). Choose a different name before importing. Dashboard and model imports still mint a new asset rather than updating one with the same name.

Once the positional refs are mapped, fields, tables and measures resolve by name against the target model. If the target model’s aliases and field names match the origin’s, everything binds. If they don’t, you get an amber warning — “Unresolved in the mapped model (aliases/field names must match):” — with the list. Fix the target model’s naming, or re-export from a workspace that matches.

Open a dashboard in the editor and expand the Deployment Pipeline strip at the bottom. (It’s hidden in preview mode, and needs edit permission.)

Publish New Version snapshots the dashboard — name, description, layout, theme, filter state, pages, cards and filters — as an immutable version. The strip shows the current version number and whether it’s Published or Draft — publish to deploy, plus the Version History (latest 10).

The diff compares two snapshots field by field and reports one entry per changed leaf, with readable paths — cards and filters are matched by ID but labelled by title, so you read:

cards."Revenue by month".chartSpec.yFields[0].aggregation sum → avg

The summary counts + added, − removed, ~ changed, and an unchanged comparison says “No differences — the two versions are identical.”

You reach it from four places:

Control Compares Gates anything?
Changes since v{n} the published version → your working state no — preview
diff (per version row) the previous version → that version no — preview
Promote to {stage} the version already on the stage → the one you’re promoting yes — see below
restore your working state → the version you’re restoring yes — it’s the confirm step

Promote to {stage} deploys a version to the next stage in your pipeline (auto-publishing first if that stage requires it). When the target stage already has a different version deployed, the diff opens first as a confirmation — you see exactly what the promotion would change, and the deploy only fires when you confirm Promote v{n} to {stage}.

Stages are workspace-configurable, and a version can be deployed to any stage directly (Deploy v{n}) or removed from one. Rolling a stage back is just deploying an older version to it.

In Version History, every version other than the current one has a restore link. Clicking it opens the diff — your working state on the left, that version on the right — and the confirm button reads Replace working state with v{n}.

Restore replaces the dashboard’s working state with the snapshot (keeping the original card and filter IDs), then auto-publishes, so the restore is itself a new version. Nothing is lost: the history records that the new version was restored from the old one, and you can restore back out of it.

Publishing a data model now stores a real definition snapshot. Open the model’s Version History and choose Restore on an older version to put its tables, relationships, fields, measures and policies back into the working definition. Existing IDs are preserved where possible so dashboard cards, filters and data-quality tests keep their bindings.

Model restore does not publish automatically and does not create another version. Review the restored working definition, validate it, and publish it when it is ready; that publish creates the next snapshot. The restore runs in one transaction and reconciles tables, fields, relationships, hierarchies, RLS policies, measures, and table transforms in dependency-safe order.

Version history starts with the first publish made after snapshot versioning was introduced; older version numbers do not have a snapshot to restore. The current model dialog executes Restore directly—there is no model diff or second confirmation step.

  • Field-level diff, promotion stages and auto-publishing restore remain dashboard-only. Models have snapshot history and restore, but no promotion pipeline or pre-restore field diff. Metrics still have no version restore.
  • Import always creates. There is no “update this existing dashboard from this artifact” path.
  • Fields resolve by name, so renaming a field in the target model breaks imports that referenced it.