---
title: "Get a dashboard by ID."
method: GET
path: "/v1/dashboards/{id}"
tags: ["dashboards"]
---

# Get a dashboard by ID.

`GET /v1/dashboards/{id}`

## Request

- Base URL: `http://localhost:8080`
- URL: `http://localhost:8080/v1/dashboards/{id}`
- Auth: one of:
  - API key in cookie `dvt_session`
  - HTTP bearer

Supports conditional GET (DVT-3724): a weak, content-derived `ETag` and
`Cache-Control: private, no-cache` (forces revalidation, never a shared
cache) on every 200 — the tag hashes the fully-resolved, per-caller body,
including this caller's `permissions` and this org's `governance`, so two
callers who would receive different bytes always get different tags. A
matching `If-None-Match` gets a **304** instead, with no body — the two
headers are repeated on the 304 so a caller polling only that response still
learns the current tag and policy.

## Headers

- `If-None-Match` string

## Response `200`

The dashboard, including its full current-revision spec.

- DashboardEnvelope
  - `data` Dashboard, required
    - `id` string, uuid, required
    - `folderId` string, uuid, nullable, required: The containing folder, or null when unfoldered. Set via PUT /v1/dashboards/{id}/folder.
    - `ownerId` string, uuid, required: The user who created the dashboard. Only the owner (or an org admin/owner) may move it between folders.
    - `title` string, required
    - `description` string: Optional human/agent-authored summary of the dashboard (from the dashboards.description column, falling back to spec meta.brief on list). Omitted when neither source is populated.
    - `visibility` 'private' | 'shared' | 'public', required
    - `queryRole` string: Snowflake mode only (DVT-1388): the optional pinned Snowflake role this dashboard's queries run under for every viewer (canonicalized to Snowflake's case fold). A viewer who does not hold the role gets a clean no-access query error — Snowflake enforces the pin, it can never elevate. Omitted when unpinned: each viewer's own default (primary) role is used, or, for a viewer reading via a role share, the shared role.
    - `version` integer, required: Optimistic-concurrency version. Pass this back on PATCH.
    - `schemaVersion` integer, required
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `spec` object, nullable, required: The full dvt dashboard spec from the current revision snapshot. Always `null` on collection reads (`GET /v1/dashboards`, `GET /v1/me/favorites`), which omit the spec to stay cheap — fetch `GET /v1/dashboards/{id}` for it. Populated on single-dashboard reads and writes.
    - `governance` DashboardGovernance: Secret-free governance-disclosure summary for the connections this dashboard references (ADR-0012 §"Governance disclosure rule"). Present on GET responses when the auth-method kind can be resolved; carries ONLY the auth-method enum, never any credential material. Drives the shared-role notice in the UI.
      - `sharedRole` boolean, required: True when at least one referenced connection uses a service_* auth method (one shared service identity — all viewers see the same role's data). False for per-user/OAuth-backed dashboards, which display no notice.
      - `roleMapped` boolean, required: True when every shared-role source is role-mapped (the 2026-06-14 ADR-0012 amendment): the notice then reads "data shown as your mapped role" rather than "all viewers see the same data".
      - `connections` ConnectionGovernance[], required: Per-connection auth-method kind for each source the spec references.
        - `sourceId` string, required: The panel's sourceId exactly as the spec spells it — a connection id (matched case-insensitively) or name. A connection referenced both ways appears once per distinct reference.
        - `authMethod` 'service_secret' | 'service_keypair' | 'oauth_passthrough' | 'cloud_iam' | 'spcs_token', required: The connection's auth-method kind. Never any credential material.
        - `type` 'snowflake' | 'bigquery' | 'databricks_sql' | 'redshift' | 'postgres' | 'mysql' | 'sqlite' | 'parquet_s3' | 'csv' | 'google_sheets': The connection's warehouse type (e.g. snowflake, postgres, bigquery) — the same value Connection.type carries. A non-secret enum; lets a viewer-tier client name the warehouse without the editor-only connections list.
        - `roleMapped` boolean: A non_secret_config.roleMapping policy is in force (service_* only).
        - `resolvedRoles` string[]: Roles the acting viewer's role-mapping resolution yields for this connection: the primary role first, then any secondary roles sorted ascending. Omitted when the connection is not role-mapped or the resolution is unavailable (best-effort disclosure). Role names are non-secret metadata (ADR-0012/DVT-11 contract).
    - `url` string, uri: User-facing deep link into the dvt web app for this dashboard (DVT-5113), `<app URL>/o/<org>/dashboards/<id>`, built from APP_URL, else APP_BASE_URL (default https://app.dvt.dev). Present on single-dashboard reads (`GET /v1/dashboards/{id}`) and on create/update responses; omitted on collection reads and when the host is a placeholder (`.invalid`).
    - `conformance` 'core' | 'full': The spec's portability layer (ADR-0014), computed from the spec: `core` when it uses only the renderer-neutral dvt Core profile (portable, the "industry standard" layer), `full` when it uses any escape hatch (ECharts `option` passthrough, raw `html`, `media`, `$dvtRef`, `chart:custom`, or an arbitrary-CSS canvas background). A derived, read-only property — never authored. Optional for back-compat.
    - `provenance` ProvenanceSuggestion[]: Advisory provenance suggestions (DVT-249, ADR-0004 Amendment 1) computed from the persisted spec, present on create/update responses so an agent applying without a preview sees the same `meta.brief`/`purpose`/`audience`/ `keyQuestions` guidance the apply preview returns. NON-BLOCKING — persistence already succeeded. Omitted when there is nothing to suggest.
      - `field` string, required: Dotted spec path the suggestion concerns (e.g. `meta.brief`, `meta.panels.p1.serves_question`).
      - `severity` 'gate' | 'warn' | 'info', required: `gate` — the Tier-1 authoring gate (`meta.brief` on a 3+ panel dashboard); advisory at the server, treated as blocking by the authoring boundary. `warn` — a Tier-2 gap (missing `purpose`/`audience`/`keyQuestions` at 3+ panels, or a `serves_question` index past `keyQuestions`). `info` — a Tier-3 nudge (no `dataAsOf` on a data-bound dashboard, no per-panel provenance, an agent-asserted-but-unvalidated claim, or an orphan `meta.panels` key).
      - `message` string, required: Human/agent-readable guidance for resolving the suggestion.
    - `provenanceSummary` DashboardProvenanceSummary: Bounded discovery summary projected from the spec's `meta` provenance (DVT-250), present on dashboard LIST results so an agent can find the right dashboard by purpose/audience/question without fetching each full spec. Distinct from the apply-time `provenance` suggestions (DVT-249): this is recorded *content*, not advisory lint. Omitted when the dashboard carries no provenance. `findings` is a COUNT (not the array) to keep the summary cheap.
      - `brief` string: meta.brief — the one-line thesis.
      - `findingsCount` integer: Number of meta.findings entries.
      - `audience` 'executive' | 'analyst' | 'operator': meta.audience.
      - `keyQuestions` string[]: meta.keyQuestions — the questions this dashboard answers.
      - `tags` string[]: meta.tags.
      - `documentedPanelCount` integer: Number of panels that have at least one documentation field populated (`meta.panels[id].purpose` / `intent` / `assumptions` / `notes`). Computed in SQL from the manifest JSONB; non-zero means the dashboard has agent-facing panel docs. Absent (treated as 0) when the dashboard carries no panel provenance at all (ADR-0045 §7).
      - `hasPageDocs` boolean: True when at least one page carries a `doc` object with at least one populated field (`description` / `intent` / `assumptions` / `notes`). Absent when no page has docs. Use `dvt_dashboard_get(view="docs")` to read the full per-page doc bodies (ADR-0045 §7).
    - `certification` Certification: A dashboard's derived certification standing (DVT-5223), computed at read time — dashboards carry no tier column. `tier` is the EFFECTIVE tier (highest-ranked live grant, else draft). `state` and the certified* fields describe the highest-ranked ACTIVE grant, which may be stale or expired; the effective* fields describe the grant that determined `tier`. For headless render actors only `tier` and `state` are returned.
      - `tier` 'draft' | 'published' | 'certified' | 'canonical', required
      - `state` 'live' | 'stale' | 'expired': State of the highest-ranked active grant; absent when there is none.
      - `certifiedTier` 'published' | 'certified' | 'canonical'
      - `domain` string
      - `certifiedBy` string
      - `certifiedAt` string, date-time
      - `certifiedRevisionId` string, uuid
      - `certifiedVersion` integer
      - `editsSince` integer: Revisions written since the certified one.
      - `grantedVia` string
      - `expiresAt` string, date-time
      - `queryRole` string
      - `effectiveDomain` string
      - `effectiveGrantedBy` string
      - `effectiveGrantedAt` string, date-time
      - `effectiveExpiresAt` string, date-time
    - `permissions` DashboardPermissions: Server-computed, per-actor permission hint (DVT-676) on a dashboard: which mutating actions the requesting actor may take, derived from the same authz resolution the enforcing handlers use (capability floor + per-resource ownership/visibility/ACL). An advisory UI hint so clients can show/hide affordances (e.g. a Delete menu row) without re-deriving per-resource ACL grants they cannot see — the server still enforces on every mutating call, so a client MUST NOT treat a true value as authorization. Present on dashboard LIST and GET responses. `canDelete` mirrors `canWrite` today (delete is gated on dashboard:write). `canShare` follows owner-can-share (DVT-665): the owner, org admins, and ACL-admin grantees may share — except in snowflake mode, where a caller whose LIVE tier is viewer gets `canShare: false` even as the owner or an ACL-admin grantee (DVT-3302/DVT-4721: the live tier caps them at viewer rank). Exception: on a Claude connector viewer-tier call the flags are projected to editor-tier enforcement (the tier writes run at) and `requiresConnectorTier` is set; see that field.
      - `canWrite` boolean, required
      - `canDelete` boolean, required
      - `canShare` boolean, required
      - `requiresConnectorTier` 'editor': Present (as "editor") only on a Claude connector viewer-tier call (DVT-4942): the flags above were projected to what an editor-tier connector call would be allowed, since writes run at that tier. The write is additionally refused (tier_insufficient) if the caller lacks the dvt_editor app role, which dvt-api cannot observe from a viewer-tier call.
    - `lastViewedAt` string, date-time: The calling user's own most recent view of this dashboard (DVT-1407), sourced from the dashboard_views telemetry table. Absent when the caller has never viewed this dashboard, or when there is no authenticated caller to scope by.
    - `favoritedAt` string, date-time: DVT-5306. When the caller starred this dashboard. Set only on GET /v1/me/favorites results; absent everywhere else.
    - `viewCount` integer: DVT-5306. The caller's own human views of this dashboard in the org over the last 30 days (`lastViewedAt` is all-time). Set only on GET /v1/me/favorites results; omitted when 0 and everywhere else.
    - `accessViaFolderId` string, uuid: Set on LIST results (DVT-1421) when the caller's access to this dashboard resolves via a folder-inherited ACL grant rather than ownership, an org-admin bypass, or a direct/group grant of equal or higher privilege. An advisory "via folder" hint for the UI — never authoritative, and never present for the owner or an org admin/owner (neither needs the badge). Omitted otherwise.
    - `creatorName` string: Display label for ownerId (DVT-1418): the owning user's display_name, else email, else "system". Absent only if the owner row is somehow unresolvable.
    - `lastEditedBy` string: Display label for the most recent create/update edit against this dashboard (DVT-1418), sourced from the latest matching audit_logs row for this org. For an agent-attributed edit, the API-key name or MCP client id (DVT-1196 "via {key}" convention) when present, else the editor's own display_name/email/ "system" fallback. Absent when the dashboard has no create/update audit history (e.g. pre-audit legacy rows).
    - `lastEditedByType` 'user' | 'agent': The actor_type of the audit_logs row behind lastEditedBy (DVT-1418), clamped to `user` | `agent` (audit_logs also allows `system`/`render`, folded into `user` here). Absent when lastEditedBy is absent.
    - `lastViewedBy` string: Display label (display_name/email/"system") of whoever — any actor, any type, not just the calling viewer — most recently viewed this dashboard org-wide (DVT-1418), sourced from the dashboard_views telemetry table. Distinct from `lastViewedAt` above (DVT-1407's caller-scoped read receipt). Absent when the dashboard has never been viewed by anyone in this org.
    - `lastViewedByAt` string, date-time: Timestamp paired with lastViewedBy above (DVT-1418). Named distinctly from `lastViewedAt` (DVT-1407, caller-scoped) to avoid colliding with that already-shipped, differently-scoped field. Absent when lastViewedBy is absent.
  - `meta` Meta, required: Response metadata.
    - `requestId` string, required: Correlates with the X-Request-Id header.
    - `total` integer: Item count, present on collection responses.
    - `nextCursor` string, nullable: Opaque keyset-pagination cursor, present on paginated responses (e.g. GET /v1/dashboards) when more rows exist beyond this page. Pass it back as the `cursor` query param to fetch the next page; absent or null on the last page.
    - `roleResolveDegraded` boolean: Snowflake mode only (DVT-2260), on GET /v1/dashboards: present and true when the caller's live Snowflake role resolution failed or was unavailable — role-shared dashboards may be missing from this page. Omitted (not merely false) whenever resolution succeeded or the role-share arm does not apply (off snowflake mode, an org admin/owner, or off the ingress listener).

## Other responses

- `304`: Unchanged since the `If-None-Match` value (DVT-3724). No body; `ETag` and `Cache-Control` are repeated.
- `401`: Missing, invalid, or expired session.
- `404`: Resource does not exist.

---

[API](https://skmtc.dev/dvt/apis/dvt-rest-api.md) · [All operations](https://skmtc.dev/dvt/apis/dvt-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dvt/apis/dvt-rest-api/revisions/00016f504165?raw)
