> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hebbianrobotics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> What Quality Indexing measures about a corpus of footage, and what the API publishes.

Quality Indexing measures a corpus of video and publishes the measurements over
an API. It does not publish a verdict on the footage. What counts as good
depends on what you intend to train, so you get the counts and the denominators
and decide for yourself.

## The model

Three nouns carry the whole API.

<CardGroup cols={3}>
  <Card title="Dataset" icon="database">
    A corpus of footage, addressed by id. Every id you pass to the API comes
    from `GET /api/quality/datasets`, which lists the ones your credential can
    read.
  </Card>

  <Card title="Clip" icon="film">
    One span of one source video: the unit everything is measured over, and the
    join key between the endpoints. A clip's identity survives renumbering.
  </Card>

  <Card title="Measurement" icon="ruler">
    One thing a dataset can answer for. A dataset carries a measurement once
    the work behind it is stored; the listing says which of the four it has.
  </Card>
</CardGroup>

## What the API answers

| Endpoint                         | Answers                                                                                                       |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `GET /api/quality/datasets`      | Which datasets you can read, and which measurements each carries.                                             |
| `GET /api/quality/vocabulary`    | What the corpus contains — environments, objects and actions — as published terms with tiers and clip counts. |
| `GET /api/quality/cleanliness`   | How legible the footage is, as an exhaustive partition of the measured frames.                                |
| `GET /api/quality/hand-activity` | How much of the measured footage shows hands, two hands, and active manipulation.                             |
| `GET /api/quality/clips`         | One row per clip, with its terms and — on request — its frame counts and hand-activity seconds.               |

The first three are corpus-level reductions. `clips` is the same underlying
measurements unreduced, so you can re-total them over any subset you care
about: one operator, one site, one week of collection.

`GET /api/access` sits beside all of them and answers what your credential can
do. It never errors on a missing or bad one, which makes it the right first
call.

## What it does not publish

The endpoints publish results and the populations they were computed over.
They do not publish how the results were produced — no thresholds, no model or
tooling identity, no agreement rules, no scoring formula.

That boundary is deliberate, and it is not withholding for its own sake. A
reader who could vary the cutoffs could re-partition a corpus into whatever
answer suited them, and a number nobody can re-derive to taste is worth more
than one anybody can. Where a rule matters for comparing two answers, it is
published as an opaque version token instead: same token, directly comparable;
different tokens, not.

## Conventions

**Counts and seconds, not shares, wherever a denominator is yours to choose.**
Corpus-level endpoints do publish shares, because at corpus level the
denominator is unambiguous — and each one arrives beside the population it was
computed over.

**An absent field is not a zero.** A clip with no `frame_quality` was never
decoded; a clip whose `frame_quality.active` is `0` was decoded and found to
contain no usable frames. Averaging the first as though it were the second
quietly drags your numbers down. See [Quality metrics](/quality-metrics).

**Every metric response echoes the run that answered it.** You address a
dataset, never a run, and you always get its most recent measurements. The
echoed token is how two answers a week apart can be told apart: the same token
means the corpus was not re-measured between them.

Handled errors are JSON, carrying a machine-readable `code` and a human
`message`. Branch on `code`; the message is for people.

```json theme={null}
{
  "code": "quality_enrichment_unavailable",
  "message": "dataset \"example-corpus\" carries no cleanliness measurements"
}
```

<Warning>
  A malformed **query string** is rejected before the handler runs, so it
  returns a `400` with a plain-text body (`Failed to deserialize query string:
      missing field \`dataset\`\`) rather than the JSON shape above. Parse
  defensively: check the status first, and do not assume every non-2xx body is
  JSON.
</Warning>

<Note>
  This reference is generated from the server's OpenAPI 3.1 document, which the
  server produces from its own routes and is verified against on every build.
  If the reference and the API ever disagree, that is a bug — the document
  cannot drift.
</Note>
