---
title: "This dashboard's table-level lineage."
method: GET
path: "/v1/dashboards/{id}/lineage"
tags: ["lineage"]
---

# This dashboard's table-level lineage.

`GET /v1/dashboards/{id}/lineage`

## Request

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

Returns the dashboard's current table-level lineage — the (panel, referenced table)
edges C1's staleness sweep extracts via sqlglot on each panel's SQL (`source:
sql_parse`). Requires dashboard read access on this dashboard (a non-reader gets 404,
same as GET /v1/dashboards/{id}).

`status` is `"pending"` (not 404, not an error) when the dashboard has never been
through an extraction pass yet — a brand-new dashboard, or one the staleness sweep
hasn't reached — `"ok"`/`"partial"`/`"error"` once it has (`"partial"` means at least
one panel had a non-fatal issue, surfaced in `warnings`; `"error"` means the last
attempt could not reach the extraction engine at all — prior edges, if any, are left
untouched, not cleared).

LOAD-BEARING: each edge's `resolvedObjectId` is re-resolved against the synced
catalog (A3) at READ TIME, on every call — it is never read back from storage.
`catalog_objects.id` is re-minted on every catalog sync pass, so a persisted
resolution would silently dangle after the very next sync; `resolvedObjectId` is
`null` whenever the table isn't (or isn't yet) resolvable, which is a normal outcome,
not an error.

BOUNDED: `edges` is capped at 500 edges — a deterministic safety bound, not a
paginator (the cut is taken after a stable sort, so the same dashboard always yields
the same 500). There is no paging parameter and no cursor; a response holding exactly
500 edges means there may be more that were never returned, and the true total is
unknown.

## Response `200`

The dashboard's current lineage.

- object
  - `data` DashboardLineage, required: GET /v1/dashboards/{id}/lineage's response body.
    - `status` 'pending' | 'ok' | 'partial' | 'error', required: "pending" when the dashboard has never been through an extraction pass yet (never a 404, never an error).
    - `extractedAt` string, date-time, nullable, required: When the last extraction attempt ran; null while status is "pending". `status == "pending"` is the discriminator for this null case — do not infer "pending" from a zero-value timestamp.
    - `warnings` LineageWarning[], required
      - `panelSlug` string, required
      - `kind` 'dynamic' | 'parse_error', required
      - `message` string, required
      - `line` integer: 1-based line of the parse failure (DVT-2322). Present only for `kind: parse_error` warnings the engine reported a position for; OMITTED (not null) otherwise, including on every warning row persisted before this field existed. Positions index the placeholder-substituted SQL the engine parsed. Named placeholders are length-padded so columns match the stored panel SQL exactly; a positional %s placeholder shifts later columns on its line right; %% shifts them left by one. Line numbers always match the stored SQL.
      - `col` integer: 1-based column of the parse failure, in the same coordinate frame as `line` (see its description). Present and omitted under exactly the same conditions as `line`.
    - `edges` LineageEdge[], required
      - `panelSlug` string, required
      - `pageSlug` string: The id of the spec `pages[]` entry the panel lives on (DVT-2315) — what the Lineage graph groups panels by. DELIBERATELY not in `required` and OMITTED rather than served as null: it is absent for a single-page spec's top-level `panels[]` (there is no page to name) and for edge rows extracted before this field existed, which the background sweep rewrites on its next pass. Both cases mean the same thing to a client — "ungrouped" — so treat an absent key exactly as you would an empty one; never assume the key is present.
      - `connectionId` string, uuid, nullable: The connection this edge's table belongs to; null for an env/demo source with no connection row.
      - `database` string, required: The raw database part as parsed; empty string for an unqualified reference.
      - `schema` string, required: The raw schema part as parsed; empty string for an unqualified reference.
      - `table` string, required: The raw table name as parsed.
      - `resolvedObjectId` string, uuid, nullable: The matching catalog_objects id, re-resolved live; null when not resolved.
  - `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

- `401`: Missing, invalid, or expired session.
- `403`: The caller's role does not permit this action.
- `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)
