---
title: "Global search — the ⌘K palette/drill-down endpoint."
method: GET
path: "/v1/search"
tags: ["search"]
---

# Global search — the ⌘K palette/drill-down endpoint.

`GET /v1/search`

## Request

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

Full-text (Postgres `websearch_to_tsquery` + `ts_rank_cd`) and trigram
typo-tolerant search over dvt objects, grouped by kind. Slice B1 (DVT-1814)
covered dashboards only; slice B2 (DVT-1816) adds the synced warehouse
catalog's `catalog_object` (tables/views) and `schema` (distinct database +
schema pairs) kinds. A requested kind this server build doesn't recognize at
all returns an empty group rather than an error, so clients can roll out a
new kind ahead of server support. `catalog_column` is deliberately never a
top-level kind — a column match always folds into its parent
`catalog_object` result (see `matchedIn` below) — so requesting it also
resolves to an empty group, permanently, not a gap to be filled later.

The `catalog_object`/`schema` kinds additionally require the API key's
`data:query` scope (same requirement as `GET /v1/connections/{id}/catalog` —
synced catalog names/comments may be warehouse PII). An API key missing that
scope gets an empty group for just those kinds, not a 403 for the whole
request; cookie/browser-session and unscoped-key callers are unaffected.

Two modes, selected by which kind parameter is present:
  - **Palette mode** (no `kind`, optional `kinds`): returns up to `limit`
    top-ranked results per requested kind (default: every supported kind).
    `cursor` is not valid in this mode.
  - **Drill-down mode** (`kind` present): returns one page of up to `limit`
    results for that single kind, in keyset order; pass a group's
    `nextCursor` back as `cursor` to continue.

Ranking blends, per kind:
  - `dashboard`: full-text rank over a weighted tsvector (title > description/
    brief > tags/keyQuestions > audience), a trigram typo-tolerance bonus
    against the title, and a bonus for dashboards whose comment thread (not
    the dashboard itself) matches — which is why a hit can appear even when
    `q` doesn't appear in its title/description. Read-visibility matches
    `GET /v1/dashboards` exactly (owner, public, ACL-granted, or role-shared
    in snowflake mode) — a result is never returned for an object the caller
    cannot read.
  - `catalog_object`: full-text rank over a weighted tsvector (table/view name
    > its schema+database > its comment), a trigram typo-tolerance bonus
    against the name, and a bonus when at least one child column matches —
    a matching column is folded into the table/view's own result rather than
    ever appearing as its own row; `matchedIn` carries a `"column:<name>"`
    entry per matching column (capped at 5).
  - `schema`: trigram-only (no tsvector) match against the schema name and/or
    its parent database name.

`snippet` is HTML in every kind, uniformly: a `ts_headline` excerpt with
matched term(s) wrapped in `<b>...</b>` for `dashboard`; for `catalog_object` /
`schema` it is the object's comment (if the sync captured one) or else its
dotted `database.schema` context path, HTML-escaped server-side — plain text
content, no highlight markup of its own, but escaped so a client that treats
every snippet as HTML (e.g. sanitizes it) never misinterprets a warehouse
comment containing `<`, `>`, or `&` as markup.

## Query parameters

- `q` string, required
- `kinds` string
- `kind` string
- `limit` integer
- `cursor` string

## Response `200`

Search results grouped by kind.

- SearchEnvelope
  - `data` SearchResponse, required
    - `query` string, required: The `q` this response was computed for (echoed back).
    - `groups` SearchGroup[], required
      - `kind` string, required: The object kind this group's results belong to (today: "dashboard", "catalog_object", "schema" — see GET /v1/search's description for the unsupported-kind and data:query-scope contracts).
      - `results` SearchResult[], required
        - `kind` string, required
        - `id` string, uuid, required
        - `title` string, required
        - `snippet` string, required: Always HTML — the same encoding across every kind. For kind "dashboard": a `ts_headline`-generated excerpt with the matched term(s) wrapped in `<b>...</b>`, drawn from title/description/brief. For "catalog_object"/"schema": the object's comment (if the sync captured one) or its dotted `database.schema` context path, HTML-escaped server-side — plain text content and no highlight markup of its own, but escaped so a client that treats every snippet as HTML never misinterprets a warehouse comment containing `<`, `>`, or `&` as markup.
        - `score` number, float, required: The ranking score (full-text rank + typo-tolerance bonus + a fold-in bonus for "catalog_object" when a child column also matched, or a comment-match bonus for "dashboard"). Opaque beyond relative ordering within the SAME kind — never compare a score across different kinds or across different queries.
        - `matchedIn` string[], required: Which field(s) of the object matched `q` — a free-form list, not a closed enum, since it varies by kind: "dashboard" uses title / description / tags / audience / comment; "catalog_object" uses name / schema / comment plus one `"column:<name>"` entry per matching child column (a column match is never its own top-level result — it always folds in here); "schema" uses name / database.
        - `updatedAt` string, date-time, required
        - `link` SearchLink, required: A discriminated-by-kind pointer the client resolves to a route via its own URL-builder — the server does not hardcode a path shape here.
          - `kind` string, required
          - `dashboardId` string, uuid: Present when kind is "dashboard".
          - `connectionId` string, uuid: Present when kind is "catalog_object" or "schema".
          - `database` string: Present when kind is "catalog_object" (may be "" if the object has no database ancestor, e.g. a database-less warehouse) or "schema" (may be "" if the schema has no database ancestor).
          - `schema` string: Present when kind is "catalog_object" (may be "" if the object has no schema ancestor) or "schema" (the schema's own name).
          - `name` string: Present when kind is "catalog_object" (the table/view's own name). Absent/"" for "schema" — the schema's name is carried in `schema` instead, not duplicated here.
        - `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
      - `nextCursor` string: Opaque keyset cursor for this kind's next page (drill-down mode only). Pass back as `cursor` alongside this same single `kind` to continue. Absent when there is no next page.
  - `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.

---

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