> ## 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.

# Quickstart: from credentials to a per-clip table

> Go from client credentials to a per-clip Quality Indexing table in five HTTP requests: authenticate, list datasets, pick clips, and read metrics.

Every endpoint here requires a credential. Get one first — see [Authentication](/authentication) — then export it alongside the host:

```bash theme={null}
export SQUASH_API=https://api.hebbianrobotics.com
export SQUASH_TOKEN=…            # the access token, not the client secret
```

## 1. Confirm what the credential can see

```bash theme={null}
curl -s "$SQUASH_API/api/access" \
  -H "Authorization: Bearer $SQUASH_TOKEN" | jq
```

```json Response theme={null}
{
  "scope": { "kind": "organization", "organization_id": "org_01JACME…" },
  "capabilities": ["view_data"],
  "writes_require_admin": true
}
```

The scope is resolved from the token, and it is what every dataset id below is interpreted within. If `kind` is `public` here, your token was not presented or not recognized, and every endpoint below will answer `401`. This call is how you find that out without having to tell a bad token from a bad request.

## 2. List your datasets

```bash theme={null}
curl -s "$SQUASH_API/api/quality/datasets" \
  -H "Authorization: Bearer $SQUASH_TOKEN" | jq
```

```json Response theme={null}
{
  "datasets": [
    {
      "dataset": "egocentric-100k",
      "metrics": ["vocabulary", "clips", "cleanliness", "hand_activity"]
    }
  ]
}
```

`dataset` is what every endpoint below means by `dataset`, and `metrics` is what that dataset can answer today. A metric missing from the list means it has not been measured yet, and asking for it returns `404
quality_enrichment_unavailable` rather than an empty result.

That is the whole addressing story: you name a dataset and you get its most recent measurements. There is no run id to obtain, pin, or keep in sync.

```bash theme={null}
export DATASET=egocentric-100k
```

## 3. Read the corpus vocabulary

What the footage contains, in three sections — where it was shot, what is handled, what is done.

```bash theme={null}
curl -s --get "$SQUASH_API/api/quality/vocabulary" \
  -H "Authorization: Bearer $SQUASH_TOKEN" \
  --data-urlencode "dataset=$DATASET" | jq
```

```json Response (abridged) theme={null}
{
  "dataset": "egocentric-100k",
  "vocabulary": "4f9c2ab7e15d3806",
  "clips_analyzed": 500,
  "segments_unassessed": 12,
  "segments_unusable": 3,
  "sections": [
    {
      "section": "objects",
      "distinct_terms": 134,
      "effective_distinct_terms": 41.7,
      "rarefied_distinct_terms": 134.0,
      "comparable_depth_clips": 500,
      "distinct_kinds": 46,
      "tiers": [
        {
          "tier": "non_deformable",
          "label": "T1: Non-deformable Object",
          "distinct_terms": 98,
          "share": 0.71,
          "example_terms": ["cup", "knife", "pan"]
        },
        {
          "tier": "deformable",
          "label": "T2: Deformable objects",
          "distinct_terms": 36,
          "share": 0.29,
          "example_terms": ["towel", "dough", "cable"]
        }
      ],
      "untiered_terms": ["unidentified container"],
      "terms": [
        {
          "term": "cup",
          "tier": "non_deformable",
          "kind": "drinkware",
          "occurrences": 212,
          "clip_count": 212,
          "spellings": ["cup", "mug", "coffee cup"]
        }
      ]
    }
  ]
}
```

Pass `section=objects` to get one section instead of all three.

Two numbers here are easy to misread and are worth reading about before you use them: `effective_distinct_terms` and `rarefied_distinct_terms`. See the [Metric glossary](/glossary).

## 4. Read the cleanliness partition

```bash theme={null}
curl -s --get "$SQUASH_API/api/quality/cleanliness" \
  -H "Authorization: Bearer $SQUASH_TOKEN" \
  --data-urlencode "dataset=$DATASET" | jq
```

```json Response theme={null}
{
  "dataset": "egocentric-100k",
  "analysis": "9d41e7c0b8a2f513",
  "frame_quality": "c07be4a19f26d835",
  "classification_version": "cleanliness-1",
  "clips_analyzed": 500,
  "clips_measured": 486,
  "clips_without_measurements": 14,
  "clips_without_identity": 0,
  "frames_measured": 291600,
  "usable_frames": 238104,
  "distorted_frames": 24786,
  "jerky_frames": 28710,
  "usable_share": 0.8165,
  "distorted_share": 0.085,
  "jerky_share": 0.0985
}
```

The three counts partition `frames_measured` exactly, and the shares are derived from that same denominator — they are a convenience, not a second source of truth. `clips_without_measurements` is the part of the analysed corpus this partition says nothing about; it is published so the gap is visible rather than absorbed.

The endpoint decodes nothing and calls no model. It reduces measurements that were already stored, so it is cheap to call repeatedly.

## 5. Page through the clips

Corpus-level answers commit you to our denominators. This one does not.

```bash theme={null}
curl -s --get "$SQUASH_API/api/quality/clips" \
  -H "Authorization: Bearer $SQUASH_TOKEN" \
  --data-urlencode "dataset=$DATASET" \
  --data-urlencode "frame_quality=true" \
  --data-urlencode "hand_activity=true" \
  --data-urlencode "limit=2" | jq
```

```json Response (abridged) theme={null}
{
  "dataset": "egocentric-100k",
  "vocabulary": "4f9c2ab7e15d3806",
  "filtered_to": [],
  "total": 500,
  "offset": 0,
  "limit": 2,
  "clips": [
    {
      "clip": "clips/0007/f31c9a02-000.mp4",
      "episode_index": 7,
      "analysed_secs": 58.0,
      "environments": ["kitchen"],
      "objects": ["cup", "kettle"],
      "actions": ["pour", "carry"],
      "frame_quality": {
        "frames": 600,
        "active": 501,
        "distorted": 44,
        "jerky": 55
      },
      "hand_activity": {
        "measured_seconds": 58.0,
        "hands_visible_seconds": 41.5,
        "two_hands_visible_seconds": 12.0,
        "active_manipulation_seconds": 29.75
      }
    }
  ]
}
```

`frame_quality` and `hand_activity` are **off by default**, because each joins one stored read per clip on the page. They are booleans, not addresses: they add columns to rows the vocabulary has already chosen.

Page with `offset` and `limit`. `total` is the size of the whole match, not of the page.

### Narrow to a term

```bash theme={null}
curl -s --get "$SQUASH_API/api/quality/clips" \
  -H "Authorization: Bearer $SQUASH_TOKEN" \
  --data-urlencode "dataset=$DATASET" \
  --data-urlencode "term=towel" | jq '.filtered_to, .total'
```

Matching is evidence-based. Any spelling that was merged into a published term finds that term, a spelling no pass ever produced selects nothing, and a term the corpus does not publish is a `404` rather than an empty page — those are different answers and you should not have to guess which you got.

`filtered_to` states every published term the query resolved to, per section, because a word can be published in more than one and the clips are the union.

## Next

<CardGroup cols={2}>
  <Card title="Metric glossary" icon="book" href="/glossary">
    What each field means and how it is derived, metric by metric.
  </Card>

  <Card title="Quality metrics" icon="scale-balanced" href="/quality-metrics">
    How the pieces fit together, and which numbers can be compared with what.
  </Card>
</CardGroup>
