---
title: "Org content overlap — pairs of dashboards reading the same source tables."
method: GET
path: "/v1/orgs/{orgId}/analytics/content/overlap"
tags: ["dashboards"]
---

# Org content overlap — pairs of dashboards reading the same source tables.

`GET /v1/orgs/{orgId}/analytics/content/overlap`

## Request

- Base URL: `http://localhost:8080`
- URL: `http://localhost:8080/v1/orgs/{orgId}/analytics/content/overlap`
- Auth: one of:
  - API key in cookie `dvt_session`
  - HTTP bearer

Org-wide pairs of **live (non-deleted) dashboards** that read the same source
tables, ranked by Jaccard similarity (DVT-5385). The org-level counterpart of
`GET /v1/dashboards/{id}/related`, with the same definition: each dashboard's set
is its distinct lineage table tuples, and a pair with `sharedCount` shared tuples
scores `sharedCount / (|A| + |B| - sharedCount)`. Pairs sharing no table never
appear. Computed in one query, not one call per dashboard.

Each pair appears once, with `a.id < b.id`. Pairs are sorted by `jaccard` desc,
then `sharedCount` desc, then `a.id`, `b.id`. Admin or owner only (audit:read);
own-org only. Because it is admin-only, no per-dashboard read filter is applied.
`sharedTables` is capped at 50 tuples per pair; `sharedCount` is the true
intersection size. Invalid `min_jaccard` or `limit` are rejected with 400.
Edit provenance (lastEditedBy, lastEditedByType) is not populated on a and b.
Returns 503 if the computation exceeds its time bound for a very large org; raise
`min_jaccard` or lower `limit` and retry.

## Query parameters

- `min_jaccard` number, double
- `limit` integer

## Response `200`

The top overlapping dashboard pairs.

- object
  - `data` ContentOverlapResult, required: Response of GET /v1/orgs/{orgId}/analytics/content/overlap.
    - `minJaccard` number, double, required: The applied threshold.
    - `limit` integer, required: The applied page size.
    - `pairs` ContentOverlapPair[], required
      - `a` LineageDashboardRef, required: A bounded dashboard reference returned by the lineage read surfaces (deliberately not the full Dashboard — no spec). Everything past `updatedAt` is OPTIONAL provenance enrichment (DVT-2317) used by the Related list's meta lines; `required` is deliberately unchanged, so a client written against the original four fields keeps working. These fields mirror `Dashboard`'s own DVT-1418 provenance block field-for-field, by NAME as well as by meaning — same `lastEditedBy` / `lastEditedByType` / `lastViewedBy` / `lastViewedByAt`, resolved by the same server helpers. In particular `lastViewedByAt` is NOT `Dashboard.lastViewedAt`: that one is DVT-1407's CALLER-SCOPED read receipt ("when did *I* last open this"), while this is org-wide ("when did *anyone* last open this"). The `ByAt` name exists on `Dashboard` precisely to keep those two apart, and this schema uses it for the same reason. Every enrichment field is OMITTED — not null, not empty — when the underlying fact does not exist: a dashboard with no audit history has no `lastEditedBy`, one nobody has opened has no `lastViewedBy`. Nothing here is nullable; absence is the only "unknown" signal, matching how the server serialises it.
        - `id` string, uuid, required
        - `title` string, required
        - `visibility` 'private' | 'shared' | 'public', required
        - `updatedAt` string, date-time, required
        - `description` string: The dashboard's own description field (raw markdown, untruncated). Absent when the dashboard has no description.
        - `version` integer: The dashboard's monotonic version counter, which IS its edit count — dashboard_revisions is deliberately not consulted (it carries no org_id).
        - `ownerName` string: Display label of the dashboard's owner — display_name, else email, else "system". ALWAYS present: the server COALESCEs to "system", so this never falls back to absent the way the audit-derived fields below do.
        - `lastEditedBy` string: Display label of the most recent create/update audit actor. For an agent-attributed edit this is the API-key name / MCP client id (the DVT-1196 "via {key}" convention), so it names WHICH credential edited it; otherwise the human display_name/email/"system". Absent when the dashboard has no create/update audit history.
        - `lastEditedByType` 'user' | 'agent': The actor_type of the audit_logs row behind lastEditedBy, clamped to `user` | `agent` (audit_logs also allows `system`/`render`, folded into `user` here) — identical in name, domain and meaning to Dashboard.lastEditedByType, so a client can share one agent-badge join across both surfaces. Absent when lastEditedBy is absent.
        - `lastViewedBy` string: Display label of the most recent viewer of this dashboard, ANY actor — org-wide "who looked at this last", NOT scoped to the calling user. Absent when nobody in the org has ever viewed it.
        - `lastViewedByAt` string, date-time: Timestamp paired with lastViewedBy above. Named `ByAt`, matching Dashboard's own field, to stay clear of DVT-1407's caller-scoped `lastViewedAt`. Present exactly when lastViewedBy is.
      - `b` LineageDashboardRef, required: A bounded dashboard reference returned by the lineage read surfaces (deliberately not the full Dashboard — no spec). Everything past `updatedAt` is OPTIONAL provenance enrichment (DVT-2317) used by the Related list's meta lines; `required` is deliberately unchanged, so a client written against the original four fields keeps working. These fields mirror `Dashboard`'s own DVT-1418 provenance block field-for-field, by NAME as well as by meaning — same `lastEditedBy` / `lastEditedByType` / `lastViewedBy` / `lastViewedByAt`, resolved by the same server helpers. In particular `lastViewedByAt` is NOT `Dashboard.lastViewedAt`: that one is DVT-1407's CALLER-SCOPED read receipt ("when did *I* last open this"), while this is org-wide ("when did *anyone* last open this"). The `ByAt` name exists on `Dashboard` precisely to keep those two apart, and this schema uses it for the same reason. Every enrichment field is OMITTED — not null, not empty — when the underlying fact does not exist: a dashboard with no audit history has no `lastEditedBy`, one nobody has opened has no `lastViewedBy`. Nothing here is nullable; absence is the only "unknown" signal, matching how the server serialises it.
        - `id` string, uuid, required
        - `title` string, required
        - `visibility` 'private' | 'shared' | 'public', required
        - `updatedAt` string, date-time, required
        - `description` string: The dashboard's own description field (raw markdown, untruncated). Absent when the dashboard has no description.
        - `version` integer: The dashboard's monotonic version counter, which IS its edit count — dashboard_revisions is deliberately not consulted (it carries no org_id).
        - `ownerName` string: Display label of the dashboard's owner — display_name, else email, else "system". ALWAYS present: the server COALESCEs to "system", so this never falls back to absent the way the audit-derived fields below do.
        - `lastEditedBy` string: Display label of the most recent create/update audit actor. For an agent-attributed edit this is the API-key name / MCP client id (the DVT-1196 "via {key}" convention), so it names WHICH credential edited it; otherwise the human display_name/email/"system". Absent when the dashboard has no create/update audit history.
        - `lastEditedByType` 'user' | 'agent': The actor_type of the audit_logs row behind lastEditedBy, clamped to `user` | `agent` (audit_logs also allows `system`/`render`, folded into `user` here) — identical in name, domain and meaning to Dashboard.lastEditedByType, so a client can share one agent-badge join across both surfaces. Absent when lastEditedBy is absent.
        - `lastViewedBy` string: Display label of the most recent viewer of this dashboard, ANY actor — org-wide "who looked at this last", NOT scoped to the calling user. Absent when nobody in the org has ever viewed it.
        - `lastViewedByAt` string, date-time: Timestamp paired with lastViewedBy above. Named `ByAt`, matching Dashboard's own field, to stay clear of DVT-1407's caller-scoped `lastViewedAt`. Present exactly when lastViewedBy is.
      - `jaccard` number, double, required: |A ∩ B| / |A ∪ B| over each dashboard's distinct lineage table set (same as RelatedDashboard.jaccard). Always > 0.
      - `sharedCount` integer, required: Number of shared table tuples (the intersection size).
      - `sharedTables` SharedTable[], required: The shared table tuples, capped at 50 per pair; `sharedCount` is the true size.
        - `connectionId` string, uuid, nullable
        - `database` string
        - `schema` string
        - `table` string, required
  - `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

- `400`: Malformed request.
- `401`: Missing, invalid, or expired session.
- `403`: The caller's role does not permit this action.
- `404`: Resource does not exist.
- `503`: The service or a required dependency is temporarily unavailable.

---

[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)
