---
title: "dbt exposures: what they are and how to declare them | Metacenta"
description: "A dbt exposure records a dashboard or application that reads your models. How to declare one, which fields it needs, and how to build what it depends on."
url: "https://metacenta.com/rules/model-possibly-unused"
updated: "2026-10-14"
tokens: 921
---

Lineage integrity · rule model-possibly-unused

# dbt exposures: what they are and how to declare them

A dbt exposure is a YAML entry naming something outside dbt that uses your models, such as a dashboard, notebook or application. It adds that consumer to your lineage, with an owner.

A Metacenta review checks this under the rule **Leaf models are consumed by something**. Everything below applies whether or not you ever commission one.

## What a dbt exposure does

dbt can't see who reads its models. An exposure records that link, so you can trace a model change to the dashboard it affects and know who to tell.

## How to declare an exposure

Add an `exposures:` key to a `.yml` file under `models/`. Three fields are required: `name`, `type` and `owner`, which needs a `name` or an `email`. List what it reads under `depends_on`.

```yaml
exposures:
  - name: weekly_revenue
    type: dashboard
    maturity: high
    url: https://bi.example.com/dashboards/1
    owner:
      name: Finance analytics
    depends_on:
      - ref('fct_orders')
```

`type` is one of `dashboard`, `notebook`, `analysis`, `ml` or `application`. `maturity` is optional and takes `high`, `medium` or `low`.

## How to run everything an exposure needs

Select the exposure with `+` to include everything upstream of it.

```bash
dbt run -s +exposure:weekly_revenue
dbt test -s +exposure:weekly_revenue
```

See [dbt's exposures docs](https://docs.getdbt.com/docs/build/exposures) for every property.

## What this rule checks

This rule flags dbt models that no other model reads and that no exposure, metric or semantic model declares as a consumer. It runs only when the project declares at least one of those three, and declines otherwise. Superseded model versions are left out. So is a MetricFlow time spine, when the project has a semantic layer.

## Why it matters

If nothing reads a leaf model, it costs build time and warehouse storage for no return. A dashboard that reads it without a declared exposure looks identical, so we report the finding at low confidence.

## How to fix it

**Declare exposures for leaf models, or retire them.** Declare an exposure for each dashboard or application that reads the model. Drop the model if nothing does.

Before:

```yaml
models:
  - name: fct_orders
    description: One row per order.
```

After:

```yaml
models:
  - name: fct_orders
    description: One row per order.

exposures:
  - name: weekly_revenue
    type: dashboard
    owner:
      name: Finance analytics
    depends_on:
      - ref('fct_orders')
```

## When it is fine to leave

A mart read only by a BI tool, in a team that does not yet declare exposures, is the common case. The gap is then a missing declaration, not waste. We rate it low severity in a `marts` or `reporting` folder for that reason, and medium elsewhere.

## What we need to check it

`manifest.json` alone, which `dbt parse` writes. We list the superseded versions and time spines we left out, so the exclusion is visible rather than silent.

[Ask about a review](https://metacenta.com/enquire?src=rule-model-possibly-unused&via=md)

## Related pages

- [dbt dependency cycles in the model DAG](https://metacenta.com/rules/dependency-cycle)
- [dbt models with a hardcoded table name](https://metacenta.com/rules/hardcoded-table-reference)
- [Deep dbt lineage: long chains of models](https://metacenta.com/rules/deep-lineage)
- [dbt models with too many direct dependents](https://metacenta.com/rules/fan-out-hotspot)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
