---
title: "List catalog objects (databases/schemas/relations) for the Explorer's live browse tree."
method: POST
path: "/v1/data/objects"
tags: ["data"]
---

# List catalog objects (databases/schemas/relations) for the Explorer's live browse tree.

`POST /v1/data/objects`

## Request

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

Lists the databases, schemas, or tables/views reachable through `sourceId` at one
catalog `level`, for the Explorer's catalog tree (DVT-1807/1809). The relation scope is
given as TYPED PARTS (`database`/`schema`), each validated against the same strict
identifier allowlist as `POST /v1/data/describe` — never one dotted string (DVT-7).
`database`/`schema` are optional per level; an absent or unreadable parent degrades to
an empty `objects` list rather than an error.

Same tenant gate, render-session source scoping, and identity resolution as
`POST /v1/data/query` and `POST /v1/data/describe`: a cross-tenant `sourceId` returns
404, and the listing runs under the SAME resolved warehouse identity the query path
uses (role-mapped Snowflake service, per-user `oauth_passthrough`, or the shared
service identity) — a role-mapped or per-user viewer sees only what THEIR identity can
see when browsing, never a broader service-identity view.

Successful listings are cached per resolved identity for 60 seconds (ADR-0012 —
the cache key always includes the identity dimension, so two viewers under different
identities never share an entry) for auth methods safe to cache under that identity
dimension; a Snowflake caller's-rights (`spcs_token`) or `cloud_iam` connection is
never cached (every distinct caller would otherwise collapse into one shared bucket).
`POST /v1/data/preview` never reads or writes this cache.

## Request body

- ObjectsRequest
  - `sourceId` string, required: Connection name or UUID (resolved per connection-as-code.md).
  - `level` 'databases' | 'schemas' | 'relations', required: Catalog level to list.
  - `database` string: Parent database part, when scoping "schemas" or "relations". Optional — an absent or unreadable parent degrades to an empty list rather than an error. Identifier allowlist: letters, digits, _ or $, up to 255 chars.
  - `schema` string: Parent schema part, when scoping "relations". Optional, same degrade behavior as database. Identifier allowlist: letters, digits, _ or $, up to 255 chars.
  - `includeComments` boolean: Include each object's warehouse COMMENT, when available.

## Response `200`

Catalog objects at the requested level (empty list when not visible/found).

- ObjectsResponseEnvelope
  - `data` object, required
    - `objects` object[], required: One entry per catalog object at the requested level, in catalog order. Empty when the parent is not visible to the connection's identity or does not exist.
      - `name` string, required: Source object name.
      - `kind` string, required: e.g. "database", "schema", "table", "view".
      - `comment` string, nullable: The object's warehouse COMMENT, or null when none is set or not requested.
  - `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`: Three shapes, distinguished by the Problem `type`: - `https://docs.dvt.dev/errors/bad-request` (the default) — malformed request or relation name parts. - `https://docs.dvt.dev/errors/bad-query` — the engine rejected the metadata read (relayed through the same engine-error mapping as `POST /v1/data/query`, e.g. a statement timeout). - `https://docs.dvt.dev/errors/caller-session-unconfirmed` (DVT-3780, DVT-4299, DVT-4318) — the caller's-rights (Snowflake `spcs_token`) session's safety parameters could not be confirmed in band, neither as requested on the login nor after the engine re-pinned them with `ALTER SESSION SET` and read them back again. If that re-pin is itself refused but `MULTI_STATEMENT_COUNT` still reads back as 1, the engine accepts the session in client-bound mode instead, enforcing the statement timeout client-side on every statement — the bound is enforced by dvt cancelling the statement from its own process; a statement detached by an engine restart falls back to the account-level `STATEMENT_TIMEOUT_IN_SECONDS`. This error remains for a parameter read that fails outright, a single-statement count that does not confirm, a re-pin that "succeeds" but still disagrees on read-back, or a non-positive configured timeout. Not a grant or name problem and not remediable by the consumer: report it to dvt support.
- `401`: Missing, invalid, or expired session.
- `403`: The caller's role does not permit this action.
- `404`: Resource does not exist.
- `409`: Two shapes, distinguished by the Problem `type`: - `https://docs.dvt.dev/errors/oauth-consent-required` — the source is a per-user OAuth (`oauth_passthrough`) connection and the caller has not connected their warehouse account yet, or their stored grant has expired/been revoked (DVT-123). The client starts the consent flow at `GET /v1/connections/{connectionId}/oauth/authorize` and retries. - `https://docs.dvt.dev/errors/not-queryable-connection` (DVT-2540) — the resolved connection is configured for writeback only (`kind='writeback'`) and cannot be used as a query source. No self-heal retry; bind a pushdown connection to this `sourceId` instead. Both shapes carry a `connectionId` extension (the consent target, or the non-queryable connection).
- `429`: Two causes, distinguished by the Problem `type` (DVT-3464 split this from a single shared `errors/rate-limited`, which made an engine-capacity shed indistinguishable from an actual rate limit): - `https://docs.dvt.dev/errors/rate-limited` — a rate or concurrency limiter for this operation rejected the request before it reached the engine. See `components/responses/RateLimited` for the general per-actor SEC-2 tier; some operations stack an additional operation-specific limiter (the query submit rate limit, the per-org running-job cap, or the trial query ceiling). - `https://docs.dvt.dev/errors/engine-at-capacity` — the engine's own admission control (ADR-0053) shed the request before it reached the warehouse — a dvt engine limit, not a warehouse or rate problem (DVT-765). `suggestion` names the actual wait in seconds and, when the engine reports a scope, whether the per-org or global engine concurrency cap was hit. Both carry `Retry-After` (seconds); back off for at least that long before retrying.
- `502`: The analytics engine could not be reached (`https://docs.dvt.dev/errors/engine-unavailable`) or returned an error (`https://docs.dvt.dev/errors/engine-error`). One 502 on this shape is NOT an engine fault and says so: when Snowflake refuses the caller's-rights LOGIN, the Problem type is `https://docs.dvt.dev/errors/caller-connect-refused` — no session was established and the caller's SQL was never reached, so the remediation is a fresh dvt session rather than the engine logs (DVT-4345).

---

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