---
title: "The dashboard's card thumbnail image, streamed same-origin."
method: GET
path: "/v1/dashboards/{id}/thumbnail"
tags: ["renders"]
---

# The dashboard's card thumbnail image, streamed same-origin.

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

## Request

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

Streams the newest usable whole-dashboard PNG render for the dashboard's **current
version** — a small default-viewport (1280×800) screenshot kept fresh by a background
sweeper (DVT-1205), never wider than 25 days old. The API fetches the artifact from R2
itself and streams the bytes back same-origin, so the dashboards page card grid never
needs the object-storage host in `img-src` — the R2 host is deliberately NOT
allowlisted there; see `web/src/media-safety.ts` `ALLOWED_MEDIA_HOSTS` and the
lockstep assertion in `web/src/csp.test.ts` (no CSP change). Supports
conditional GET via `If-None-Match`/`ETag` (the render id). Returns **404** when no
thumbnail-eligible render exists yet (e.g. a brand-new dashboard the sweeper hasn't
reached) or the R2 object is no longer available — callers should fall back to a
placeholder, not treat it as an error.

An optional `?revision=N` (DVT-1466 stage 3) pins the lookup to a specific past
revision instead of the dashboard's current version — used by the activity feed's
before/after edit thumbnails (ActivityEvent.beforeThumbUrl/afterThumbUrl). Resolves
to the newest succeeded render at or before revision N (nearest-older fallback, same
25-day artifact horizon); still never re-renders — a missing artifact is a 404, not a
fresh render of old history (ADR-0011).

An optional `?page=P` (DVT-1483, 0-based) prefers a succeeded render of that page at
or before the resolved revision over the sweeper's page-0 render, which previously
always won regardless of which page was edited. Falls back to the page-0 render
(never a 404) when page P has no qualifying artifact yet. Only takes effect alongside
`?revision=N` — `?page=P` with no `?revision=` is accepted (validated, never 400) but
has no effect on the current-version lookup, which is always the sweeper's page-0
render.

## Query parameters

- `revision` integer
- `page` integer

## Headers

- `If-None-Match` string

## Response `200`

The thumbnail PNG bytes.

## Other responses

- `304`: The thumbnail is unchanged since the `If-None-Match` value. No body; `ETag` and `Cache-Control` are repeated.
- `400`: Malformed request.
- `401`: Missing, invalid, or expired session.
- `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)
