{"openapi":"3.1.0","info":{"title":"crate-api","version":"2.57.0","description":"# crate API (v2 — cluster-first)\n\ncrate is the **aggregation gateway** for a fleet of music-data producers. You ask it about an artist, a label, or a festival; it joins every signal the fleet can see about that entity and hands you back one composed picture — collector behavior, editorial coverage, live-circuit demand, breakout momentum, web presence — each piece labelled with where it came from and how fresh it is.\n\nTwo things make crate different from a typical catalogue API, and the rest of this doc exists to teach them:\n\n1. **Identity is a `cluster_id`, not a Discogs/MusicBrainz/Bandcamp id.** The same artist scattered across those platforms collapses to one canonical key. You resolve to it once, then key everything off it.\n2. **An empty answer is a first-class answer.** crate returns HTTP `200` and tells you plainly what it *can't* see (`present:false`, a null field, `state:\"honest_gap\"`) instead of `404`-ing or fabricating data. Only `4xx`/`5xx` are errors.\n\nv2 is **cluster-first**: the agent (artist / label / festival) is the prime resource, and sources (Bandcamp, Discogs) and releases **attach to it as dimensions** rather than being top-level nouns. If you do nothing else, internalize the cold-start recipe: **resolve a name or pasted link → `cluster_id` → dossier.** `GET /api/v2` returns this recipe live, and `GET /api/v2/resolve?q=<name>` is the front door.\n\n## Authentication\n\nThe API is keyed: every operation requires an `X-API-Key` header (`ck_(live|test)_<…>` format) **except** the two public front-door endpoints that opt out — `GET /api/v2` (the root index) and `GET /api/v2/openapi.json` (this spec). One family of endpoints uses a *different* credential entirely — see **beacons** below.\n\n## Concepts\n\n### `cluster_id` — the canonical artist identity\n\n`cluster_id` is crate's prime key for an artist: a `pe-norm-v1` hex string derived from the artist's name, designed so that the same artist's Discogs page, MusicBrainz entry, and Bandcamp profile all **collapse to one `cluster_id`**. Key *all* artist data off it.\n\n- It is an **opaque string** — pass it through verbatim. Never numericize it, parse it, or assume structure.\n- `cluster_id: null` is an **honest gap**, not an error: crate couldn't resolve a canonical identity for that lookup. Roughly half of the long-tail booking artists have neither a Discogs id nor an MBID, so `null` is normal.\n- You get one by calling `GET /api/v2/resolve` (from a name, a pasted link, or a foreign id). You then address the artist dossier directly as `GET /api/v2/artist/{key}`, where `{key}` is the 64-hex `cluster_id` **or** a human slug.\n- A 64-hex key resolves identity *directly* from the cluster — it deliberately skips the Discogs lookup so a hex address never silently re-anchors onto a same-name Discogs row. Foreign locators (`discogs:<id>`, `mbid:<uuid>`) are **not** canonical addresses: convert them via `/resolve` first, or `/artist/{key}` returns `400` (the response's `next` field is a ready-to-call `/resolve` URL).\n\n### dossier · grains — the composed per-entity picture\n\nA **dossier** is the full picture crate composes for one entity by joining every fleet signal. v2 has three agent **grains**:\n\n| grain | addressed by | what it is |\n|---|---|---|\n| `artist` | `GET /api/v2/artist/{key}` or `GET /api/v2/dossier/artist/{slug}` | identity + collector behavior + editorial + emergence + live presence + web + compositions + **discography** + **bandcamp** dimensions… |\n| `label` | `GET /api/v2/label/{key}` or `GET /api/v2/dossier/label/{slug}` | label identity + sublabel→parent lineage + collector behavior |\n| `festival` | `GET /api/v2/dossier/festival/{slug}` | de-fragmented festival identity + consolidated editions ⋈ lineup |\n\n**Releases are not a top-level grain in v2.** In the cluster-first model a release (a Discogs release-group, what a consumer calls a \"release\") **attaches to the artist** as the dossier's `discography` dimension, keyed by the artist's `cluster_id` — there is no standalone release resource. Likewise **Bandcamp is a dimension of the artist dossier** (`bandcamp_emergence` / `bandcamp_tastemaker`), not a top-level surface.\n\nEvery dossier facet carries a classified **`state`** (e.g. `present`, `empty`/absent, `honest_gap`) and the dossier ships a **provenance manifest** — an array where each entry names the `producer`, `sourceTable`, `refreshCadence`, `tier`, and `honestGapState` for a field. Read `GET /api/v2/dossier/manifest` (the data dictionary) to discover the entire field surface across grains — including grains that are deliberately *unavailable* (e.g. song, because the fleet has no track key; and the demoted master grain, whose detail now lives in the artist's `discography`) — without hitting every entity endpoint.\n\n### honest gap — empty is `200`, not `404`\n\nThis is crate's defining principle: **crate shows what it can see and is explicit about what it can't, rather than 404-ing or faking data.** An unresolved or empty lookup returns **HTTP `200`** with one of:\n\n- `present: false` (e.g. a dossier dimension with no match),\n- a `null` field (`cluster_id: null`, `identity: null`),\n- `state: \"honest_gap\"` on a dossier facet.\n\nBranch on the body, not the status, for \"did I get data?\". An unresolved artist slug returns `identity:null` at `200` — never `404`. Reserve your error handling for genuine **`4xx`/`5xx`**.\n\n### `resolved_via` · `resolved_from` — binding tier and match method\n\nWhen crate resolves an identity it tells you two orthogonal things:\n\n- **`resolved_via`** = the *binding tier*, i.e. how trustworthy the identity is:\n  - `'discogs'` — canonical, Discogs-bound (verified).\n  - `'cluster'` — **observed/unverified**: the identity came from the booking graph with no Discogs bind. Surface it flagged as unverified, never as canonical.\n  - `null` — did not resolve.\n- **`resolved_from`** = *how* you addressed it on `/resolve`: `'url'` (a pasted link), `'name'`, or `'locator'` (a foreign id). `matched_on` names the surface that matched; `note` explains a recognized-but-unresolved link (e.g. a Twitter URL crate recognizes but does not yet cross-reference).\n\nA 64-hex `cluster_id` address always yields `resolved_via: 'cluster'` (observed tier) by design.\n\n### the cube · `cube_quadrant` — the behavioral-signal model\n\nThe **cube** is crate's behavioral model of a release: a **3-bit code** (string, e.g. `\"101\"`) placing it on three independent behavioral axes — **who OWNS it (collector), who PLAYS it (DJ), who WRITES about it (critic)**. Each bit is `0`/`1`, so the eight quadrants run `\"000\"` (\"No signal\") through `\"111\"` (\"Full intersection\"): `\"100\"` = collector-only, `\"010\"` = DJ-only, `\"101\"` = collector + critic, and so on. The collector-vs-DJ split — *who owns it vs who plays it* — is the heart of the model. `cube_quadrant: null` means it isn't yet classified (an honest gap). You meet `cube_quadrant` (with companion `owner_count` / `dj_count` / `critic_count` magnitudes + a `link_to_cube` explorer deep-link) on the master-grain result rows of `GET /api/v2/search`.\n\n### tastemakers · breakouts — discovery surfaces\n\nTwo read-only discovery surfaces, served from offline-published snapshots (no DB checkout) and **fail-soft**: each returns a `state` of `present` / `empty` / `degraded`, where `degraded` is a `200` honest-gap (a read failure never `500`s), and `stale:true` flags a snapshot older than 7 days.\n\n- **tastemakers** (`GET /api/v2/tastemakers`, `…/ones-to-watch`) — influential curators and the richest artist-grain analytics crate has: rank, own-tier, brokerage score, corroborating axes, lead-times, Bandcamp demand. `?limit=` bounds each array (`1..200`).\n- **breakouts** (`GET /api/v2/breakouts`) — emerging artists on the rise (\"ones to watch\"): booking-momentum signal cross-validated against press. `?tier=breakout|rising` and `?corroboration=corroborated|booking_ahead` filter it; `?limit=` is clamped to `200`.\n\n### beacons — search-event telemetry (different credential)\n\n**Beacons** are client-side telemetry about search behavior: `POST /api/v2/search-events/observed` (a result was served from cache) and `…/refined` (the user changed facets). They are **not** authenticated with your `X-API-Key`. Each search response issues a **short-lived per-search JWT** bound to one `search_event_id`; send it as `Authorization: Bearer <token>`, and the token must match the body's `search_event_id`. Bodies are capped at 512 bytes and beacons are idempotent (a duplicate is a `204` no-op). Beacon `400`s carry a Zod *flattened* `details` object (not the array shape of normal validation errors), so they use a distinct error schema.\n\n### sparse fieldsets — `?fields=` (opt-out trim)\n\nThe artist dossier is **default-rich**: `GET /api/v2/artist/{key}` returns the whole dossier in one round-trip. If you want less, `?fields=identity,discography` **trims** to the named top-level facets (the envelope is always present). An unknown field returns `400 invalid_fields` with the exact valid set and a copy-pasteable `example` — so you never guess.\n\n### Bandcamp in v2 — analytical dimensions, and the link-only posture\n\nIn v2 Bandcamp is an **analytical dimension of the artist dossier**, not a release surface. Two facets carry it, both keyed by the artist's `cluster_id` and both **signals/metrics** (not per-release listings):\n\n- **`bandcamp_emergence`** — purchase-backed demand signals (emergence class, demand lead/ratio, owner reach, wishlist demand, distinct releases, earliest-wished date).\n- **`bandcamp_tastemaker`** — early-supporter quality scores (supporter-cohort size, aesthetic-quality scores, mean first-buyer earliness).\n\nPer-release Bandcamp listings — and the `bandcamp_item_id` / `track_url` per-release fields — are **not a v2 surface**; they belong to the v1 Bandcamp endpoints. (The discography dimension is the **Discogs** release-group attachment, keyed by `discogsMasterId` — distinct from Bandcamp.)\n\ncrate is **link-only** everywhere it does carry links: every artwork item is a hotlink `url` with `rehost:false` — crate never fetches or re-hosts bytes (a Cover Art Archive URL is best-effort and may `404` if no cover exists), and crate never stores Bandcamp audio streams (tokenized, expiring, out of ToS bounds).\n\n### Opaque ids — the universal rule\n\n`cluster_id` is **always a string, always opaque**. Round-trip it verbatim; do not parse, increment, numericize, or infer structure from it. (The same discipline applies to any id crate hands you.) This keeps your client correct across id-scheme changes.\n\n### Versioning\n\nThe **API major version lives in the URL path** (`/api/v2`). The spec's `info.version` (currently `2.33.0`) bumps on **every** spec change and is drift-guarded: the document is generated from code, so docs cannot drift from runtime behavior. Operations carry stable `operationId`s for codegen, and keyed `2xx` responses declare `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` headers (back off on `429` using `retry_after_seconds` / `Retry-After`). v2 is the cluster-first stable major; the frozen v1 predecessor remains available during a time-boxed, announced deprecation — the route-by-route migration map is at `/docs/migration/v1-to-v2`.","contact":{"name":"crate support","email":"support@crate.hosaka.fm"},"license":{"name":"Commercial — see Terms of Service"},"x-concepts":[{"term":"cluster_id","eli5":"crate's one true name for an artist — a pe-norm-v1 hex string that collapses the same artist's Discogs, MusicBrainz, and Bandcamp identities into a single canonical key. Key all artist data off it. It's an opaque string: pass it through verbatim, never numericize it. cluster_id:null is an honest gap (couldn't resolve), not an error.","see":"IdentityResolution (resolveIdentity, GET /api/v2/resolve); ArtistDossierContract.cluster_id (GET /api/v2/artist/{key})"},{"term":"dossier","eli5":"The full picture crate composes for one entity by joining every fleet signal. Each facet carries a classified state plus a provenance manifest (producer, source table, refresh cadence, tier). Read /dossier/manifest to discover the whole field surface without calling every endpoint.","see":"DossierManifest (GET /api/v2/dossier/manifest); ArtistDossierContract / LabelDossierContract / FestivalDossierContract"},{"term":"grain","eli5":"Which kind of agent a dossier is about: artist, label, or festival. Releases are not a top-level grain in v2 — release/master detail attaches to the artist dossier's discography dimension. The dossier manifest also lists deliberately-unavailable grains (e.g. song — no fleet track key; and the demoted master grain).","see":"GET /api/v2/dossier/artist/{slug}, /api/v2/dossier/label/{slug}, /api/v2/dossier/festival/{slug}; ArtistDossierContract.discography; DossierManifest.unavailable_grains"},{"term":"honest gap","eli5":"An unresolved or empty lookup returns HTTP 200 with present:false, a null field, or state:\"honest_gap\" — this is NORMAL, not an error. crate shows what it can see and is explicit about what it can't, rather than 404-ing or faking data. Only 4xx/5xx are errors; branch on the body, not the status.","see":"Error schema (the inverse: only 4xx/5xx); ArtistDossierContract.identity:null; dossier facets with state:\"honest_gap\""},{"term":"resolved_via","eli5":"The binding TIER of a resolved identity: 'discogs' = canonical Discogs-bound (verified); 'cluster' = OBSERVED/unverified from the booking graph (surface flagged-unverified, never as canonical); null = unresolved. A 64-hex cluster_id address is always 'cluster' by design.","see":"IdentityResolution.resolved_via; ArtistDossierContract.resolved_via"},{"term":"resolved_from","eli5":"How you addressed the artist on /resolve: 'url' (pasted link), 'name', or 'locator' (a foreign id). Paired with matched_on (which surface matched) and note (why a recognized link didn't resolve).","see":"IdentityResolution.resolved_from (GET /api/v2/resolve)"},{"term":"cube / cube_quadrant","eli5":"crate's behavioral-signal model: a 3-bit string code (e.g. \"101\") placing a release on three axes — who OWNS it (collector), who PLAYS it (DJ), who WRITES about it (critic). Codes run \"000\" (no signal) to \"111\" (full intersection). null = not yet classified (honest gap). owner_count/dj_count/critic_count give the magnitude. You meet it on the result rows of /api/v2/search.","see":"ResultRow.cube_quadrant + owner_count/dj_count/critic_count + link_to_cube (GET /api/v2/search)"},{"term":"tastemakers","eli5":"A discovery surface of influential curators plus crate's richest artist-grain analytics (rank, own-tier, brokerage score, lead-times, Bandcamp demand). Served from an offline snapshot, fail-soft: state is present/empty/degraded (degraded = 200 honest-gap), stale=true if older than 7 days.","see":"TastemakersResponse (GET /api/v2/tastemakers); OnesToWatchResponse (GET /api/v2/tastemakers/ones-to-watch)"},{"term":"breakouts","eli5":"A discovery surface of emerging artists on the rise ('ones to watch') — booking momentum cross-validated against press. Filter by ?tier=breakout|rising and ?corroboration=corroborated|booking_ahead; ?limit= clamped to 200. Fail-soft state present/empty/degraded.","see":"BreakoutsResponse (GET /api/v2/breakouts)"},{"term":"beacons","eli5":"Client-side search-event telemetry (observed = cache hit, refined = facet change). Authenticated NOT with X-API-Key but with a short-lived per-search JWT bound to one search_event_id, sent as Authorization: Bearer. Bodies <=512 bytes, idempotent (duplicate = 204 no-op).","see":"ObservedBeaconRequest / RefinedBeaconRequest (POST /api/v2/search-events/observed, /refined); BeaconBearerAuth security scheme; BeaconError"},{"term":"fields","eli5":"The v2 artist dossier is default-rich (one round-trip). ?fields=identity,discography TRIMS to the named top-level facets (opt-out, not opt-in); omit it for the full dossier. An unknown field → 400 invalid_fields with the exact valid set + a copy-pasteable example.","see":"GET /api/v2/artist/{key}?fields=...; the invalid_fields error (valid set + example)"},{"term":"bandcamp dimensions","eli5":"In v2 Bandcamp is an ANALYTICAL dimension of the artist dossier (keyed by cluster_id), not a release surface: bandcamp_emergence = purchase-backed demand signals (emergence class, demand lead/ratio, owner reach, wishlist demand), bandcamp_tastemaker = early-supporter quality scores. Per-release Bandcamp listings (and bandcamp_item_id / track_url) are NOT a v2 surface — they belong to the v1 Bandcamp endpoints.","see":"ArtistDossierContract.bandcamp_emergence / bandcamp_tastemaker (signals; GET /api/v2/artist/{key})"},{"term":"link-only (artwork + streams)","eli5":"crate is link-only wherever it carries links: every artwork item is a hotlink url with rehost:false — crate never fetches or re-hosts bytes (a Cover Art Archive URL is best-effort, may 404). crate never stores Bandcamp audio streams (tokenized, expiring, out of ToS).","see":"ArtworkItem (rehost:false) on the dossiers"},{"term":"opaque ids","eli5":"cluster_id is always a string and always opaque — round-trip it verbatim, never parse, increment, or numericize. The same discipline applies to any id crate hands you. This keeps clients correct across id-scheme changes.","see":"IdentityResolution.cluster_id; ArtistDossierContract.cluster_id"},{"term":"versioning","eli5":"The API major version is the URL path (/api/v2). The spec's info.version (2.33.0) bumps on every spec change and is drift-guarded because the document is generated from code. v2 is the cluster-first stable major; the frozen v1 predecessor is in a time-boxed deprecation (migration map at /docs/migration/v1-to-v2). Operations carry stable operationIds; keyed 2xx responses declare X-RateLimit-* headers.","see":"info.version; GET /api/v2/openapi.json; the migration guide at /docs/migration/v1-to-v2"},{"term":"surface registry","eli5":"A generic cluster-keyed read layer over raw producer tables crate hasn't yet composed into a bespoke dossier facet (cycle-096; carrefour#135): GET /api/v2/surface lists every registered surface (grain, key, keyspace, keyset, cap); GET /api/v2/surface/{name}?cluster=<hex64> reads it, keyset-paginated via an opaque ?after cursor. Fail-closed by construction — a column the registry doesn't allowlist can never appear on the wire. state:'degraded' (still HTTP 200) means the dedicated read pool isn't provisioned yet.","see":"GET /api/v2/surface (getSurfaceIndex); GET /api/v2/surface/{name} (getSurfaceRows)"}]},"servers":[{"url":"https://crate.hosaka.fm","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Customer API key in `ck_(live|test)_<32-base62>` format"},"BeaconBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Short-lived per-search beacon JWT (issued with the search response), sent as `Authorization: Bearer <token>`. Distinct from the X-API-Key customer key; the token is bound to a single search_event_id."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable error code (stable lowercase snake_case). The ONLY field guaranteed on every error body — switch on it programmatically, never on HTTP status alone (several codes share a status). See the code table in this schema's description.","example":"invalid_query"},"message":{"type":"string","description":"One-sentence human-readable statement of WHAT is wrong (developer-facing). Present only for catalogued codes. Describes the violated rule — not a fix (see hint/next)."},"hint":{"type":"string","description":"Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with <placeholders>). The human counterpart to the machine-actionable `next`."},"doc_url":{"type":"string","description":"Deep link to this code's docs: https://crate.hosaka.fm/docs/api#error-<code>. Auto-populated for catalogued codes."},"param":{"type":"string","description":"The specific request parameter that caused the failure (e.g. \"key\", \"q\"), so a client can point at the offending input. Present only when the code declares one."},"next":{"type":"string","description":"A copy-pasteable, fully-formed corrected call (a concrete URL, NOT a template) an agent can fire verbatim to recover — the machine-actionable counterpart to `hint`. Present only when a handler supplies one."},"details":{"type":"array","items":{},"description":"Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached."},"retry_after_seconds":{"type":"number","description":"On a 429 rate_limited response, seconds to wait before retrying (mirrors the Retry-After header). Sleep at least this long, then re-issue the identical request."},"master_id":{"type":"number","description":"Echoed on master_not_found (404) — the master id that did not resolve."}},"required":["error"],"description":"The error envelope for all 4xx/5xx responses. `error` is the only guaranteed field — branch on it, never on HTTP status alone. An unresolved/empty lookup is NOT an error: it returns HTTP 200 with `present:false` / a null field / `state:\"honest_gap\"`.\n\n| code | HTTP | when thrown | fix |\n|---|---|---|---|\n| `invalid_artist_key` | 400 | `/artist/{key}` key is not a 64-hex cluster_id, `discogs:<id>`, or `mbid:<uuid>` | resolve by name first: `GET /api/v2/resolve?q=<name>`, then call `/artist/{cluster_id}` |\n| `use_resolve_for_locator` | 400 | `/artist/{key}` given a `discogs:`/`mbid:` locator (not a canonical address) | `GET /api/v2/resolve?discogs=<id>` (or `?mbid=`), then use the returned cluster_id (the response `next` field is the ready-to-call URL) |\n| `invalid_label_key` | 400 | `/label/{key}` key is not a 64-hex `label_cluster_id` or name-slug (e.g. a `discogs:`/`mbid:` locator — not resolvable for labels yet) | address a label by its 64-hex `label_cluster_id` or its name-slug (e.g. `/api/v2/label/warp`) |\n| `invalid_fields` | 400 | `/artist/{key}?fields=` lists a facet that is not a valid top-level dossier field | use only the fields in the response `valid` set; omit `?fields=` entirely for the full dossier (see the `example` field) |\n| `missing_locator` | 400 | `/resolve` called with none of q/cluster/discogs/mbid | pass exactly one locator |\n| `invalid_locator` | 400 | a `/resolve` locator is malformed for its type | fix the format, or fall back to `?q=<name>` |\n| `invalid_query` | 400 | `/search` `?q=` missing/empty, or any Zod validation failure (`details[]` attached) | pass `?q=<text>`; fix each `details` entry |\n| `invalid_facet` | 400 | an unknown facet filter name was supplied | `GET /api/v2/facets` for valid names + values |\n| `rate_limited` | 429 | an IP/key/tier rate or concurrency cap was exceeded (`retry_after_seconds` + `Retry-After` + `X-RateLimit-*` set) | back off `retry_after_seconds` (or until `X-RateLimit-Reset`), then retry |"},"RateLimited":{"type":"object","properties":{"error":{"type":"string","enum":["rate_limited"]},"retry_after_seconds":{"type":"number"}},"required":["error","retry_after_seconds"]},"ArtworkItem":{"type":"object","properties":{"url":{"type":"string","description":"Hotlink-only artwork URL: a Bandcamp CDN string, or a deterministic Cover Art Archive release-group URL. crate never fetches or re-hosts the bytes; a CAA url is best-effort and may 404 if no cover exists."},"source":{"type":"string","enum":["bandcamp","coverartarchive"]},"grain":{"type":"string","enum":["artist","release"]},"license":{"type":"string"},"rehost":{"type":"boolean","enum":[false],"description":"A const literal — always the boolean false — declaring crate's link-only artwork posture. WHY IT MATTERS: it is a contractual promise that the `url` on the artwork item is a HOTLINK the caller dereferences directly (a Bandcamp CDN url or a deterministic Cover Art Archive url); crate never fetches, caches, or re-hosts the image bytes. GOTCHA: because crate never dereferences the url, it can be stale or 404 (a CAA url is best-effort and may not exist) — your client must handle a broken image gracefully. Treat rehost:false as a fixed flag, not a toggle; it will never be true.","example":"false"},"width":{"type":"integer","description":"Delivered pixel width of the hotlinked image. For bandcamp these are the _16 locator's render dims (700×700 square) — exactly the box the hotlink renders at, NOT the original artwork's source aspect (HEAD the url for non-square edge cases). Absent when the source carries no dims (coverartarchive)."},"height":{"type":"integer","description":"Delivered pixel height — see width."}},"required":["url","source","grain","license","rehost"]},"Freshness":{"type":"object","properties":{"ridden_lag_s":{"type":["number","null"]},"mirror_lag_s":{"type":["number","null"]}},"required":["ridden_lag_s","mirror_lag_s"]},"ResultRow":{"type":"object","properties":{"master_id":{"type":"integer"},"title":{"type":"string"},"artist":{"type":"string"},"year":{"type":["integer","null"]},"cube_quadrant":{"type":["string","null"]},"owner_count":{"type":["integer","null"]},"dj_count":{"type":["integer","null"]},"critic_count":{"type":["integer","null"]},"formats":{"type":"array","items":{"type":"string"}},"link_to_cube":{"type":"string"},"artist_cluster_id":{"type":["string","null"],"description":"The row artist's canonical 64-hex cluster_id (the fleet identity spine) — null for multi-artist credits or when no identity bind is held. Feed it straight to /api/v2/artist/{key}."}},"required":["master_id","title","artist","year","cube_quadrant","owner_count","dj_count","critic_count","formats","link_to_cube"]},"SearchResponse":{"type":"object","properties":{"query":{"type":"object","properties":{"q":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"facet":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"number"}]},"mode":{"type":"string","enum":["and","or"]}},"required":["facet","value"]}}},"required":["filters"]},"pagination":{"type":"object","properties":{"page":{"type":"integer"},"total_pages":{"type":["integer","null"]},"total_results":{"type":"integer"},"total_results_mode":{"type":"string","enum":["exact","approximate_50k_sample","lower_bound"]}},"required":["page","total_pages","total_results","total_results_mode"]},"facets":{"type":"object","additionalProperties":{}},"freshness":{"$ref":"#/components/schemas/Freshness"},"results":{"type":"array","items":{"$ref":"#/components/schemas/ResultRow"}}},"required":["query","pagination","facets","freshness","results"]},"MasterEnrichment":{"type":"object","properties":{"master_id":{"type":"integer"},"title":{"type":"string"},"artist":{"type":"string"},"year":{"type":["integer","null"]},"cube_quadrant":{"type":["string","null"]},"owner_count":{"type":["integer","null"]},"dj_count":{"type":["integer","null"]},"critic_count":{"type":["integer","null"]},"formats":{"type":"array","items":{"type":"string"}},"freshness":{"$ref":"#/components/schemas/Freshness"},"link_to_cube":{"type":"string"},"insert_links":{"type":"null"}},"required":["master_id","title","artist","year","cube_quadrant","owner_count","dj_count","critic_count","formats","freshness","link_to_cube","insert_links"]},"BatchResponse":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/MasterEnrichment"}},"not_found":{"type":"array","items":{"type":"integer"}}},"required":["results","not_found"]},"UsageResponse":{"type":"object","properties":{"tier":{"type":"string","enum":["sync","self_serve_49","self_serve_99","self_serve_299","free"]},"month":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"calls_this_month":{"type":"integer"},"quota_monthly":{"type":"integer"},"remaining_this_month":{"type":"integer"},"burst_limit_per_minute":{"type":"integer"},"tier_metadata":{"type":"object","properties":{"endpoints_available":{"type":"array","items":{"type":"string"}}},"required":["endpoints_available"]}},"required":["tier","month","calls_this_month","quota_monthly","remaining_this_month","burst_limit_per_minute","tier_metadata"]},"FacetCounts":{"type":"object","additionalProperties":{}},"WayfindAnswerResponse":{"type":"object","additionalProperties":{}},"WayfindInterpretResponse":{"type":"object","additionalProperties":{}},"ObservedBeaconRequest":{"type":"object","properties":{"search_event_id":{"type":"string","format":"uuid"},"source":{"type":"string","enum":["swr-cache-hit","client-cache-hit"]},"timestamp":{"type":"string","format":"date-time"}},"required":["search_event_id","source","timestamp"],"additionalProperties":false},"RefinedBeaconRequest":{"type":"object","properties":{"search_event_id":{"type":"string","format":"uuid"},"changed_facets":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","enum":["genre","style","year_from","year_to","country","format","label","q"]},"from":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}},{"type":"null"},{"type":"null"}]},"to":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}},{"type":"null"},{"type":"null"}]}},"required":["name"],"additionalProperties":false},"maxItems":10},"timestamp":{"type":"string","format":"date-time"}},"required":["search_event_id","changed_facets","timestamp"],"additionalProperties":false},"BeaconError":{"type":"object","properties":{"error":{"type":"string","example":"invalid_payload"},"details":{},"skew_ms":{"type":"number"}},"required":["error"]},"BandcampLabel":{"type":"object","properties":{"name":{"type":["string","null"]},"url":{"type":["string","null"]}},"required":["name","url"]},"BandcampReleaseEconomics":{"type":"object","properties":{"minimum_price":{"type":["number","null"]},"set_price":{"type":["number","null"]},"is_set_price":{"type":["boolean","null"]},"default_price":{"type":["number","null"]},"currency":{"type":["string","null"]},"download_pref":{"type":["integer","null"],"description":"Bandcamp enum: 1 = free, 2 = name-your-price / paid."},"free_download":{"type":["boolean","null"]},"require_email":{"type":["boolean","null"]},"about":{"type":["string","null"],"description":"The artist's own release description."},"credits":{"type":["string","null"],"description":"The artist's own credits text."},"upc":{"type":["string","null"],"description":"The release barcode (UPC/EAN) as Bandcamp records it — the closest thing Bandcamp has to a catalogue number. WHY IT MATTERS: it is a cross-platform release identifier you can use to match the release against other catalogues. GOTCHA: it is a STRING (barcodes carry leading zeros and exceed safe-integer range — numericizing corrupts them) and is nullable (most Bandcamp self-releases have none) — null is an honest gap, not an error.","example":"888002345678"},"is_preorder":{"type":["boolean","null"]},"preorder_count":{"type":["integer","null"]},"publish_date":{"type":["string","null"]},"packages":{"description":"Raw physical-format / edition jsonb (verbatim)."}},"required":["minimum_price","set_price","is_set_price","default_price","currency","download_pref","free_download","require_email","about","credits","upc","is_preorder","preorder_count","publish_date"]},"BandcampRelease":{"type":"object","properties":{"bandcamp_item_id":{"type":"string","description":"A Bandcamp release id — the direct address for one release dossier (?item=<id> on /bandcamp/release returns the full tracklist). WHY IT MATTERS: it is the stable per-release key you carry from a release-list row back to the full dossier fetch. GOTCHA: it is a bigint serialized AS A STRING and is OPAQUE — never numericize it (JS numbers lose precision past 2^53 and the value is an identifier, not a quantity), never do arithmetic on it, and pass it back verbatim.","example":"1234567890"},"cluster_id":{"type":["string","null"],"description":"crate's CANONICAL artist identity — a pe-norm-v1 hex string. The SAME artist across Discogs, MusicBrainz and Bandcamp collapses to ONE cluster_id, so this is the key you store and the key you address every artist surface off of (/artist/{key} takes a 64-hex cluster_id directly). WHY IT MATTERS: it is crate's prime IP — the non-Discogs long-tail join key; discogs_artist_id / mbid are mere leaf coordinates onto it. GOTCHA: it is an OPAQUE string — pass it through verbatim, NEVER numericize, parse, or compare it as a number. null is an HONEST GAP (the name/link couldn't be resolved to a cluster), NOT an error — you still get HTTP 200. A 64-hex cluster_id always resolves at OBSERVED tier (resolved_via:'cluster'), never re-anchored onto a same-name Discogs row.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"artist":{"type":["string","null"]},"artist_subdomain":{"type":["string","null"]},"title":{"type":["string","null"]},"release_date":{"type":["string","null"],"description":"ISO timestamp (may be a reissue date or a future pre-order)."},"source_url":{"type":["string","null"],"description":"The Bandcamp album page URL."},"tags":{"type":"array","items":{"type":"string"},"description":"Per-release genre/mood/location tags (Bandcamp keywords)."},"label":{"allOf":[{"$ref":"#/components/schemas/BandcampLabel"},{"type":["object","null"],"description":"Release label (null when self-released/unknown)."}]},"artwork":{"type":"array","items":{"$ref":"#/components/schemas/ArtworkItem"},"description":"Link-only cover art (source: bandcamp, grain: release)."},"tracks":{"type":"array","items":{"type":"object","properties":{"track_num":{"type":"integer"},"title":{"type":["string","null"]},"duration_s":{"type":["number","null"],"description":"Track length in SECONDS, derived from Bandcamp's integer duration_ms. WHY IT MATTERS: a uniform per-track length you can sum for a release runtime. GOTCHA: the unit is seconds (the _s suffix), not milliseconds and not minutes — don't re-divide by 1000. It is nullable when Bandcamp exposed no duration (honest gap).","example":"284"},"license_type":{"type":["string","null"]},"track_url":{"type":["string","null"],"description":"The Bandcamp track PAGE url for one track. WHY IT MATTERS: it is the canonical human-visitable link to the track on Bandcamp. GOTCHA: this is NOT a playable/direct audio stream URL — Bandcamp's actual stream URLs are tokenized and expiring and are not stored (both a technical and a ToS constraint). Do not feed track_url to an audio element expecting bytes; it is a page link. It is also nullable (a track may have no stored page url) — an honest gap, not an error.","example":"https://artistname.bandcamp.com/track/the-track-title"}},"required":["track_num","title","duration_s","license_type","track_url"]}},"economics":{"allOf":[{"$ref":"#/components/schemas/BandcampReleaseEconomics"},{"type":["object","null"],"description":"Pricing / about / credits / barcode / packages / preorder (null if mirror has none)."}]},"scarcity":{"type":["object","null"],"description":"Supply scarcity × k-anon wishlist demand (the fleet's only made-vs-wanted ratio; mirror mig 0181). NULL means \"no supply-scarcity signal on file\" (the producer only scores releases that are limited or running low) — not \"unknown\". wishlist_demand is k-anon FLOORED by the producer (<5 distinct wishers → 0).","required":["is_limited","edition_size","quantity_remaining","sell_through","low_stock","is_preorder","scarcity_score","wishlist_demand","spi","computed_at"],"properties":{"is_limited":{"type":"boolean","description":"Any limited-edition package on the release."},"edition_size":{"type":["integer","null"],"description":"Smallest limited-edition cap (creator-declared, public)."},"quantity_remaining":{"type":["integer","null"],"description":"Units remaining on the scarcest limited package."},"sell_through":{"type":["number","null"],"description":"DERIVED 1 - quantity_available/edition_size in [0,1]; null when edition_size unknown (Bandcamp never exposes quantity_sold)."},"low_stock":{"type":"boolean","description":"Bandcamp's own 'running low' warning on any package."},"is_preorder":{"type":"boolean"},"scarcity_score":{"type":"number","description":"[0,1]: sell_through when edition_size known, else 0.8 if low_stock, else 0."},"wishlist_demand":{"type":"integer","description":"Distinct wishers — k-anon floored to 0 below 5 by the producer."},"spi":{"type":"number","description":"Scarcity Pressure Index = scarcity_score · ln(1 + wishlist_demand) — \"scarce AND wanted = about to be unobtainable\"."},"computed_at":{"type":["string","null"]}}}},"required":["bandcamp_item_id","cluster_id","artist","artist_subdomain","title","release_date","source_url","tags","label","artwork","tracks","economics"]},"BandcampReleaseSummary":{"type":"object","properties":{"bandcamp_item_id":{"type":"string","description":"A Bandcamp release id — the direct address for one release dossier (?item=<id> on /bandcamp/release returns the full tracklist). WHY IT MATTERS: it is the stable per-release key you carry from a release-list row back to the full dossier fetch. GOTCHA: it is a bigint serialized AS A STRING and is OPAQUE — never numericize it (JS numbers lose precision past 2^53 and the value is an identifier, not a quantity), never do arithmetic on it, and pass it back verbatim.","example":"1234567890"},"artist":{"type":["string","null"]},"title":{"type":["string","null"]},"release_date":{"type":["string","null"]},"source_url":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}},"label":{"allOf":[{"$ref":"#/components/schemas/BandcampLabel"},{"type":["object","null"]}]}},"required":["bandcamp_item_id","artist","title","release_date","source_url","tags","label"]},"DossierManifest":{"type":"object","properties":{"contract_version":{"type":"string"},"generated_at":{"type":"string"},"unavailable_grains":{"type":"array","items":{}},"grains":{"type":"array","items":{}}},"required":["contract_version","generated_at","unavailable_grains","grains"]},"MasterDossierContract":{"type":"object","properties":{"contract_version":{"type":"string"},"grain":{"type":"string","enum":["master"]},"id":{"type":"number"},"header":{"type":"object","properties":{"title":{"type":"string"},"artist":{"type":["string","null"]},"year":{"type":["number","null"]},"formats":{"type":"array","items":{"type":"string"}},"cube_quadrant":{"type":"object","properties":{"code":{"type":["string","null"]},"label":{"type":["string","null"]}},"required":["code","label"]}},"required":["title","artist","year","formats","cube_quadrant"]},"sections":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"state":{"type":"string"}},"required":["field","state"]}},"artwork":{"type":"array","items":{"$ref":"#/components/schemas/ArtworkItem"}},"freshness":{"type":"object","properties":{"mirror_lag_s":{"type":["number","null"]},"seen_lag_s":{"type":["number","null"]}},"required":["mirror_lag_s","seen_lag_s"]},"cache":{"type":"object","properties":{"etag":{"type":"string"},"maxAge":{"type":"number"}},"required":["etag","maxAge"]},"provenance":{"type":"array","items":{}},"generated_at":{"type":"string"}},"required":["contract_version","grain","id","header","sections","artwork","freshness","cache","provenance","generated_at"]},"ArtistDossierContract":{"type":"object","properties":{"contract_version":{"type":"string"},"grain":{"type":"string","enum":["artist"]},"slug":{"type":"string"},"display":{"type":"string"},"id":{"type":["number","null"]},"cluster_id":{"type":["string","null"],"description":"crate's CANONICAL artist identity — a pe-norm-v1 hex string. The SAME artist across Discogs, MusicBrainz and Bandcamp collapses to ONE cluster_id, so this is the key you store and the key you address every artist surface off of (/artist/{key} takes a 64-hex cluster_id directly). WHY IT MATTERS: it is crate's prime IP — the non-Discogs long-tail join key; discogs_artist_id / mbid are mere leaf coordinates onto it. GOTCHA: it is an OPAQUE string — pass it through verbatim, NEVER numericize, parse, or compare it as a number. null is an HONEST GAP (the name/link couldn't be resolved to a cluster), NOT an error — you still get HTTP 200. A 64-hex cluster_id always resolves at OBSERVED tier (resolved_via:'cluster'), never re-anchored onto a same-name Discogs row.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"resolved_via":{"type":["string","null"],"enum":["discogs","cluster"],"description":"The binding TIER — how trustworthy the identity match is. 'discogs' = canonical, Discogs-bound (verified). 'cluster' = OBSERVED/UNVERIFIED identity inferred from the seen booking graph with no Discogs bind. null = the lookup did not resolve at all (honest gap). WHY IT MATTERS: it is a trust signal you must respect — a 'cluster' result is crate showing you what it can SEE in the booking graph, not what it has verified. GOTCHA: surface a 'cluster' result as flagged/unverified, NEVER as canonical truth; do not silently merge a 'cluster' artist with a verified one. A bare 64-hex key always comes back 'cluster' by design (it skips the cc0_artists lookup so a hex address never re-anchors onto a same-name Discogs row).","example":"discogs"},"identity":{"type":["object","null"],"properties":{"discogsArtistId":{"type":["number","null"]},"clusterId":{"type":["string","null"]},"resolvedVia":{"type":"string","enum":["discogs","cluster"]},"name":{"type":"string"},"realname":{"type":["string","null"]},"profile":{"type":["string","null"]},"urls":{"type":"array","items":{"type":"string"}},"aliases":{"type":"array","items":{"type":"string"}}},"required":["discogsArtistId","clusterId","resolvedVia","name","realname","profile","urls","aliases"]},"behavioral":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"ownerReach":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["count"]},"value":{"type":"number"}},"required":["kind","value"]},{"type":"object","properties":{"kind":{"type":"string","enum":["suppressed"]}},"required":["kind"]}]},"wantlistDemand":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["count"]},"value":{"type":"number"}},"required":["kind","value"]},{"type":"object","properties":{"kind":{"type":"string","enum":["suppressed"]}},"required":["kind"]}]},"collectorCoownership":{"type":["number","null"]},"communityFootprint":{"type":["number","null"]},"travelsWith":{"type":"array","items":{}},"workCount":{"type":["number","null"]},"primaryGenre":{"type":["string","null"]},"primaryStyles":{"type":"array","items":{"type":"string"}},"masterCountWithGenre":{"type":["number","null"]}},"required":["ownerReach","wantlistDemand","collectorCoownership","communityFootprint","travelsWith","workCount","primaryGenre","primaryStyles","masterCountWithGenre"]}},"required":["state","signals"]},"editorial":{"type":"object","properties":{"state":{"type":"string"},"signals":{}},"required":["state"]},"across_the_web":{"type":"object","properties":{"count":{"type":"number"},"links":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"sourceSubtype":{"type":["string","null"]},"url":{"type":"string"},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"confidence":{"type":["number","null"]}},"required":["source","sourceSubtype","url","title","description","confidence"]}}},"required":["count","links"]},"journalism":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"pressCount":{"type":"number"},"reviewCount":{"type":"number"},"interviewCount":{"type":"number"},"featureCount":{"type":"number"},"items":{"type":"array","items":{}},"citedMentions":{"type":"array","description":"Cited press mentions via the CLUSTER spine (archive_api_v1.artist_press_mentions, cycle-107) — outlet/title/url/publishedAt/sourceRepo, newest first, ≤10, deduped by url. Serves BOTH entry lanes (the typed items[] remain the Discogs-entity path). UNFILTERED by design: the producer's extraction_confidence is a constant (no working precision signal); precision posture = gazetteer+LLM extraction across 38 outlets, ~13.6% article coverage — absence is COVERAGE, never 'no press'.","items":{"type":"object","required":["outlet","title","url","publishedAt","sourceRepo"],"properties":{"outlet":{"type":"string"},"title":{"type":"string"},"url":{"type":"string"},"publishedAt":{"type":"string","description":"ISO timestamp (evidence-side; never null upstream)."},"sourceRepo":{"type":["string","null"],"description":"The producer's dedup key — which crawl lane observed the article."}}}}},"required":["pressCount","reviewCount","interviewCount","featureCount","items"]}},"required":["state","signals"]},"connections":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":"object","properties":{"count":{"type":"number"},"related":{"type":"array","items":{"type":"object","required":["clusterId","discogsArtistId","mbid","name","slug","coEventCount","coVenueCount","lastCoAt"],"properties":{"clusterId":{"type":["string","null"],"description":"Co-artist's 64-hex cluster_id (the fleet identity-spine join key; carrefour#124) — null when the co-booking edge carries no cluster bind (consumers filter). Feed it straight to /api/v2/artist/{key}."},"discogsArtistId":{"type":["integer","null"],"description":"Co-artist's resolved Discogs id, or null for the unsigned tail."},"mbid":{"type":["string","null"],"description":"Co-artist's resolved MusicBrainz id, or null."},"name":{"type":"string","description":"Display name (always present) — the carrefour#124 displayName source (co_artist_name_display)."},"slug":{"type":"string","description":"Crate artist-page slug derived from the name."},"coEventCount":{"type":"integer","description":"Distinct events the two artists were booked into together."},"coVenueCount":{"type":"integer","description":"Distinct venues across those co-events."},"lastCoAt":{"type":["string","null"],"description":"ISO date of the most recent co-booking, or null."}}},"description":"Co-booked neighbours, strongest first — bounded at 24 rows (the carrefour#124 contract cap)."}},"required":["count","related"]}},"required":["state","signals"]},"emergence":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"emergenceTier":{"type":"string"},"momentumTier":{"type":"string"},"emergenceScore":{"type":"number"},"momentumScore":{"type":"number"},"establishmentDelta":{"type":"number"},"eventsRecent":{"type":"number"},"isFreshBreakout":{"type":"boolean"},"isUnsigned":{"type":"boolean"}},"required":["emergenceTier","momentumTier","emergenceScore","momentumScore","establishmentDelta","eventsRecent","isFreshBreakout","isUnsigned"]}},"required":["state","signals"]},"breakout":{"type":"object","properties":{"state":{"type":"string"},"signal":{"type":["object","null"],"properties":{"emergenceTier":{"type":"string"},"corroboration":{"type":"string"},"pressCount":{"type":"number"}},"required":["emergenceTier","corroboration","pressCount"]}},"required":["state","signal"]},"compositions":{"type":"object","properties":{"state":{"type":"string"},"works":{"type":"array","items":{"type":"object","properties":{"workMbid":{"type":"string"},"workName":{"type":"string"},"ownerReach":{"type":"number"},"versionCollectors":{"type":"number"},"versionDepth":{"type":"number"},"nRecordingsGlobal":{"type":"number"}},"required":["workMbid","workName","ownerReach","versionCollectors","versionDepth","nRecordingsGlobal"]}}},"required":["state","works"]},"tier_presence":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"spansTiers":{"type":"boolean"},"playsClubs":{"type":"boolean"},"playsFestivals":{"type":"boolean"},"clubEvents":{"type":"number"},"festivalEditions":{"type":"number"},"firstClubAt":{"type":["string","null"]},"lastClubAt":{"type":["string","null"]},"firstFestivalAt":{"type":["string","null"]},"lastFestivalAt":{"type":["string","null"]}},"required":["spansTiers","playsClubs","playsFestivals","clubEvents","festivalEditions","firstClubAt","lastClubAt","firstFestivalAt","lastFestivalAt"]}},"required":["state","signals"]},"early_demand":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"ownerReach":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["count"]},"value":{"type":"number"}},"required":["kind","value"]},{"type":"object","properties":{"kind":{"type":"string","enum":["suppressed"]}},"required":["kind"]}]},"wishlistDemand":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["count"]},"value":{"type":"number"}},"required":["kind","value"]},{"type":"object","properties":{"kind":{"type":"string","enum":["suppressed"]}},"required":["kind"]}]},"earliestSupportedAt":{"type":["string","null"]},"earliestWishedAt":{"type":["string","null"]},"demandCoverage":{"type":["number","null"]},"wishedCoverage":{"type":["number","null"]},"alsoOwnDegree":{"type":["number","null"]}},"required":["ownerReach","wishlistDemand","earliestSupportedAt","earliestWishedAt","demandCoverage","wishedCoverage","alsoOwnDegree"]}},"required":["state","signals"]},"bandcamp_emergence":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"emergenceClass":{"type":"string"},"demandLead":{"type":"number"},"demandRatio":{"type":["number","null"]},"ownerReach":{"type":["number","null"]},"wishlistDemand":{"type":["number","null"]},"distinctReleases":{"type":["number","null"]},"earliestWishedAt":{"type":["string","null"]}},"required":["emergenceClass","demandLead","demandRatio","ownerReach","wishlistDemand","distinctReleases","earliestWishedAt"]}},"required":["state","signals"]},"artwork":{"type":"object","properties":{"state":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ArtworkItem"}}},"required":["state","items"]},"dated_appearance":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":"object","properties":{"count":{"type":"number"},"sources":{"type":"array","items":{"type":"object","properties":{"sourceKind":{"type":"string"},"firstAppearanceDate":{"type":["string","null"]},"latestAppearanceDate":{"type":["string","null"]},"appearanceCount":{"type":"number"}},"required":["sourceKind","firstAppearanceDate","latestAppearanceDate","appearanceCount"]}}},"required":["count","sources"]}},"required":["state","signals"]},"network_position":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"degree":{"type":"number"},"weightedDegree":{"type":"number"},"resolvedNeighbourRatio":{"type":["number","null"]},"recentDegree":{"type":"number"},"establishmentDelta":{"type":["number","null"]}},"required":["degree","weightedDegree","resolvedNeighbourRatio","recentDegree","establishmentDelta"]}},"required":["state","signals"]},"airplay_breadth":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"firstAirplayDate":{"type":["string","null"]},"latestAirplayDate":{"type":["string","null"]},"airplayCount":{"type":"number"},"distinctStations":{"type":"number"},"distinctVenues":{"type":"number"},"distinctDjs":{"type":"number"}},"required":["firstAirplayDate","latestAirplayDate","airplayCount","distinctStations","distinctVenues","distinctDjs"]}},"required":["state","signals"]},"brokerage":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"degree":{"type":"number"},"effectiveSize":{"type":["number","null"]},"brokerageScore":{"type":["number","null"]},"brokerageTier":{"type":"string"}},"required":["degree","effectiveSize","brokerageScore","brokerageTier"]}},"required":["state","signals"]},"lead_time":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"pressToAirplayDays":{"type":"number"},"undergroundToBookingDays":{"type":"number"}},"required":["pressToAirplayDays","undergroundToBookingDays"]}},"required":["state","signals"]},"bandcamp_tastemaker":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"supporterCohortSize":{"type":"number"},"aesq":{"type":"number"},"aesqMedian":{"type":["number","null"]},"aesqRw":{"type":["number","null"]},"meanFirstBuyerEarliness":{"type":["number","null"]}},"required":["supporterCohortSize","aesq","aesqMedian","aesqRw","meanFirstBuyerEarliness"]}},"required":["state","signals"]},"live_demand":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"demandTier":{"type":"string"},"demandMomentumScore":{"type":["number","null"]},"eventsTracked":{"type":"number"},"eventsSoldOut":{"type":"number"},"distinctProviders":{"type":"number"},"distinctPromoters":{"type":"number"},"avgFillFraction":{"type":["number","null"]},"maxSoldOutLeadDays":{"type":["number","null"]}},"required":["demandTier","demandMomentumScore","eventsTracked","eventsSoldOut","distinctProviders","distinctPromoters","avgFillFraction","maxSoldOutLeadDays"]}},"required":["state","signals"]},"editorial_attention":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"memberCount":{"type":"number"},"sourceRepoCount":{"type":"number"},"firstObservedAt":{"type":["string","null"]},"latestObservedAt":{"type":["string","null"]}},"required":["memberCount","sourceRepoCount","firstObservedAt","latestObservedAt"]}},"required":["state","signals"]},"web_presence":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"bandcampUrl":{"type":["string","null"]},"soundcloudUrl":{"type":["string","null"]},"instagramUrl":{"type":["string","null"]},"websiteUrl":{"type":["string","null"]},"discogsArtistIdClaimed":{"type":["number","null"]},"raStatus":{"type":["string","null"]},"followerCount":{"type":["number","null"]},"country":{"type":["string","null"]},"firstEventAt":{"type":["string","null"]},"spotifyUrl":{"type":["string","null"],"description":"Authority-derived (mirror.external_artist_links, keyed by the CANONICAL discogs id — never the RA-claimed one) with the RA self-declared field as fallback."},"youtubeUrl":{"type":["string","null"],"description":"Same sourcing as spotifyUrl."},"lastfmUrl":{"type":["string","null"],"description":"Authority-derived only. No Apple Music field — the corpus carries no Apple links (honest absence)."}},"required":["bandcampUrl","soundcloudUrl","instagramUrl","websiteUrl","discogsArtistIdClaimed","raStatus","followerCount","country","firstEventAt","spotifyUrl","youtubeUrl","lastfmUrl"]}},"required":["state","signals"]},"editorial_canon":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"pickCount":{"type":"number"},"picks":{"type":"array","items":{"type":"object","properties":{"relation":{"type":"string"},"polarity":{"type":["string","null"]},"confidence":{"type":["number","null"]},"assertedBy":{"type":"string"},"isDirectQuote":{"type":"boolean"},"speakerName":{"type":["string","null"]},"objectName":{"type":"string"}},"required":["relation","polarity","confidence","assertedBy","isDirectQuote","speakerName","objectName"]}}},"required":["pickCount","picks"]}},"required":["state","signals"]},"discography":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":"object","properties":{"clusterId":{"type":["string","null"]},"total":{"type":"number"},"shown":{"type":"number"},"truncated":{"type":"boolean"},"entries":{"type":"array","items":{"type":"object","properties":{"discogsMasterId":{"type":"number"},"representativeName":{"type":["string","null"]},"isPrimary":{"type":"boolean"},"billingPosition":{"type":"number"},"_links":{"type":"object","properties":{"master":{"type":"string"}},"required":["master"]}},"required":["discogsMasterId","representativeName","isPrimary","billingPosition","_links"]}},"overMerge":{"type":["object","null"],"properties":{"discogsArtistIdCount":{"type":"number"},"folded":{"type":"boolean"}},"required":["discogsArtistIdCount","folded"]},"observedAt":{"type":["string","null"]}},"required":["clusterId","total","shown","truncated","entries","overMerge","observedAt"]}},"required":["state","signals"]},"provenance":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"producer":{"type":"string"},"sourceTable":{"type":"string"},"refreshCadence":{"type":"string"},"tier":{"type":"string"},"honestGapState":{"type":"string"},"detailAnchor":{"type":"string"}},"required":["field","producer","sourceTable","refreshCadence","tier","honestGapState"]}},"generated_at":{"type":"string"},"bandcamp_releases":{"type":"object","description":"Per-artist Bandcamp release list (cluster-first re-entry of the release grain demoted in the v2 cut). Populated whenever the artist resolves to a cluster identity (64-hex entry, or a slug that falls back to the booking-graph cluster); honest_gap otherwise. Summary rows only — fetch the full tracklist+durations+artwork+economics at GET /api/v2/artist/{key}/bandcamp/{item}.","required":["state","releases"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"releases":{"type":"array","items":{"$ref":"#/components/schemas/BandcampReleaseSummary"}}}},"rights":{"type":"object","description":"Rights-readiness for sync clearance (v2-only). Two provenance dimensions, both counts-only (no ISRC/ISWC values cross the wire). `signals` (cycle-088, MB proxy; entry re-anchored cycle-105): how identifiable (ISRC) and how registered (ISWC — the FACT) the artist's MB-bridged catalogue is; the catalogue entry is the UNION of the Discogs-master bridge and the artist's own MB credits (mirror.mb_recording_artist_credits primary credits + credited works) — cluster-native, served on BOTH lanes; a superset of the pre-2.44.0 masters-only cut. `ledger` (cycle-101, CLUSTER-anchored): what the catalogue has been REGISTERED as in ledger's rights ledger, and who wrote it — registered works, works with a registered ISWC, distinct co-writers, plus a SoundCloud non-MB leg. MB-bridged (reach == the MB proxy — MB recording coverage gates entry). COVERAGE, measured 2026-07-16 against ledger's full 59.8M-row isrc_work_crosswalk (daily delta; the ship-time 2.88M was a stalled slice): 53.1% of corpus ISRCs match (n=121,548) and matched works carry writer identity at 100% (measured n=230,405, both iswc:/prov: bridge modes). A leg that reaches nothing still serves an honest gap; the SoundCloud leg remains pilot-scale. Co-writer → performing-artist NAME is a deferred v2 (cowritersWithMbid marks the ceiling).","required":["state","signals","ledger"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["recordingsBridged","recordingsWithIsrc","isrcCount","worksBridged","worksWithIswc"],"properties":{"recordingsBridged":{"type":"integer","description":"Distinct recordings reachable from the artist masters via the MB bridge."},"recordingsWithIsrc":{"type":"integer","description":"…carrying ≥1 ISRC (identifiable recordings — the clearance prerequisite)."},"isrcCount":{"type":"integer","description":"Distinct ISRC codes across those recordings."},"worksBridged":{"type":"integer","description":"Distinct works (compositions) reachable via the bridge."},"worksWithIswc":{"type":"integer","description":"Works with a registered ISWC — the FACT only; the value is never exposed."}}},"ledger":{"type":["object","null"],"description":"Rights-REGISTRATION dimension (cycle-101; entry re-anchored cycle-103), cluster-anchored via the UNION of the artist's PRIMARY-credit recordings (mirror.mb_recording_artist_credits — cluster-native, reaches artists with no Discogs presence) and the recordings on their own Discogs masters, → ISRCs → ledger's ISRC↔work crosswalk → work-writer identity. The union is a superset of the pre-2.42.0 master-anchored cut for every artist. null = neither the ledger nor the SoundCloud leg reached anything (honest gap). Counts only — no writer_person_key / ISRC / ISWC values.","required":["registeredWorks","worksWithRegisteredIswc","distinctCowriters","cowritersWithMbid","soundcloud"],"properties":{"registeredWorks":{"type":"integer","description":"Distinct works from the artist's catalogue registered in ledger's rights ledger (canonical_work_key)."},"worksWithRegisteredIswc":{"type":"integer","description":"…of which carry a registered ISWC."},"distinctCowriters":{"type":"integer","description":"Distinct co-writer/songwriter person_keys credited across those works (OUTPUT count; raw keys never exposed)."},"cowritersWithMbid":{"type":"integer","description":"Distinct co-writer MB artist_mbids — the ceiling for the DEFERRED co-writer→performing-name mapping (~0 today; the ~22% path, carrefour#116)."},"soundcloud":{"type":["object","null"],"description":"SoundCloud rights-binding coverage (seen; non-MB, cluster-keyed). null when the cluster has no SoundCloud rows (pilot-scale).","required":["tracksWithIsrc","tracksBound","tracksWithRegisteredIswc"],"properties":{"tracksWithIsrc":{"type":"integer","description":"SoundCloud tracks carrying an ISRC."},"tracksBound":{"type":"integer","description":"…bound to a ledger work."},"tracksWithRegisteredIswc":{"type":"integer","description":"…whose work carries a registered ISWC."}}}}}}},"marketplace":{"type":"object","description":"Discogs MARKETPLACE supply/price for the artist's catalogue (v2-only; mirror.marketplace_signal_by_cluster_v1, cluster-anchored — both entry paths reach it). The demand model's third leg beside collection reach (signals.ownerReach) and wantlist level: how many releases are priced on the market, total copies for sale (supply pressure), the market floor, and typical/ceiling sale levels. `latest` is the newest weekly snapshot (the producer-contracted current state); `series` tells you how much history stands behind it. PUBLIC release-level marketplace facts deduped to release before rollup — not collector behaviour. Amounts within-currency (USD — all live data). null signals = no marketplace data observed for the cluster (honest gap, never zeros).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["currency","latest","series"],"properties":{"currency":{"type":"string","description":"ISO 4217 — 'USD' for all live data (amounts are within-currency, USD-filtered at source)."},"latest":{"type":"object","required":["snapshotDate","pricedReleaseCount","totalCopiesForSale","minPrice","medianSales","maxSales"],"properties":{"snapshotDate":{"type":"string","description":"Snapshot date (YYYY-MM-DD) these numbers were observed — the newest weekly snapshot."},"pricedReleaseCount":{"type":"integer","description":"Distinct releases by this artist-cluster with a marketplace price observed (catalogue/market fact, NOT a user count)."},"totalCopiesForSale":{"type":"integer","description":"Sum of copies-for-sale across the cluster's priced releases — supply pressure."},"minPrice":{"type":["number","null"],"description":"Market floor: lowest listing across the cluster's releases (USD). null = not observed."},"medianSales":{"type":["number","null"],"description":"Typical sale level: median of per-release sales medians (USD). null = not observed."},"maxSales":{"type":["number","null"],"description":"Historical ceiling: max of per-release sales maxima (USD). null = not observed."}}},"series":{"type":"object","required":["snapshots","firstSnapshotDate"],"properties":{"snapshots":{"type":"integer","description":"Weekly snapshots observed for this cluster (>= 1) — the depth behind `latest`."},"firstSnapshotDate":{"type":"string","description":"Earliest snapshot date (YYYY-MM-DD) — the start of this cluster's observed series."}}}}}}},"debut":{"type":"object","description":"First-release DEBUT anchor (v2-only; mirror.artist_first_release_v1 — MusicBrainz's earliest dated release-group crediting the artist, plus the dated-release-group count behind it). An \"in the MusicBrainz index\" catalogue claim, never a biography fact. Served via the EXACT Discogs↔MB bind only (Discogs-resolved artists; cluster-path artists read honest_gap — cluster/name keying was measured to graft homonym facts and is deliberately refused). Dates are YEAR-only granularity: month/day are Jan-1 placeholders. Rare MB source-date errors exist at the extremes — treat implausible years as source noise, not fact. null signals = no dated MB release or no Discogs↔MB bind (honest gap).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["firstReleaseDate","releaseGroupCount","artistMbid","mbidMatches","computedAt"],"properties":{"firstReleaseDate":{"type":"string","description":"Earliest dated MB release-group (YYYY-MM-DD; YEAR-only granularity — month/day are Jan-1 placeholders)."},"releaseGroupCount":{"type":"integer","description":"Dated release-groups crediting the artist in MusicBrainz — the catalogue span behind the date."},"artistMbid":{"type":"string","description":"The MB artist the debut belongs to (the exact Discogs-bound entry) — an onward MusicBrainz key."},"mbidMatches":{"type":"integer","description":"Distinct MB artists bound to this Discogs id (>1 = MB-side duplicates; the earliest is served)."},"computedAt":{"type":"string","description":"Producer refresh timestamp (monthly MB dump cadence)."}}}}},"facts":{"type":"object","description":"Structured Wikidata biographical facts (v2-only; mirror.artist_facts_v1, CC0 — re-publishable, no attribution): origin city/country (human-readable + QIDs), birth/death dates, official website, and genre/label/influence QID arrays. As-recorded-in-Wikidata claims, not verified biography. Discogs-resolved artists join 1:1 on the producer's PK; cluster-path artists are served only when EXACTLY ONE facts row binds the cluster (the surface's cluster key is name-minted and homonyms co-merge by design — an ambiguous bind reads honest_gap, never a guess). RESOLVED NAMES ship alongside the QIDs (genres/recordLabels/influencedBy — render the names; QIDs are exact-join keys); influencedBy(_Qids) is SPARSE and non-canonical (gate any influence UI; MusicBrainz-native edges are the canonical lineage source). PORTRAIT: `portrait.url` is a Wikidata P18 → Commons FilePath pointer (append ?width=N for a thumbnail) whose FILE carries its own per-file licence, resolved in-surface — CONTRACT: render ONLY when `portrait.displayReady` is true; licence 'Public domain' needs no attribution, any other licence must display `portrait.credit` and link `portrait.licenceUrl`. displayReady=false = licence resolution pending. null signals = not-yet-enriched or ambiguous bind (absent == unknown).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["wikidataQid","mbid","originCity","originCityQid","originCountry","originCountryQid","birthDate","deathDate","officialWebsite","genresQids","recordLabelsQids","influencedByQids","genres","recordLabels","influencedBy","birthName","familyName","portrait","lastEnrichedAt"],"properties":{"wikidataQid":{"type":"string","description":"Wikidata self QID — provenance + onward key."},"mbid":{"type":"string","description":"MusicBrainz artist id (unique on the surface) — onward key."},"originCity":{"type":["string","null"],"description":"Human-readable origin city (Wikidata; dense field)."},"originCityQid":{"type":["string","null"]},"originCountry":{"type":["string","null"],"description":"Human-readable origin country."},"originCountryQid":{"type":["string","null"]},"birthDate":{"type":["string","null"],"description":"As recorded in Wikidata (YYYY-MM-DD) — a catalogue claim, not a verified biography fact."},"deathDate":{"type":["string","null"]},"officialWebsite":{"type":["string","null"],"description":"The artist's own site per Wikidata P856 (link-only)."},"genresQids":{"type":"array","items":{"type":"string"},"description":"RAW Wikidata Q-numbers (P136) — resolve before rendering; never display as text."},"recordLabelsQids":{"type":"array","items":{"type":"string"},"description":"RAW Q-numbers (P264) — same caveat."},"influencedByQids":{"type":"array","items":{"type":"string"},"description":"RAW Q-numbers (P737) — SPARSE + non-canonical (<5% populated); MB-native edges are canonical."},"genres":{"type":"array","items":{"type":"string"},"description":"Resolved en-labels for genresQids (QIDs lacking an en-label are dropped here, kept in the QID array)."},"recordLabels":{"type":"array","items":{"type":"string"},"description":"Resolved en-labels for recordLabelsQids."},"influencedBy":{"type":"array","items":{"type":"string"},"description":"Resolved names for influencedByQids — same sparse/non-canonical gating caveat."},"birthName":{"type":["string","null"],"description":"Wikidata P1477 (~8% fill)."},"familyName":{"type":["string","null"],"description":"Wikidata P734 surname (~48% fill)."},"portrait":{"type":["object","null"],"description":"Commons portrait with its PER-FILE licence resolved in-surface. Render ONLY when displayReady; 'Public domain' needs no attribution, else show credit + link licenceUrl. null = no image.","required":["url","licence","licenceUrl","credit","displayReady"],"properties":{"url":{"type":"string","description":"Commons Special:FilePath URL (CC0 POINTER — the file is separately licensed). Append ?width=N for a thumbnail."},"licence":{"type":["string","null"],"description":"Commons LicenseShortName (e.g. 'CC BY-SA 4.0', 'Public domain'). null = resolution pending — do not render."},"licenceUrl":{"type":["string","null"],"description":"Licence deed link — display it with the credit for non-PD licences."},"credit":{"type":["string","null"],"description":"Attribution string (HTML-stripped)."},"displayReady":{"type":"boolean","description":"true = licence resolved; safe to render honoring the contract above."}}},"lastEnrichedAt":{"type":"string","description":"Upstream Wikidata enrichment time — the honest \"as of\" for these facts."}}}}},"visibility":{"type":"object","description":"Institutional-visibility badge (v2-only; seen.radar_only_artists_v1): every dimension-converging artist (>=2 independent live-circuit dimensions in 18 months) classed canon_alive | canon_quiet | uncanonized_alive | uncanonized_quiet. institutionallyInvisible (= NOT institutionally catalogued: no authority record AND no seen-side Discogs bind — the mig-0331 CORRECTED cut; the old authority-only cut over-stated invisibility by up to ~21.4%) is the producer's badge cut; radarOnly (uncanonized_quiet) is the strict cut. PRODUCER CAVEATS, verbatim: 'alive' = ListenBrainz-corpus presence — a coverage-biased LOWER bound (Western/indie/electronic lean; sub-k-anon clusters read as no-flow) — quiet ≠ dead; some uncanonized rows are name-variant/persona sibling mints of catalogued artists; composition not timing — no lead/lag claim. Flow magnitudes are mirror's k>=5 values verbatim (null = no k>=5 catalogued flow, never zero). null signals = not dimension-converging (universe floor), not a verdict on the artist.","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["institutionalVisibility","institutionallyInvisible","radarOnly","hasAuthorityRecord","hasListenFlow","independentDimensions","dimensions","firstSignal","latestSignal","spanDays","inBookingCircuit","listenCount","distinctListenerCount","lastListenedAt"],"properties":{"institutionalVisibility":{"type":"string","enum":["canon_alive","canon_quiet","uncanonized_alive","uncanonized_quiet"],"description":"The producer's frozen four-quadrant vocabulary."},"institutionallyInvisible":{"type":"boolean","description":"The badge cut (mig-0331 corrected): NOT institutionally catalogued — no authority record AND no seen-side Discogs bind. The previous authority-only cut over-stated invisibility by up to ~21.4%. The seen-side Discogs bind is heuristic-not-truth (~0.3% internal mbid<->discogs inconsistency, the same resolver class as the authority record); mb_only clusters deliberately not folded."},"radarOnly":{"type":"boolean","description":"Strict cut: converging with no authority record AND no catalogued listens — visible to nothing public except the live circuit."},"hasAuthorityRecord":{"type":"boolean"},"hasListenFlow":{"type":"boolean","description":"ListenBrainz k>=5 presence — coverage-biased lower bound; false ≠ unheard."},"independentDimensions":{"type":["integer","null"],"description":"Convergence depth (>=2 by universe construction)."},"dimensions":{"type":["string","null"],"description":"Comma-joined independent dimension list."},"firstSignal":{"type":["string","null"]},"latestSignal":{"type":["string","null"]},"spanDays":{"type":["integer","null"]},"inBookingCircuit":{"type":["boolean","null"]},"listenCount":{"type":["integer","null"],"description":"Mirror's k>=5 value verbatim; null = no k>=5 flow, never zero."},"distinctListenerCount":{"type":["integer","null"]},"lastListenedAt":{"type":["string","null"]}}}}},"lineage":{"type":"object","description":"Creative-LINEAGE facet (v2-only; MusicBrainz's recording→recording derivation graph — mirror.mb_recording_relations, ~238K remix/sample/edit/dj-mix/mashup edges — made per-artist-addressable through the mb_recording_artist_credits bridge). DIRECTION is verified against live titles: in an edge src→dst, src is the DERIVATIVE and dst is the SOURCE. So `asDerivative` counts the artist's recordings that remix/sample/edit another recording; `asSource` counts recordings another recording derived from. IMPORTANT billing-grain caveat: MB credits a remix recording to the ORIGINAL artist (a Photomachine remix of a Four Tet track is credited \"Four Tet\"), so `asDerivative` largely reflects OTHERS remixing this artist's work — read these as GRAPH PARTICIPATION, never as authorship (\"this artist remixed N others\" is NOT a valid reading). Discogs-resolved artists aggregate their MB-duplicate mbids; cluster-path artists are served only when EXACTLY ONE mb_artists row binds the cluster (name-minted cluster keys co-merge homonyms — an ambiguous bind reads honest_gap). `edges` is a capped, deterministic, balanced sample (≤12 per direction) — the counts are the full totals. otherArtistCredit is the AS-CREDITED billing string (never a join key). null signals = no edges in the graph (honest gap; ~97% of artists).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["asDerivative","asSource","edges","artistMbidCount"],"properties":{"asDerivative":{"type":["object","null"],"required":["remix","sample","edit","djMix","mashup","total"],"properties":{"remix":{"type":"integer"},"sample":{"type":"integer"},"edit":{"type":"integer"},"djMix":{"type":"integer"},"mashup":{"type":"integer"},"total":{"type":"integer","description":"Sum across rel types (edges in this direction)."}},"description":"The artist's recordings that are the DERIVATIVE end (remix/sample/edit OF another recording). Graph participation, not authorship."},"asSource":{"type":["object","null"],"required":["remix","sample","edit","djMix","mashup","total"],"properties":{"remix":{"type":"integer"},"sample":{"type":"integer"},"edit":{"type":"integer"},"djMix":{"type":"integer"},"mashup":{"type":"integer"},"total":{"type":"integer","description":"Sum across rel types (edges in this direction)."}},"description":"The artist's recordings that are the SOURCE end (another recording derived from them)."},"edges":{"type":"array","description":"Capped balanced sample (≤12/direction) of concrete edges — illustrative, not the whole graph (counts are the totals).","items":{"type":"object","required":["relType","direction","otherRecordingMbid","otherRecordingName","otherArtistCredit"],"properties":{"relType":{"type":"string","enum":["remix","sample","edit","dj-mix","mashup"],"description":"MB relation type, verbatim."},"direction":{"type":"string","enum":["derived","source"],"description":"'derived' = this artist's recording derives from the counterparty; 'source' = the counterparty derives from this artist's recording."},"otherRecordingMbid":{"type":"string","description":"Counterparty recording MB gid — onward key."},"otherRecordingName":{"type":["string","null"],"description":"Counterparty recording title (MB), or null if unresolved."},"otherArtistCredit":{"type":["string","null"],"description":"Counterparty AS-CREDITED billing string — descriptive only, never a join key."}}}},"artistMbidCount":{"type":"integer","description":"MB artist mbids the key resolved to (>1 on the Discogs path = MB-side dupes; cluster path is gated to 1)."}}}}},"authorship":{"type":"object","description":"AUTHORSHIP facet (v2-only; mirror.songwriter_signals_v1, cluster-grain — one row per pe-norm-v1 cluster credited on ≥1 MusicBrainz work). This is the \"who WROTE it\" axis, distinct from `rights` (what the recorded catalogue is registered as + its co-writers). `worksWritten` = DISTINCT MB works credited in ANY role; `roles` is the per-role DISTINCT-work breakdown (composer/lyricist/writer/arranger — a work can hold >1 role, so the parts may exceed the whole). `recordingsOfWorks` = DISTINCT recordings of those works (catalogue realisation, not commercial releases). The enrichment legs are HONEST-SPARSE (~10% of works): `worksWithRights` = works matched in ledger's rights rollup; `worksWithPlacement` + `placementTitleReach` = film/TV placement reach. IMPORTANT coverage caveat: MusicBrainz authorship coverage is UNEVEN — strong for classical/composed repertoire, THIN for producer/electronic credits MB records as performance rather than authorship (a well-known electronic artist can read worksWritten=3 here while carrying dozens of recorded-catalogue registered works on the `rights` facet). A 0 on an enrichment leg is \"not observed\", never \"no rights\". Cluster-keyed on both entry paths (Discogs resolves discogs→cluster); a homonym-merged cluster blends its co-merged artists' authorship (a spine property, not re-adjudicated here). null signals = the cluster is credited on no MB work (honest gap — the norm for performers who aren't recorded writers). Counts only; non-user-derived at source.","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["worksWritten","roles","recordingsOfWorks","worksWithRights","worksWithPlacement","placementTitleReach"],"properties":{"worksWritten":{"type":"integer","description":"DISTINCT MB works this cluster is credited on in any role (≥1 when present)."},"roles":{"type":"object","description":"Per-role DISTINCT-work breakdown; parts may sum above worksWritten (a work can hold >1 role).","required":["composer","lyricist","writer","arranger"],"properties":{"composer":{"type":"integer"},"lyricist":{"type":"integer"},"writer":{"type":"integer"},"arranger":{"type":"integer"}}},"recordingsOfWorks":{"type":"integer","description":"DISTINCT recordings of their works (MB recordings, not commercial releases)."},"worksWithRights":{"type":"integer","description":"Works matched in ledger's rights rollup — HONEST-SPARSE; 0 = not observed, not \"no rights\"."},"worksWithPlacement":{"type":"integer","description":"Works with a film/TV placement signal — honest-sparse."},"placementTitleReach":{"type":"integer","description":"Σ resolved screen titles across their placed works."}}}}},"listen_flow":{"type":"object","description":"LISTEN-FLOW facet (v2-only; mirror.listen_flow_by_cluster_v1 — ListenBrainz streaming attention per cluster). listenCount + distinctListeners + distinctRecordings + first/lastListenedAt. K-ANON'D UPSTREAM (distinctListeners ≥ 5 when the row exists) — presence is a LOWER BOUND on aliveness; absence NEVER means no listeners (below-floor or MBID-unmapped: ~29% of listens carry no MBID upstream and never reach any cluster; the k≥5 floor suppresses ~44% of listened clusters). null signals = honest gap.","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["listenCount","distinctListeners","distinctRecordings","firstListenedAt","lastListenedAt"],"properties":{"listenCount":{"type":"integer","description":"Total mapped listens — a lower bound, never a global play count."},"distinctListeners":{"type":"integer","description":"≥5 by the producer k-anon floor when present."},"distinctRecordings":{"type":"integer"},"firstListenedAt":{"type":["string","null"]},"lastListenedAt":{"type":["string","null"]}}}}},"arrival":{"type":"object","description":"ARRIVAL facet (v2-only; wintermute.cluster_arrival_wide_v1 — per-substrate-layer first-arrival pivot). layersPresent + per-layer *FirstAt timestamps + anyAliasBridged. CLOCK CAVEATS ARE LOAD-BEARING: catalogueFirstAt + placementFirstAt are YEAR-grain Jan-1 placeholders (sub-year comparison forbidden); implausibly-old values (e.g. 1970-01-01) are source sentinels, never facts; seenKnownSince is a DETECTION clock — never rank it against the event columns. NULL column = no defensible arrival in that layer, never imputed. null signals = no arrival row (honest gap).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["layersPresent","anyAliasBridged","listenFirstAt","bandcampFirstAt","attentionFirstAt","authorityFirstAt","seenEventFirstAt","seenKnownSince","editorialFirstAt","catalogueFirstAt","placementFirstAt"],"properties":{"layersPresent":{"type":"integer","description":"Count of layers with a defensible first-arrival."},"anyAliasBridged":{"type":["boolean","null"]},"listenFirstAt":{"type":["string","null"]},"bandcampFirstAt":{"type":["string","null"]},"attentionFirstAt":{"type":["string","null"]},"authorityFirstAt":{"type":["string","null"]},"seenEventFirstAt":{"type":["string","null"],"description":"MIN over ALL seen:* kinds — mixes stated with evidence-class; never rank against a single-class column."},"seenKnownSince":{"type":["string","null"],"description":"DETECTION clock — when the fleet first detected, not when the event happened."},"editorialFirstAt":{"type":["string","null"]},"catalogueFirstAt":{"type":["string","null"],"description":"YEAR-grain Jan-1 placeholder — sub-year comparison forbidden."},"placementFirstAt":{"type":["string","null"],"description":"YEAR-grain Jan-1 placeholder — sub-year comparison forbidden."}}}}},"aura":{"type":"object","description":"AURA facet (v2-only; seen.artist_substrate_signal_v1 — the /api/v2/aura signal folded per-cluster). convergenceDimCount + dimensions[] + breakOdds + firstSignalAt/latestAppearanceAt + break-event flags. DEEP not GLOBAL: convergence across the signals hosaka observes, not a popularity meter. breakOdds is the measured BASE RATE of past artists at that convergence depth breaking within 12 months — NEVER a per-artist forecast. ~11K clusters converge; null signals = not converging (honest gap — the norm).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["convergenceDimCount","dimensions","breakOdds","firstSignalAt","latestAppearanceAt","hasBreakEvent","breakEventAt"],"properties":{"convergenceDimCount":{"type":"integer"},"dimensions":{"type":"array","items":{"type":"string"}},"breakOdds":{"type":["number","null"],"description":"Base rate at this convergence depth — not a per-artist forecast."},"firstSignalAt":{"type":"string"},"latestAppearanceAt":{"type":"string"},"hasBreakEvent":{"type":"boolean"},"breakEventAt":{"type":["string","null"]}}}}},"dj_champion":{"type":"object","description":"DJ-CHAMPION facet (v2-only; seen.dj_champion — WHICH DJs championed this artist). championCount/earlyChampionCount/repeatChampionCount are FULL totals; topChampions[] is a capped sample (≤8, playCount DESC) with each champion's dj clusterId as an onward key (/api/v2/artist/{hex}). isEarlyChampion = played within the early window after the artist's first corpus play. The full edge list pages via GET /api/v2/surface/seen.dj_champion. null signals = no championing DJs observed (honest gap).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["championCount","earlyChampionCount","repeatChampionCount","topChampions"],"properties":{"championCount":{"type":"integer","description":"Distinct championing DJs (full total)."},"earlyChampionCount":{"type":"integer"},"repeatChampionCount":{"type":"integer"},"topChampions":{"type":"array","description":"Capped sample (≤8) by playCount.","items":{"type":"object","required":["djClusterId","playCount","playEventCount","firstPlayedAt","latestPlayedAt","isEarlyChampion","isRepeatChampion"],"properties":{"djClusterId":{"type":"string","description":"The championing DJ's 64-hex cluster_id — an onward key."},"playCount":{"type":"integer"},"playEventCount":{"type":"integer"},"firstPlayedAt":{"type":["string","null"]},"latestPlayedAt":{"type":["string","null"]},"isEarlyChampion":{"type":"boolean"},"isRepeatChampion":{"type":"boolean"}}}}}}}},"radio_play":{"type":"object","description":"RADIO-PLAY facet (v2-only; seen.radio_play_v1 — the LATEST dated titled plays, ≤10, quarantined/out-of-sanity-window excluded). The dossier's current-signal detail: playedOn + stationKey + djName + extracted title/remix + sourceType. `airplay_breadth` carries the full rollup counts; the full play archive pages via GET /api/v2/surface/seen.radio_play_v1. Titles are tracklist-extracted — many plays carry NO title (title null, honest by design). null signals = no dated in-window plays (honest gap).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["recentPlays"],"properties":{"recentPlays":{"type":"array","description":"Latest dated plays (≤10, playedOn DESC).","items":{"type":"object","required":["playedOn","stationKey","djName","title","remix","sourceType"],"properties":{"playedOn":{"type":"string"},"stationKey":{"type":"string"},"djName":{"type":["string","null"]},"title":{"type":["string","null"],"description":"Tracklist-extracted; null = untitled play row."},"remix":{"type":["string","null"]},"sourceType":{"type":"string"}}}}}}}},"credits":{"type":"object","description":"WHO wrote / WHO produced, with names (v2-only; cycle-094, entry re-anchored cycle-104): the artist's catalogue joined to the MusicBrainz credit tables (mirror MB Phase-3), names + onward keys via mb_artists. The catalogue entry is the UNION of the Discogs-master bridge and the artist's own MB credits (mirror.mb_recording_artist_credits primary credits + credited works) — cluster-native, so artists with no Discogs presence are served; a superset of the pre-2.43.0 masters-only cut. Complements the rights facet's counts with the diligence NAMES. Performance role families (performer/vocal/instrument) are deliberately excluded — the desk, not the lineup. The subject is NOT excluded (self-written/self-produced is signal).","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["writing","production"],"properties":{"writing":{"type":"array","description":"Songwriting credits, strongest-first (≤12).","items":{"type":"object","required":["name","mbid","discogsArtistId","clusterId","role","count"],"properties":{"name":{"type":["string","null"],"description":"MB-canonical display name (null only when MB holds no name row)."},"mbid":{"type":"string","description":"Credited person's MusicBrainz id."},"discogsArtistId":{"type":["integer","null"],"description":"Onward Discogs id when MB binds one."},"clusterId":{"type":["string","null"],"description":"Onward 64-hex fleet cluster_id when bound — feed straight to /api/v2/artist/{key}."},"role":{"type":"string","description":"role-family@1 string (composer/lyricist/writer/… | producer/engineer/mix/…)."},"count":{"type":"integer","description":"Distinct bridged works (writing) or recordings (production) carrying the credit."}}}},"production":{"type":"array","description":"Production-desk credits, strongest-first (≤12).","items":{"type":"object","required":["name","mbid","discogsArtistId","clusterId","role","count"],"properties":{"name":{"type":["string","null"],"description":"MB-canonical display name (null only when MB holds no name row)."},"mbid":{"type":"string","description":"Credited person's MusicBrainz id."},"discogsArtistId":{"type":["integer","null"],"description":"Onward Discogs id when MB binds one."},"clusterId":{"type":["string","null"],"description":"Onward 64-hex fleet cluster_id when bound — feed straight to /api/v2/artist/{key}."},"role":{"type":"string","description":"role-family@1 string (composer/lyricist/writer/… | producer/engineer/mix/…)."},"count":{"type":"integer","description":"Distinct bridged works (writing) or recordings (production) carrying the credit."}}}}}}}},"placements":{"type":"object","description":"Placement/type history rollup (v2-only; cycle-094): the bridged catalogue’s MusicBrainz release-groups typed by secondary_types — \"has this artist been synced/soundtracked before\". COUNTS ONLY. null signals = the bridge reached no release-groups (unmeasured); a present row with soundtracks=0 is an answer (\"measured — no placements\").","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["releaseGroupsLinked","soundtracks","compilations","remixes","liveReleases","earliestSoundtrack"],"properties":{"releaseGroupsLinked":{"type":"integer","description":"Distinct MB release-groups reached (the measurement base)."},"soundtracks":{"type":"integer","description":"Release-groups typed Soundtrack — the placement signal."},"compilations":{"type":"integer"},"remixes":{"type":"integer"},"liveReleases":{"type":"integer"},"earliestSoundtrack":{"type":["string","null"],"description":"Earliest MB first-release date among soundtrack groups (year-accurate)."}}}}},"geography":{"type":"object","description":"Primary geography at the artist grain (v2-only; cycle-092): the producer-resolved scene location (seen.artist_primary_geography, cluster_id-keyed via the performing-entity spine — unsigned/no-Discogs artists carry it too). honest_gap when the producer holds no location for the cluster.","required":["state","signals"],"properties":{"state":{"type":"string","enum":["present","honest_gap"]},"signals":{"type":["object","null"],"required":["city","region","country"],"properties":{"city":{"type":["string","null"],"description":"Primary city, producer-resolved (e.g. Bristol)."},"region":{"type":["string","null"],"description":"Primary region/state, or null."},"country":{"type":["string","null"],"description":"Primary country, or null."}}}}}},"required":["contract_version","grain","slug","display","id","cluster_id","resolved_via","identity","behavioral","editorial","across_the_web","journalism","connections","emergence","breakout","compositions","tier_presence","early_demand","bandcamp_emergence","artwork","dated_appearance","network_position","airplay_breadth","brokerage","lead_time","bandcamp_tastemaker","live_demand","editorial_attention","web_presence","editorial_canon","discography","provenance","generated_at"]},"LabelDossierContract":{"type":"object","properties":{"contract_version":{"type":"string"},"grain":{"type":"string","enum":["label"]},"slug":{"type":"string"},"display":{"type":"string"},"id":{"type":["number","null"],"description":"Resolved Discogs label id (the LEAF coordinate); null = cluster-only / unresolved."},"cluster_id":{"type":["string","null"],"description":"crate's CANONICAL artist identity — a pe-norm-v1 hex string. The SAME artist across Discogs, MusicBrainz and Bandcamp collapses to ONE cluster_id, so this is the key you store and the key you address every artist surface off of (/artist/{key} takes a 64-hex cluster_id directly). WHY IT MATTERS: it is crate's prime IP — the non-Discogs long-tail join key; discogs_artist_id / mbid are mere leaf coordinates onto it. GOTCHA: it is an OPAQUE string — pass it through verbatim, NEVER numericize, parse, or compare it as a number. null is an HONEST GAP (the name/link couldn't be resolved to a cluster), NOT an error — you still get HTTP 200. A 64-hex cluster_id always resolves at OBSERVED tier (resolved_via:'cluster'), never re-anchored onto a same-name Discogs row.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"resolved_via":{"type":["string","null"],"enum":["discogs","cluster"],"description":"The binding TIER — how trustworthy the identity match is. 'discogs' = canonical, Discogs-bound (verified). 'cluster' = OBSERVED/UNVERIFIED identity inferred from the seen booking graph with no Discogs bind. null = the lookup did not resolve at all (honest gap). WHY IT MATTERS: it is a trust signal you must respect — a 'cluster' result is crate showing you what it can SEE in the booking graph, not what it has verified. GOTCHA: surface a 'cluster' result as flagged/unverified, NEVER as canonical truth; do not silently merge a 'cluster' artist with a verified one. A bare 64-hex key always comes back 'cluster' by design (it skips the cc0_artists lookup so a hex address never re-anchors onto a same-name Discogs row).","example":"discogs"},"identity":{"type":["object","null"],"properties":{"discogsLabelId":{"type":["number","null"]},"name":{"type":"string"},"parentLabel":{"type":["string","null"]},"profile":{"type":["string","null"]},"urls":{"type":"array","items":{"type":"string"}}},"required":["discogsLabelId","name","parentLabel","profile","urls"]},"binds":{"type":["object","null"],"properties":{"discogs_label_id":{"type":["number","null"]},"label_mbid":{"type":["string","null"]},"bandcamp_url":{"type":["string","null"],"description":"The label's self-declared Bandcamp page URL from the cross-source identity spine (a LINK, never fetched — same rehost:false posture as artwork). WHY IT MATTERS: it is the canonical 'listen/buy on Bandcamp' address for the label, and the bind that lets a Bandcamp-native (cluster-only) label still resolve. GOTCHA: it is a page link, not an API or audio endpoint; it is nullable (no Bandcamp bind for this cluster yet) — null is an honest gap, not an error.","example":"https://labelname.bandcamp.com"}},"required":["discogs_label_id","label_mbid","bandcamp_url"],"description":"cycle-L1: cross-source identity binds from label_identity_v1. bandcamp_url is a LINK only (rehost:false, never fetched)."},"presence":{"type":["object","null"],"properties":{"has_discogs":{"type":"boolean"},"has_bandcamp":{"type":"boolean"},"has_seen_momentum":{"type":"boolean"},"has_editorial":{"type":"boolean"}},"required":["has_discogs","has_bandcamp","has_seen_momentum","has_editorial"],"description":"cycle-L1: which source planes this cluster is observed in (presence flags, not measured counts)."},"over_merge":{"type":["object","null"],"properties":{"discogs_label_id_count":{"type":"number","description":"OVER-MERGE observability: how many distinct raw Discogs label ids folded into this one label_cluster_id. WHY IT MATTERS: a label cluster is name-derived, so distinct Discogs labels that share a normalized name can collapse together; this count is crate's honesty signal that the cluster may conflate more than one real label. GOTCHA: when it is >1 the dossier is forced to the OBSERVED tier (resolved_via:'cluster') and surfaces an `over_merge` block — do NOT present such a cluster's identity or signals as authoritative for a single label. 1 (or absent) means a clean single-label cluster.","example":"1"},"folded":{"type":"boolean","enum":[true]}},"required":["discogs_label_id_count","folded"],"description":"cycle-L1: present ONLY when >1 raw Discogs label id folded into this cluster (over-merge honesty) — the dossier is then forced to the OBSERVED tier."},"behavioral":{"type":"object","properties":{"state":{"type":"string"},"signals":{}},"required":["state"]},"travels":{"type":"object","properties":{"state":{"type":"string"},"related":{"type":"array","items":{"type":"object","properties":{"clusterId":{"type":"string"},"labelName":{"type":["string","null"]},"npmi":{"type":"number","description":"The rarity-weighted normalized pointwise mutual information of a Bandcamp co-ownership edge — how much more often two labels are co-collected than chance, downweighting ubiquitous labels. WHY IT MATTERS: it ranks the `travels` adjacency (higher = a stronger, more distinctive co-ownership tie) so you can take the top few as the label's nearest neighbors. GOTCHA: it is a relative affinity score, NOT a count or a probability — don't sum it, threshold it absolutely, or show it as a percentage; use it only to order edges. Pair it with `support` (the distinct-supporter count behind the edge, k-anon floor 5).","example":"0.42"},"support":{"type":"number"},"_links":{"type":"object","properties":{"label":{"type":"string"}},"required":["label"]}},"required":["clusterId","labelName","npmi","support","_links"]}}},"required":["state","related"],"description":"cycle-L1: Bandcamp co-ownership adjacency (mirror.bandcamp_label_travels_v1, cluster-keyed, k-anon k>=5). Each entry links onward to the label by its cluster_id (GET /api/v2/label/{cluster_id})."},"dj_momentum":{"type":"object","properties":{"state":{"type":"string"},"signals":{"type":["object","null"],"properties":{"playCount":{"type":"number"},"distinctDjs":{"type":"number"},"distinctArtists":{"type":"number"},"distinctTracklists":{"type":"number"},"firstPlayedAt":{"type":["string","null"]},"lastPlayedAt":{"type":["string","null"]},"playsLast365d":{"type":"number","description":"How many times the label's roster was played in DJ sets in the last 365 days — the RECENCY signal of label DJ-momentum (seen.label_djset_momentum). WHY IT MATTERS: it is the lead 'which labels are breaking right now' number; pair it with distinctArtists (roster breadth) to separate a one-hit label from a deep accelerating roster. GOTCHA: it is a raw count over public DJ tracklists (no per-fan data), not a rate — divide by playCount to get `velocity`. A label with no DJ plays has no row at all (dj_momentum.state='honest_gap'), not a zero.","example":"342"},"velocity":{"type":["number","null"],"description":"The share of a label's all-time DJ-set plays that happened in the last 365 days: playsLast365d / playCount ∈ [0,1]. WHY IT MATTERS: it normalizes momentum so a young breaking label (near 1.0) is distinguishable from a long-established catalogue label with the same recent count but a huge back-catalogue (near 0). GOTCHA: it is null when playCount is 0 (no all-time plays — avoid div-by-zero); it is a ratio, not a percentage or a count — don't sum it across labels.","example":"0.61"}},"required":["playCount","distinctDjs","distinctArtists","distinctTracklists","firstPlayedAt","lastPlayedAt","playsLast365d","velocity"]}},"required":["state","signals"],"description":"cycle-L2: per-label DJ-set momentum (seen.label_djset_momentum, cluster-keyed) — which labels are breaking in DJ sets. Lead with playsLast365d (recency) + distinctArtists (roster breadth); velocity = playsLast365d/playCount. honest_gap when the label has no DJ plays."},"provenance":{"type":"array","items":{}},"generated_at":{"type":"string"}},"required":["contract_version","grain","slug","display","id","cluster_id","resolved_via","identity","binds","presence","over_merge","behavioral","travels","dj_momentum","provenance","generated_at"]},"FestivalDossierContract":{"type":"object","properties":{"contract_version":{"type":"string"},"grain":{"type":"string","enum":["festival"]},"slug":{"type":"string"},"display":{"type":"string"},"id":{"type":["string","null"]},"identity":{},"lineup":{},"provenance":{"type":"array","items":{}},"generated_at":{"type":"string"}},"required":["contract_version","grain","slug","display","id","provenance","generated_at"]},"BandcampFeedContract":{"type":"object","properties":{"key":{"type":"object","properties":{"kind":{"type":"string"},"input":{"type":"string"},"resolved_cluster_id_hex":{"type":["string","null"]},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"]}},"required":["kind","input","resolved_cluster_id_hex","resolved_discogs_artist_id","resolved_mbid"]},"sources":{"type":"object","additionalProperties":{}},"_meta":{"type":"object","properties":{"k_anon_floor":{"type":"number"},"note":{"type":"string"},"granted_sources":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["k_anon_floor","note","granted_sources","generated_at"]}},"required":["key","sources","_meta"]},"BandcampBulkPage":{"type":"object","properties":{"source":{"type":"string"},"rows":{"type":"array","items":{"type":"object","additionalProperties":{}}},"next_cursor":{"type":["string","null"],"description":"An OPAQUE keyset-pagination cursor. To page the full corpus of one Bandcamp source, take the next_cursor from a page and pass it back as ?cursor=<value> on the next request; null means you have reached the last page. WHY IT MATTERS: keyset pagination over a cluster_id-PK source is stable under inserts (unlike offset paging) and is the only way to walk the whole bulk feed. GOTCHA: it is OPAQUE — pass it back verbatim, NEVER construct, decode, or mutate it; a hand-built or stale cursor yields invalid_source_or_cursor (400). It is always a string.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"_meta":{"type":"object","properties":{"k_anon_floor":{"type":"number"},"note":{"type":"string"},"generated_at":{"type":"string"}},"required":["k_anon_floor","note","generated_at"]}},"required":["source","rows","next_cursor","_meta"]},"BandcampReleaseResponse":{"anyOf":[{"type":"object","properties":{"object":{"type":"string","enum":["bandcamp.release"]},"present":{"type":"boolean","enum":[true]},"release":{"$ref":"#/components/schemas/BandcampRelease"}},"required":["object","present","release"]},{"type":"object","properties":{"object":{"type":"string","enum":["bandcamp.release"]},"present":{"type":"boolean","enum":[false]},"note":{"type":"string"}},"required":["object","present","note"]},{"type":"object","properties":{"object":{"type":"string","enum":["bandcamp.release_list"]},"cluster_id":{"type":"string","description":"crate's CANONICAL artist identity — a pe-norm-v1 hex string. The SAME artist across Discogs, MusicBrainz and Bandcamp collapses to ONE cluster_id, so this is the key you store and the key you address every artist surface off of (/artist/{key} takes a 64-hex cluster_id directly). WHY IT MATTERS: it is crate's prime IP — the non-Discogs long-tail join key; discogs_artist_id / mbid are mere leaf coordinates onto it. GOTCHA: it is an OPAQUE string — pass it through verbatim, NEVER numericize, parse, or compare it as a number. null is an HONEST GAP (the name/link couldn't be resolved to a cluster), NOT an error — you still get HTTP 200. A 64-hex cluster_id always resolves at OBSERVED tier (resolved_via:'cluster'), never re-anchored onto a same-name Discogs row.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"count":{"type":"integer"},"releases":{"type":"array","items":{"$ref":"#/components/schemas/BandcampReleaseSummary"}}},"required":["object","cluster_id","count","releases"]}]},"ApiRootIndex":{"type":"object","properties":{"object":{"type":"string","enum":["api_index"]},"version":{"type":"string","enum":["v1"]},"cold_start":{"type":"object","properties":{"problem":{"type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"step":{"type":"number"},"call":{"type":"string"},"gives":{"type":"string"}},"required":["step","call","gives"]}}},"required":["problem","steps"]},"recipes":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"call":{"type":"string"},"gives":{"type":"string"}},"required":["call","gives"]}}},"required":["task","steps"]}},"resources":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"auth":{"type":"string","enum":["public","sync"]},"eli5":{"type":"string"},"description":{"type":"string"},"example":{"type":"string"},"how_to_get_the_key":{"type":["string","null"]}},"required":["name","url","auth","eli5","description","example","how_to_get_the_key"]}},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"http_status":{"type":"integer"},"when":{"type":"string"},"fix":{"type":"string"}},"required":["code","http_status","when","fix"]}},"links":{"type":"object","properties":{"openapi":{"type":"string"},"docs":{"type":"string"}},"required":["openapi","docs"]},"types":{"type":"object","properties":{"openapi":{"type":"string"},"generate":{"type":"string"}},"required":["openapi","generate"]}},"required":["object","version","cold_start","recipes","resources","errors","links","types"]},"SignupAccepted":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["ok","message"]},"SignupRequest":{"type":"object","properties":{"email":{"type":"string","maxLength":255,"format":"email"}},"required":["email"]},"IdentityResolution":{"type":"object","properties":{"cluster_id":{"type":["string","null"],"description":"crate's CANONICAL artist identity — a pe-norm-v1 hex string. The SAME artist across Discogs, MusicBrainz and Bandcamp collapses to ONE cluster_id, so this is the key you store and the key you address every artist surface off of (/artist/{key} takes a 64-hex cluster_id directly). WHY IT MATTERS: it is crate's prime IP — the non-Discogs long-tail join key; discogs_artist_id / mbid are mere leaf coordinates onto it. GOTCHA: it is an OPAQUE string — pass it through verbatim, NEVER numericize, parse, or compare it as a number. null is an HONEST GAP (the name/link couldn't be resolved to a cluster), NOT an error — you still get HTTP 200. A 64-hex cluster_id always resolves at OBSERVED tier (resolved_via:'cluster'), never re-anchored onto a same-name Discogs row.","example":"a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6a3f9c1e84b2d70f6"},"slug":{"type":["string","null"]},"display":{"type":["string","null"]},"locators":{"type":"object","properties":{"discogs":{"type":["number","null"]},"mbid":{"type":["string","null"]},"bandcamp":{"type":"array","items":{"type":"string"}},"soundcloud":{"type":"array","items":{"type":"string"}},"instagram":{"type":"array","items":{"type":"string"}},"spotify":{"type":"array","items":{"type":"string"}},"youtube":{"type":"array","items":{"type":"string"}},"website":{"type":"array","items":{"type":"string"}}},"required":["discogs","mbid","bandcamp","soundcloud","instagram","spotify","youtube","website"]},"resolved_via":{"type":["string","null"],"enum":["discogs","cluster"],"description":"The binding TIER — how trustworthy the identity match is. 'discogs' = canonical, Discogs-bound (verified). 'cluster' = OBSERVED/UNVERIFIED identity inferred from the seen booking graph with no Discogs bind. null = the lookup did not resolve at all (honest gap). WHY IT MATTERS: it is a trust signal you must respect — a 'cluster' result is crate showing you what it can SEE in the booking graph, not what it has verified. GOTCHA: surface a 'cluster' result as flagged/unverified, NEVER as canonical truth; do not silently merge a 'cluster' artist with a verified one. A bare 64-hex key always comes back 'cluster' by design (it skips the cc0_artists lookup so a hex address never re-anchors onto a same-name Discogs row).","example":"discogs"},"resolved_from":{"type":"string","enum":["url","name","locator"],"description":"HOW you addressed the artist on this /resolve call — which input path matched. 'url' = you pasted an artist link (Discogs/MusicBrainz parsed to a clean id, or Bandcamp/SoundCloud/Instagram/etc reverse-matched against the indexed seen.artist_link_index). 'name' = exact case-insensitive ?q= name match. 'locator' = you passed a structured id (?cluster=, ?discogs=, ?mbid=). WHY IT MATTERS: it tells you which branch of the front door produced the answer, so you know how strong the match is (a name match is fuzzier than a locator). GOTCHA: distinct from resolved_via — resolved_from is the INPUT route ('how you asked'), resolved_via is the OUTPUT binding tier ('how good the answer is'). Pair it with `matched_on` (which surface matched) and `note` (why a recognized link didn't resolve).","example":"url"},"matched_on":{"type":"string"},"note":{"type":"string"},"_links":{"type":"object","description":"Onward pointers — present whenever an identity resolved (cluster-addressed when a cluster_id bound; slug-addressed when the identity matched without a verified cluster bind). Follow `artist` for the full dossier.","properties":{"artist":{"type":"string","description":"GET this for the full artist dossier (accepts 64-hex cluster_id or slug)."},"artist_dossier_by_slug":{"type":"string"}}},"candidates":{"type":"array","description":"Disambiguation aid, emitted ONLY when a ?q= resolve ends clusterless (unresolved, or identity matched without a cluster bind): up to 5 OBSERVED-tier prefix matches from the booking graph. tier:'cluster' = unverified — surface as flagged, never as canonical truth. Absent otherwise. Suppressed when members[] is present (a decomposed credit already answers the question).","items":{"type":"object","properties":{"cluster_id":{"type":"string","description":"The candidate 64-hex cluster_id (observed tier)."},"name":{"type":"string"},"tier":{"type":"string","enum":["cluster"]},"distinct_events":{"type":"integer","description":"Booking-graph support (k-anon floor 2) — a rough strength signal."}}}},"members":{"type":"array","description":"Compound-credit decomposition (matched_on:\"credit_split\"), emitted ONLY when a ?q= input reads as a collaboration credit (\"A, B\", \"A & B\", \"A feat. B\", \"NAME (A, B)\"), the WHOLE string resolved on no real tier, and at least one member name individually resolved to a REAL (non-minted) cluster. cluster_id on the envelope stays null — crate never guesses which member \"is\" the credit; query a member for its full resolution. Absent otherwise. A whole string that resolves as one act (e.g. \"Simon & Garfunkel\") is never split.","items":{"type":"object","properties":{"cluster_id":{"type":"string","description":"The member's canonical 64-hex cluster_id (real tier, never minted)."},"name":{"type":"string","description":"The member name as resolved (display form)."},"matched_on":{"type":"string","enum":["name"]},"_links":{"type":"object","properties":{"artist":{"type":"string","description":"GET this for the member’s full dossier."}}}}}}},"required":["cluster_id","slug","display","locators","resolved_via","resolved_from"]},"TastemakersResponse":{"type":"object","properties":{"leaderboard":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"number"},"name":{"type":"string"},"discogsArtistId":{"type":["number","null"]},"slug":{"type":["string","null"]},"ownTier":{"type":"string","enum":["breakout","rising","climbing","steady"]},"degree":{"type":"number"},"emergingNeighbours":{"type":"number"},"pctEmerging":{"type":"number"},"brokerageScore":{"type":["number","null"]},"egoDensity":{"type":["number","null"]},"undergroundToBookingDays":{"type":["number","null"]},"pressToAirplayDays":{"type":["number","null"]},"earliestSupportedAt":{"type":["string","null"]},"demandCoverage":{"type":["number","null"]},"ownerReach":{"type":["number","null"]},"alsoOwnDegree":{"type":["number","null"]}},"required":["rank","name","discogsArtistId","slug","ownTier","degree","emergingNeighbours","pctEmerging","brokerageScore","egoDensity","undergroundToBookingDays","pressToAirplayDays","earliestSupportedAt","demandCoverage","ownerReach","alsoOwnDegree"]}},"onesToWatch":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"number"},"name":{"type":"string"},"discogsArtistId":{"type":["number","null"]},"slug":{"type":["string","null"]},"emergenceTier":{"type":"string","enum":["breakout","rising","climbing","steady"]},"momentumTier":{"type":"string","enum":["breakout","rising","steady","cooling","dormant"]},"emergenceScore":{"type":"number"},"profile":{"type":"string","enum":["network-velocity","festival-pedigree"]},"cultureForwardFestivals":{"type":"number"},"fiveFactorScore":{"type":["number","null"]},"degree":{"type":"number"},"linkable":{"type":"boolean"},"coFestivalCount":{"type":["number","null"]},"coCountryCount":{"type":["number","null"]},"undergroundToBookingDays":{"type":["number","null"]},"pressToAirplayDays":{"type":["number","null"]},"earliestSupportedAt":{"type":["string","null"]},"demandCoverage":{"type":["number","null"]},"ownerReach":{"type":["number","null"]},"alsoOwnDegree":{"type":["number","null"]},"brokerageScore":{"type":["number","null"]},"corroborationCount":{"type":["number","null"]},"corroboratingAxes":{"type":"array","items":{"type":"string"}}},"required":["rank","name","discogsArtistId","slug","emergenceTier","momentumTier","emergenceScore","profile","cultureForwardFestivals","fiveFactorScore","degree","linkable","coFestivalCount","coCountryCount","undergroundToBookingDays","pressToAirplayDays","earliestSupportedAt","demandCoverage","ownerReach","alsoOwnDegree","brokerageScore","corroborationCount","corroboratingAxes"]}},"earlySpinners":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"number"},"djName":{"type":"string"},"totalMastersPlayed":{"type":"number"},"mastersPlayedFirst":{"type":"number"},"mastersPlayedWithin7d":{"type":"number"},"cuttingEdgeScore":{"type":"number"},"firstPlayAt":{"type":["string","null"]},"latestPlayAt":{"type":["string","null"]}},"required":["rank","djName","totalMastersPlayed","mastersPlayedFirst","mastersPlayedWithin7d","cuttingEdgeScore","firstPlayAt","latestPlayAt"]}},"snapshotDate":{"type":["string","null"]},"state":{"type":"string","enum":["present","empty","degraded"]},"stale":{"type":"boolean"}},"required":["leaderboard","onesToWatch","earlySpinners","snapshotDate","state","stale"]},"OnesToWatchResponse":{"type":"object","properties":{"onesToWatch":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"number"},"name":{"type":"string"},"discogsArtistId":{"type":["number","null"]},"slug":{"type":["string","null"]},"emergenceTier":{"type":"string","enum":["breakout","rising","climbing","steady"]},"momentumTier":{"type":"string","enum":["breakout","rising","steady","cooling","dormant"]},"emergenceScore":{"type":"number"},"profile":{"type":"string","enum":["network-velocity","festival-pedigree"]},"cultureForwardFestivals":{"type":"number"},"fiveFactorScore":{"type":["number","null"]},"degree":{"type":"number"},"linkable":{"type":"boolean"},"coFestivalCount":{"type":["number","null"]},"coCountryCount":{"type":["number","null"]},"undergroundToBookingDays":{"type":["number","null"]},"pressToAirplayDays":{"type":["number","null"]},"earliestSupportedAt":{"type":["string","null"]},"demandCoverage":{"type":["number","null"]},"ownerReach":{"type":["number","null"]},"alsoOwnDegree":{"type":["number","null"]},"brokerageScore":{"type":["number","null"]},"corroborationCount":{"type":["number","null"]},"corroboratingAxes":{"type":"array","items":{"type":"string"}}},"required":["rank","name","discogsArtistId","slug","emergenceTier","momentumTier","emergenceScore","profile","cultureForwardFestivals","fiveFactorScore","degree","linkable","coFestivalCount","coCountryCount","undergroundToBookingDays","pressToAirplayDays","earliestSupportedAt","demandCoverage","ownerReach","alsoOwnDegree","brokerageScore","corroborationCount","corroboratingAxes"]}},"snapshotDate":{"type":["string","null"]},"state":{"type":"string","enum":["present","empty","degraded"]},"stale":{"type":"boolean"}},"required":["onesToWatch","snapshotDate","state","stale"]},"BreakoutsResponse":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"discogsArtistId":{"type":"number"},"clusterId":{"type":["string","null"]},"resolvedVia":{"type":"string","enum":["discogs","cluster"]},"name":{"type":"string"},"slug":{"type":"string"},"emergenceTier":{"type":"string","enum":["breakout","rising"]},"emergenceScore":{"type":"number"},"pressCount":{"type":"number"},"corroboration":{"type":"string","enum":["corroborated","booking_ahead"]},"location":{"type":["object","null"],"properties":{"city":{"type":["string","null"]},"region":{"type":["string","null"]},"country":{"type":["string","null"]}},"required":["city","region","country"]}},"required":["discogsArtistId","clusterId","resolvedVia","name","slug","emergenceTier","emergenceScore","pressCount","corroboration","location"]}},"state":{"type":"string","enum":["present","empty","degraded"]}},"required":["items","state"]},"WayfindAnswerRequest":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":512}},"required":["question"]},"WayfindInterpretRequest":{"type":"object","properties":{"q":{"type":"string","minLength":1,"maxLength":256}},"required":["q"]}},"parameters":{}},"paths":{"/api/v2/artist/{key}/bandcamp/{item}":{"get":{"summary":"A Bandcamp release, addressed under its artist (tracklist + durations + artwork + economics)","description":"cycle-085 — the full per-release Bandcamp dossier (title, release_date, tags, label, artwork hotlink URLs, tracklist WITH per-track duration_s, and the economics object), addressed cluster-first UNDER the artist. {key} = 64-hex cluster_id (canonical) or slug; {item} = bandcamp_item_id (list them via the artist dossier's bandcamp_releases facet). CLUSTER-FIRST INTEGRITY: the release must belong to the addressed artist — a real release filed under a DIFFERENT artist returns 200 present:false (honest-gap), never another artist's data. artwork carries the fetchable CDN url + delivered width/height (700×700 — the _16 locator's render dims, not source aspect; cycle-086). Honest gap: per-track DIRECT stream URL is not stored (tokenized/expiring + ToS) — track_url is the track PAGE. Keyed (X-API-Key); ONE crate_reader checkout; unresolved → 200 present:false, NOT 404.","tags":["Bandcamp","Dossier"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"},{"schema":{"type":"string","pattern":"^\\d{1,19}$"},"required":true,"name":"item","in":"path"}],"responses":{"200":{"description":"The release dossier (present:true) or an honest-gap (present:false — not found, or not filed under this artist)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"object":{"type":"string","enum":["bandcamp.release"]},"present":{"type":"boolean","enum":[true]},"release":{"$ref":"#/components/schemas/BandcampRelease"}},"required":["object","present","release"]},{"type":"object","properties":{"object":{"type":"string","enum":["bandcamp.release"]},"present":{"type":"boolean","enum":[false]},"note":{"type":"string"}},"required":["object","present","note"]}]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistBandcampRelease"}},"/api/v2/artist/{key}/similar":{"get":{"summary":"Composed adjacency evidence for one artist — movements, not scores","description":"cycle-112 + cycle-113 — one call returning adjacency EVIDENCE grouped per axis, for suggestion/recommendation consumers (the Public Office Listen shape). NO composite similarity score exists anywhere on this surface: each axis carries its substrate's own counts + recency VERBATIM and the consumer ranks; similar[] order is a deterministic presentation convention (axis breadth → most recent evidence → key), never a ranking. EIGHT axes. co_appearance (seen.artist_co_appearance public co-booking edges — the SAME substrate as the dossier's connections.related, so this endpoint gives one-call parity with related[] plus the other axes). radio_co_play (seen.radio_co_play — programmed adjacency within 5 positions in the same radio set, NOT whole-show membership; artists in >1000 sets carry no edges, the HUB_CAP). shared_dj_champions (seen.dj_champion 2-hop: DJs championing the subject who also champion the suggested artist; attribution-bounded per source — kexp 100% DJ names, skylab 0). lineage (mirror.cluster_derivation_edges_v1 — the MusicBrainz derivation graph at CLUSTER grain: samples/remixes/edits/dj-mixes/mashups at recording level, versions/adaptations/arrangements/medleys/quotations at composition level. Endpoints resolve at BILLING grain, so read as graph participation, never authorship — MB credits a remix to the ORIGINAL billed artist; direction 'derived' = the subject's work derives from the suggested artist's, 'source' = the reverse). session_credit (mirror.session_credit_edges_v1 — 5.86M undirected co-credit edges: shared recordings, shared works, and allowlisted direct MusicBrainz artist relations. COUNTS ONLY upstream; edgeWeight is the producer's own total support, NOT a similarity. relationTypes is empty and firstYear/lastYear are null on the current producer refresh). label_mates (mirror.artist_label_tenure_v1 — shared label rosters on the cluster spine, with each label's rosterSize served BECAUSE a large roster is a weak adjacency. ROSTER FAMILIES ONLY: the six '… position at' relations are label EMPLOYMENT and are excluded. Coverage is double-gated and reaches ~0.73% of clusters, so absence is the norm, not a claim. No label name — the identity surface is the artist spine and carries none). influence (mirror.influence_edges_v1 — Wikidata P737 'influenced by', DIRECT edges only; transitive depth-2 chains are excluded. A sparse crowd-curated canonical layer). attention_flow (mirror.attention_flow_edges_v1 — Wikimedia Clickstream navigation, direction FROM→TO and NOT symmetric; carries the upstream n>=10 suppression floor, so a missing edge means below-floor or not Wikipedia-resolvable, never zero navigation. Attention flows to band members and side projects first). CROSS-AXIS: cluster identity is keyed on the NORMALISED ARTIST NAME (pe-norm-v1), so an alias or legal name is a DIFFERENT cluster and can appear as its own adjacency; session_credit and lineage are CREDIT graphs, so engineers and producers appear wherever MusicBrainz credits them; the seen-sourced axes carry an ARRIVAL LATENCY of up to ~7 days for a newly observed artist (weekly Sunday cluster fill) even though existing rows refresh nightly. Per-axis cap 10; artists appearing on multiple axes are DEDUPED with all their axes[] listed. Fail-soft per axis: a failed leg degrades ONLY its axisState (response stays 200); state:'degraded' means ≥1 axis could not be read. {key} = 64-hex cluster_id (canonical) or a name slug; the endpoint is CLUSTER-KEYED — a subject resolving without a cluster anchor returns an honest empty with a /resolve pointer, never a guess. Member names hydrate from mirror.cluster_identity_v1; a member outside that surface keeps name:null. Keyed (X-API-Key); ONE crate_reader checkout.","tags":["Dossier"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Adjacency evidence grouped per axis (deduped artists, axes[] each), or an honest empty when the subject has no cluster anchor","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["artist.similar"]},"subject":{"type":["object","null"],"properties":{"clusterId":{"type":["string","null"]},"name":{"type":["string","null"]},"discogsArtistId":{"type":["number","null"]}},"required":["clusterId","name"]},"state":{"type":"string","enum":["ok","degraded"]},"axisStates":{"type":"object","properties":{"co_appearance":{"type":"string","enum":["ok","empty","degraded"]},"radio_co_play":{"type":"string","enum":["ok","empty","degraded"]},"shared_dj_champions":{"type":"string","enum":["ok","empty","degraded"]},"lineage":{"type":"string","enum":["ok","empty","degraded"]},"session_credit":{"type":"string","enum":["ok","empty","degraded"]},"label_mates":{"type":"string","enum":["ok","empty","degraded"]},"influence":{"type":"string","enum":["ok","empty","degraded"]},"attention_flow":{"type":"string","enum":["ok","empty","degraded"]}},"required":["co_appearance","radio_co_play","shared_dj_champions","lineage","session_credit","label_mates","influence","attention_flow"]},"similar":{"type":"array","items":{"type":"object","properties":{"clusterId":{"type":["string","null"]},"name":{"type":["string","null"]},"axes":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"axis":{"type":"string","enum":["co_appearance"]},"coEventCount":{"type":"number"},"coVenueCount":{"type":"number"},"lastCoAt":{"type":["string","null"]}},"required":["axis","coEventCount","coVenueCount","lastCoAt"]},{"type":"object","properties":{"axis":{"type":"string","enum":["radio_co_play"]},"coPlayCount":{"type":"number"},"distinctStations":{"type":"number"},"firstCoPlayedAt":{"type":["string","null"]},"lastCoPlayedAt":{"type":["string","null"]}},"required":["axis","coPlayCount","distinctStations","firstCoPlayedAt","lastCoPlayedAt"]},{"type":"object","properties":{"axis":{"type":"string","enum":["shared_dj_champions"]},"sharedChampionCount":{"type":"number"},"champions":{"type":"array","items":{"type":"object","properties":{"djClusterId":{"type":"string"},"playCount":{"type":"number"},"latestPlayedAt":{"type":["string","null"]}},"required":["djClusterId","playCount","latestPlayedAt"]}}},"required":["axis","sharedChampionCount","champions"]},{"type":"object","properties":{"axis":{"type":"string","enum":["lineage"]},"edgeCount":{"type":"number"},"entries":{"type":"array","items":{"type":"object","properties":{"relType":{"type":"string"},"direction":{"type":"string","enum":["derived","source"]},"evidenceGrain":{"type":"string"}},"required":["relType","direction","evidenceGrain"]}}},"required":["axis","edgeCount","entries"]},{"type":"object","properties":{"axis":{"type":"string","enum":["session_credit"]},"edgeWeight":{"type":"number"},"sharedRecordingCount":{"type":"number"},"sharedWorkCount":{"type":"number"},"directRelationCount":{"type":"number"},"evidenceSources":{"type":"array","items":{"type":"string"}},"relationTypes":{"type":"array","items":{"type":"string"}},"firstYear":{"type":["number","null"]},"lastYear":{"type":["number","null"]}},"required":["axis","edgeWeight","sharedRecordingCount","sharedWorkCount","directRelationCount","evidenceSources","relationTypes","firstYear","lastYear"]},{"type":"object","properties":{"axis":{"type":"string","enum":["label_mates"]},"sharedLabelCount":{"type":"number"},"labels":{"type":"array","items":{"type":"object","properties":{"labelClusterId":{"type":"string"},"rosterSize":{"type":"number"},"relType":{"type":"string"},"firstYear":{"type":["number","null"]},"lastYear":{"type":["number","null"]},"isCurrent":{"type":"boolean"}},"required":["labelClusterId","rosterSize","relType","firstYear","lastYear","isCurrent"]}}},"required":["axis","sharedLabelCount","labels"]},{"type":"object","properties":{"axis":{"type":"string","enum":["influence"]},"direction":{"type":"string","enum":["influenced_by","influenced"]},"edgeCount":{"type":"number"}},"required":["axis","direction","edgeCount"]},{"type":"object","properties":{"axis":{"type":"string","enum":["attention_flow"]},"direction":{"type":"string","enum":["to","from"]},"totalWeight":{"type":"number"},"months":{"type":"number"},"lastMonth":{"type":["string","null"]},"langs":{"type":"array","items":{"type":"string"}}},"required":["axis","direction","totalWeight","months","lastMonth","langs"]}]}},"_links":{"type":"object","properties":{"artist":{"type":["string","null"]}},"required":["artist"]}},"required":["clusterId","name","axes","_links"]}},"notes":{"type":"array","items":{"type":"string"}}},"required":["object","subject","state","axisStates","similar","notes"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistSimilar"}},"/api/v2/artist/{key}/gigs":{"get":{"summary":"One artist's upcoming events, each with the full bill","description":"cycle-114 — upcoming events for one artist, with the support-act lane. A SUB-ROUTE rather than a dossier facet on purpose: at 0.07% coverage, folding it into the default-rich dossier would tax every /artist call for something empty 99.93% of the time (the same opt-in precedent as /similar and /bandcamp). {key} = 64-hex cluster_id (canonical) or a name slug; CLUSTER-KEYED, so a subject resolving without a cluster anchor returns an honest empty with a /resolve pointer, never a guess. An artist with no forward-dated event is 200 with gigs:[] — never 404, and never a claim that the artist is not touring. Cluster identity is keyed on the NORMALISED ARTIST NAME (pe-norm-v1), so an alias or legal name is a DIFFERENT cluster and may carry its own gigs. Keyed (X-API-Key); ONE crate_reader checkout; every hop index-served (measured 69.7 ms). FRESHNESS IS NOW READ LIVE (sprint-bug-148): `surface` carries the producer's own refreshed_at, event_count and orphan_artist_rate from mirror.mb_events_meta, and the import-cadence note is DERIVED from that stamp rather than frozen into copy. v2 2.53.0 shipped a probe observation hardcoded at ship time because that twin was not granted to crate_reader; a live pg_class.relacl diff found the gap on 2026-08-19 and mirror granted it the same day. The read is deliberately NOT memoized — it is a genuine singleton measured at 1.980 ms, unlike the journalism freshness read which needed a TTL cache at 315 ms. If the read fails the note is dropped rather than invented. COVERAGE IS NARROW AND SAID SO ON THE WIRE: 1,772 artist clusters of ~2,564,895 (0.07%) carry a forward-dated event, so an empty result means this surface has no event for the subject — NOT that nobody is playing. The source is mirror.mb_events (MusicBrainz volunteer editing): strong for touring and festival acts with active editors, absent for the long tail, and NOT a market feed — there is no on-sale, price, or ticketing state, and the import cadence (imported_at 2026-08-01 when probed) is right for 'upcoming' and wrong for 'on sale now'. begin_date is a MusicBrainz PARTIAL date held as text, so only full YYYY-MM-DD rows are served: 1,092 year-only and 584 year-month events are EXCLUDED and 18 malformed rows (which sort AFTER valid 2026 dates lexicographically) are filtered by a format guard. lineup[] is the reason this lane exists — mb_event_artists carries 46,331 `support act` and 713 `supporting DJ` links, making this the ONLY surface in the fleet that answers 'who is opening' (seen's festival_lineup_entries is headliners-only with zero forward-dated rows). link_type is served VERBATIM and NO billing order is implied, because MusicBrainz does not record one; lineup order is link_type, then name, then mbid. roles[] carries EVERY role the addressed cluster holds on a bill (MusicBrainz can credit one cluster under several link_types on one event, and a cluster can bind several mbids), and it is sourced from the same join that selected the event so it is never cut by the lineup bound; it is always empty on the city direction, which has no subject. A lineup longer than 30 sets lineupTruncated instead of silently dropping members. event_type is null on 17 forward-dated rows; venue is null when the event carries no place link (~4.3%). MusicBrainz can link an event to SEVERAL venues (404 events carry 2, 83 carry 3, up to 22 — genuinely multi-site festivals): venue carries one of them deterministically and venueCount says how many exist, so a multi-venue event is disclosed rather than presented as if it had one venue.","tags":["Dossier"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"The artist's forward-dated events with their bills, or an honest empty","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["artist.gigs"]},"subject":{"type":["object","null"],"properties":{"clusterId":{"type":["string","null"]},"name":{"type":["string","null"]},"discogsArtistId":{"type":["number","null"]}},"required":["clusterId","name"]},"state":{"type":"string","enum":["ok"]},"gigs":{"type":"array","items":{"type":"object","properties":{"eventMbid":{"type":"string"},"name":{"type":["string","null"]},"date":{"type":"string"},"time":{"type":["string","null"]},"eventType":{"type":["string","null"]},"venue":{"type":["object","null"],"properties":{"name":{"type":["string","null"]},"city":{"type":["string","null"]},"countryCode":{"type":["string","null"]}},"required":["name","city","countryCode"]},"venueCount":{"type":"number"},"roles":{"type":"array","items":{"type":"string"}},"lineup":{"type":"array","items":{"type":"object","properties":{"clusterId":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string"}},"required":["clusterId","name","role"]}},"lineupTruncated":{"type":"boolean"}},"required":["eventMbid","name","date","time","eventType","venue","venueCount","roles","lineup","lineupTruncated"]}},"surface":{"type":["object","null"],"properties":{"refreshedAt":{"type":["string","null"]},"eventCount":{"type":["number","null"]},"orphanArtistRate":{"type":["number","null"]}},"required":["refreshedAt","eventCount","orphanArtistRate"]},"notes":{"type":"array","items":{"type":"string"}}},"required":["object","subject","state","gigs","surface","notes"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/gigs":{"get":{"summary":"Who's playing in a city, in a date window — with each full bill","description":"cycle-114 — the discovery direction of the gig lane, same event shape as /api/v2/artist/{key}/gigs. ?city= is EXACT FREE TEXT from mb_places.area_name: MusicBrainz has no controlled place vocabulary and there is no area_mbid on the place row, so 'Köln' ≠ 'Cologne'. That is the surface's real usability problem and it is answered at the point of failure — a city matching nothing returns 200 with availableCities[] (the values that currently carry forward-dated events, most first, capped at 50), so one wrong guess teaches the right value in a single call. ?from= and ?to= are YYYY-MM-DD and `to` is EXCLUSIVE; omit both for the next 90 days. A malformed window, or `to` not after `from`, is 400 invalid_gig_window with a copy-pasteable example; a missing city is 400 invalid_city. Results are capped at 100 events per call and there is no pagination — the busiest city currently carries 102 forward-dated events fleet-wide, so the cap is not yet a constraint. Keyed (X-API-Key); ONE crate_reader checkout. FRESHNESS IS NOW READ LIVE (sprint-bug-148): `surface` carries the producer's own refreshed_at, event_count and orphan_artist_rate from mirror.mb_events_meta, and the import-cadence note is DERIVED from that stamp rather than frozen into copy. v2 2.53.0 shipped a probe observation hardcoded at ship time because that twin was not granted to crate_reader; a live pg_class.relacl diff found the gap on 2026-08-19 and mirror granted it the same day. The read is deliberately NOT memoized — it is a genuine singleton measured at 1.980 ms, unlike the journalism freshness read which needed a TTL cache at 315 ms. If the read fails the note is dropped rather than invented. COVERAGE IS NARROW AND SAID SO ON THE WIRE: 1,772 artist clusters of ~2,564,895 (0.07%) carry a forward-dated event, so an empty result means this surface has no event for the subject — NOT that nobody is playing. The source is mirror.mb_events (MusicBrainz volunteer editing): strong for touring and festival acts with active editors, absent for the long tail, and NOT a market feed — there is no on-sale, price, or ticketing state, and the import cadence (imported_at 2026-08-01 when probed) is right for 'upcoming' and wrong for 'on sale now'. begin_date is a MusicBrainz PARTIAL date held as text, so only full YYYY-MM-DD rows are served: 1,092 year-only and 584 year-month events are EXCLUDED and 18 malformed rows (which sort AFTER valid 2026 dates lexicographically) are filtered by a format guard. lineup[] is the reason this lane exists — mb_event_artists carries 46,331 `support act` and 713 `supporting DJ` links, making this the ONLY surface in the fleet that answers 'who is opening' (seen's festival_lineup_entries is headliners-only with zero forward-dated rows). link_type is served VERBATIM and NO billing order is implied, because MusicBrainz does not record one; lineup order is link_type, then name, then mbid. roles[] carries EVERY role the addressed cluster holds on a bill (MusicBrainz can credit one cluster under several link_types on one event, and a cluster can bind several mbids), and it is sourced from the same join that selected the event so it is never cut by the lineup bound; it is always empty on the city direction, which has no subject. A lineup longer than 30 sets lineupTruncated instead of silently dropping members. event_type is null on 17 forward-dated rows; venue is null when the event carries no place link (~4.3%). MusicBrainz can link an event to SEVERAL venues (404 events carry 2, 83 carry 3, up to 22 — genuinely multi-site festivals): venue carries one of them deterministically and venueCount says how many exist, so a multi-venue event is disclosed rather than presented as if it had one venue.","tags":["Discovery"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"city","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Forward-dated events in that city with their bills; when nothing matched, availableCities[] teaches the exact spellings that do","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["gigs.city"]},"query":{"type":"object","properties":{"city":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}},"required":["city","from","to"]},"state":{"type":"string","enum":["ok"]},"gigs":{"type":"array","items":{"type":"object","properties":{"eventMbid":{"type":"string"},"name":{"type":["string","null"]},"date":{"type":"string"},"time":{"type":["string","null"]},"eventType":{"type":["string","null"]},"venue":{"type":["object","null"],"properties":{"name":{"type":["string","null"]},"city":{"type":["string","null"]},"countryCode":{"type":["string","null"]}},"required":["name","city","countryCode"]},"venueCount":{"type":"number"},"roles":{"type":"array","items":{"type":"string"}},"lineup":{"type":"array","items":{"type":"object","properties":{"clusterId":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string"}},"required":["clusterId","name","role"]}},"lineupTruncated":{"type":"boolean"}},"required":["eventMbid","name","date","time","eventType","venue","venueCount","roles","lineup","lineupTruncated"]}},"surface":{"type":["object","null"],"properties":{"refreshedAt":{"type":["string","null"]},"eventCount":{"type":["number","null"]},"orphanArtistRate":{"type":["number","null"]}},"required":["refreshedAt","eventCount","orphanArtistRate"]},"availableCities":{"type":"array","items":{"type":"object","properties":{"city":{"type":"string"},"events":{"type":"number"}},"required":["city","events"]}},"notes":{"type":"array","items":{"type":"string"}}},"required":["object","query","state","gigs","surface","notes"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/preview/artist":{"get":{"summary":"Keyless education teaser — a capped, render-ready subset of the artist dossier","description":"cycle-087 — the education surface behind the hosaka.fm 'ask about any artist' widget: crate's ONLY keyless data route, deliberately. NOT for products or agents — the teaser is a small capped subset (arc ≤ 6 rows, booked_with ≤ 3, no economics, no provenance); build on GET /api/v2/artist/{key} with a key instead. ?q= accepts a name, slug, or 64-hex cluster_id (canonicalized server-side; names resolve exactly, not fuzzy). Unresolved → 200 present:false honest-gap (same cache posture — negative results edge-cache too). Anonymous IP rate limit (sliding window); the limiter FAILS CLOSED here (503) because keyless has no other barrier; anonymous traffic load-sheds FIRST under saturation. CORS: Access-Control-Allow-Origin * (public, identical body for every caller; no Vary).","tags":["Meta"],"security":[],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256},"required":true,"name":"q","in":"query"}],"responses":{"200":{"description":"The teaser (present:true) or an honest-gap (present:false — name did not resolve)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"object":{"type":"string","enum":["artist.preview"]},"present":{"type":"boolean","enum":[true]},"display":{"type":"string"},"cluster_id":{"type":["string","null"]},"resolved_via":{"type":["string","null"],"enum":["discogs","cluster"]},"emergence_tier":{"type":["string","null"]},"arc":{"type":"array","items":{"type":"object","properties":{"year":{"type":"number"},"tier":{"type":"string"},"label":{"type":"string"}},"required":["year","tier","label"]}},"arc_truncated":{"type":"boolean"},"booked_with":{"type":"array","items":{"type":"string"}},"press_count":{"type":"number"},"note":{"type":"string"},"generated_at":{"type":"string"}},"required":["object","present","display","cluster_id","resolved_via","emergence_tier","arc","arc_truncated","booked_with","press_count","note","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["artist.preview"]},"present":{"type":"boolean","enum":[false]},"note":{"type":"string"}},"required":["object","present","note"]}]}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistPreview"}},"/api/v2/artist/{key}/master/{id}":{"get":{"summary":"A master (release-group) dossier, addressed under its artist","description":"cycle-091 — the full per-master dossier (header, every signal section, artwork, provenance — the rich MasterDossierContract the master grain has always produced), addressed cluster-first UNDER the artist. {key} = 64-hex cluster_id (canonical) or slug; {id} = discogs_master_id (list them via the artist dossier's discography facet). CLUSTER-FIRST INTEGRITY: the master must be filed under the addressed artist AND authoritatively bound (not via a homonym name over-merge) — otherwise 200 present:false honest-gap, never another artist's master. When bound via an over-merged name cluster, binding.observed=true flags it OBSERVED/UNVERIFIED (mirrors the discography facet). Keyed (X-API-Key); malformed id → 400; unresolved/mismatch → 200 present:false, NOT 404. NOTE: the Discogs collector count (owner_count) is intentionally absent — retired as an anti-signal in cycle-082, not lost in the cluster cut.","tags":["Dossier"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"},{"schema":{"type":"string","pattern":"^\\d{1,9}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The master dossier (present:true) or an honest-gap (present:false — unknown, or not filed under this artist)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"object":{"type":"string","enum":["master.dossier"]},"present":{"type":"boolean","enum":[true]},"binding":{"type":"object","properties":{"observed":{"type":"boolean"}},"required":["observed"]},"master":{"$ref":"#/components/schemas/MasterDossierContract"}},"required":["object","present","binding","master"]},{"type":"object","properties":{"object":{"type":"string","enum":["master.dossier"]},"present":{"type":"boolean","enum":[false]},"note":{"type":"string"}},"required":["object","present","note"]}]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistMaster"}},"/api/v2/artists":{"get":{"summary":"Browse the artist grain — filter by genre/style/tier, discovery-ranked","description":"cycle-090 — the visual-grid spine: the artist-grain browse/list surface (retires the search-derive workaround). genre + style are EXACT case-sensitive values (see GET /api/v2/facets for the live vocabulary; an unknown value → 200 with an empty items[] + a note, never a 400). tier=breakout|rising|steady filters to artists WITH live emergence data in that tier. sort defaults to 'discovery' (tier-weighted: breakout>rising>steady>untracked, then emergence momentum, then owner reach — the grid surfaces who's RISING, not the legacy giants); sort=reach gives raw owner-count order. Offset pagination, but the discovery-ranked window is CAPPED (offset+limit ≤ 500 — narrow with filters, don't page deep). owner_reach is k-anon floored producer-side (null when suppressed). cluster_id is the onward key: feed a non-null one to GET /api/v2/artist/{key}. Keyed (X-API-Key); ONE crate_reader checkout; read failure → 200 state:'degraded'.","tags":["Discovery"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","maxLength":80},"required":false,"name":"genre","in":"query"},{"schema":{"type":"string","maxLength":80},"required":false,"name":"style","in":"query"},{"schema":{"type":"string","enum":["breakout","rising","steady"]},"required":false,"name":"tier","in":"query"},{"schema":{"type":"string","enum":["discovery","reach"]},"required":false,"name":"sort","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"The discovery grid page (state:'present'), or a fail-soft honest empty (state:'degraded')","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["artist.browse"]},"state":{"type":"string","enum":["present","degraded"]},"returned":{"type":"integer"},"filters_applied":{"type":"object","properties":{"genre":{"type":"string"},"style":{"type":"string"},"tier":{"type":"string"},"sort":{"type":"string","enum":["discovery","reach"]}},"required":["sort"]},"items":{"type":"array","items":{"type":"object","properties":{"display":{"type":"string"},"slug":{"type":"string"},"discogs_artist_id":{"type":"integer"},"cluster_id":{"type":["string","null"]},"primary_genre":{"type":["string","null"]},"primary_styles":{"type":"array","items":{"type":"string"}},"emergence_tier":{"type":["string","null"]},"momentum_tier":{"type":["string","null"]},"owner_reach":{"type":["integer","null"]}},"required":["display","slug","discogs_artist_id","cluster_id","primary_genre","primary_styles","emergence_tier","momentum_tier","owner_reach"]}},"note":{"type":"string"},"_links":{"type":"object","properties":{"next":{"type":"string"}},"required":["next"]}},"required":["object","state","returned","filters_applied","items"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"browseArtists"}},"/api/v2":{"get":{"summary":"API v2 root index — start here (cluster-first cold-start)","description":"cycle-2b — the v2 root index: the cold-start recipe + every v2 product surface with its auth tier + how to get the key + the runtime error catalogue (code → when → fix). PURE (no DB), edge-cacheable; the human-facing sibling of /api/v2/openapi.json. v2 is the cluster-first stable major: cluster_id is the prime key, sources/releases attach to it.","tags":["Meta"],"security":[],"responses":{"200":{"description":"API v2 root index","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["api_index"]},"version":{"type":"string","enum":["v2"]},"cold_start":{"type":"object","properties":{"problem":{"type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"step":{"type":"number"},"call":{"type":"string"},"gives":{"type":"string"}},"required":["step","call","gives"]}}},"required":["problem","steps"]},"recipes":{"type":"array","items":{"type":"object","properties":{"task":{"type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"call":{"type":"string"},"gives":{"type":"string"}},"required":["call","gives"]}}},"required":["task","steps"]}},"resources":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"auth":{"type":"string","enum":["public","sync"]},"eli5":{"type":"string"},"description":{"type":"string"},"example":{"type":"string"},"how_to_get_the_key":{"type":["string","null"]}},"required":["name","url","auth","eli5","description","example","how_to_get_the_key"]}},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"http_status":{"type":"integer"},"when":{"type":"string"},"fix":{"type":"string"}},"required":["code","http_status","when","fix"]}},"links":{"type":"object","properties":{"openapi":{"type":"string"},"docs":{"type":"string"}},"required":["openapi","docs"]},"types":{"type":"object","properties":{"openapi":{"type":"string"},"generate":{"type":"string"}},"required":["openapi","generate"]}},"required":["object","version","cold_start","recipes","resources","errors","links","types"]}}}}},"operationId":"getApiIndex"}},"/api/v2/openapi.json":{"get":{"summary":"OpenAPI 3.1 specification (v2 — this document)","description":"Public endpoint, no auth required. The cluster-first v2 surface (see info.version for the current 2.x); documents only /api/v2 routes.","tags":["Spec"],"security":[],"responses":{"200":{"description":"OpenAPI 3.1 spec (v2)","content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}}},"operationId":"getOpenApiSpec"}},"/api/v2/artist/{key}":{"get":{"summary":"Cluster-first canonical artist dossier (cluster_id hex OR slug)","description":"cycle-2b — the v2 cluster-first headline resource: the same exhaustive artist dossier as v1, addressed by the canonical cluster_id (64-hex, prime key) OR a human slug, plus the v2 `?fields=` sparse-fieldset. Default = the full dossier in ONE round-trip; `?fields=` opts OUT to trim to the named top-level facets (unknown field → 400 invalid_fields with the valid set + a copy-pasteable example). A 64-hex key resolves identity DIRECTLY from the cluster_id (OBSERVED tier, never re-anchors onto a same-name Discogs row). discogs:/mbid: locators are not a canonical address (→ 400 here) → resolve them to a cluster_id first via GET /api/v2/resolve (the response `next` field is the ready-to-call URL), then call /api/v2/artist/{cluster_id}. Keyed (X-API-Key); unresolved → 200 identity:null (honest-gap), not 404.","tags":["Dossier","Identity"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"},{"schema":{"type":"string","description":"Comma-separated top-level facets to KEEP (opt-out trim). Omit for the full dossier (one round-trip). An unknown field → 400 invalid_fields with the valid set + an example."},"required":false,"name":"fields","in":"query"}],"responses":{"200":{"description":"Artist dossier contract (default-rich; trimmed when ?fields= is supplied)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtistDossierContract"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistByKey"}},"/api/v2/resolve":{"get":{"summary":"Resolve any identifier (name · pasted link · id) → canonical cluster_id","description":"The cluster-first front door: converts ANY identifier into the canonical cluster_id + slug + the full locator set. Query ONE of ?url= | ?q= | ?cluster=<64-hex> | ?discogs=<int> | ?mbid=<uuid> | ?isrc=<code> | ?artist=<name>&track=<title>. The `isrc=` and `artist=&track=` arms lift distributed-catalogue tracks off the ?q= name floor — both bind via the recording→artist spine to the performing-artist cluster (a code/track outranks a bare ?q= name when both are sent). The onward _links point into /api/v2. Keyed (X-API-Key); unresolved → 200 nulls (honest-gap), not 404.","tags":["Identity"],"parameters":[{"schema":{"type":"string","description":"Any pasted artist link (discogs/musicbrainz → clean id; bandcamp/soundcloud/instagram/website/spotify/youtube → reverse-match; unrecognized hosts fall back to the handle as a name with an explanatory note). Send the URL verbatim — crate never fetches it."},"required":false,"name":"url","in":"query"},{"schema":{"type":"string","description":"Artist name, exact case-insensitive match (e.g. q=Four Tet)."},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","description":"The 64-hex lowercase pe-norm-v1 cluster_id — the fleet identity spine key."},"required":false,"name":"cluster","in":"query"},{"schema":{"type":"string","description":"BARE numeric Discogs ARTIST id, digits only (e.g. discogs=2783124). NOT a release id, NOT a prefixed form — 'artist:123' / 'release:123' do not resolve (they return nulls). Release ids have no resolve path; drop release-only locators to ?q=<artist name>."},"required":false,"name":"discogs","in":"query"},{"schema":{"type":"string","description":"MusicBrainz artist id (UUID)."},"required":false,"name":"mbid","in":"query"},{"schema":{"type":"string","description":"A 12-character ISRC (recording code; hyphens/spaces ignored, e.g. isrc=US-AKS-05-00539). Binds isrc → MB recording → the recording's PRIMARY artist cluster. The precise lever for distributed-catalogue tracks (SoundCloud/Spotify/Apple/Bandcamp exports carry ISRCs). Coverage gap → 200 nulls with a note, never an error."},"required":false,"name":"isrc","in":"query"},{"schema":{"type":"string","description":"Performing-artist name — MUST be paired with ?track= (alone it 400s). With track=, resolves the dominant primary-artist cluster of MB recordings matching BOTH (normalized), disambiguating a common name by a known recording. MusicBrainz authorship coverage is uneven (thin for electronic/underground) → 200 nulls with a note when unmatched."},"required":false,"name":"artist","in":"query"},{"schema":{"type":"string","description":"Recording/track title — pair with ?artist= (see artist=). Matched case/punctuation-insensitively against MB recording titles."},"required":false,"name":"track","in":"query"}],"responses":{"200":{"description":"Resolved identity (or nulls when unresolved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityResolution"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"resolveIdentity"}},"/api/v2/search":{"get":{"summary":"Faceted search across the catalogue","description":"Faceted master-grain search (lexical; ?nl=true runs the Haiku interpretation pipeline). Keyed (X-API-Key). Read the facets in the response, then re-query with facet filters to refine.","tags":["Search"],"parameters":[{"schema":{"type":"string","maxLength":256},"required":false,"name":"q","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"genre","in":"query"},{"schema":{"type":"string","enum":["and","or"]},"required":false,"name":"genre_mode","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"style","in":"query"},{"schema":{"type":"string","enum":["and","or"]},"required":false,"name":"style_mode","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"format","in":"query"},{"schema":{"type":"string","enum":["and","or"]},"required":false,"name":"format_mode","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"country","in":"query"},{"schema":{"type":"string","enum":["and","or"]},"required":false,"name":"country_mode","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"label","in":"query"},{"schema":{"type":"string","enum":["and","or"]},"required":false,"name":"label_mode","in":"query"},{"schema":{"type":"integer","minimum":1900,"maximum":2100},"required":false,"name":"year_from","in":"query"},{"schema":{"type":"integer","minimum":1900,"maximum":2100},"required":false,"name":"year_to","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":10000,"default":0},"required":false,"name":"offset","in":"query"},{"schema":{"anyOf":[{"type":"string","pattern":"^[01]{3}$"},{"type":"array","items":{"type":"string","pattern":"^[01]{3}$"},"minItems":1,"maxItems":8}]},"required":false,"name":"cube_quadrant","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":1000000},"required":false,"name":"dj_count_min","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"exclude_artist","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"exclude_label","in":"query"}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"searchCatalogue"}},"/api/v2/tracks":{"get":{"summary":"Track-title search — who has a track called X","description":"Title-first track discovery: a track title → the artist clusters that have a recording of it, ranked by how many releases carry that artist's rendition (a ubiquity proxy), each with onward /artist links. The title-first complement to `GET /api/v2/resolve?artist=&track=` (which needs the artist and returns one identity). CORPUS (`?source=`): default `mb` = MusicBrainz-linked Discogs tracklists (catalogued/physical-release music); `bandcamp` = the Bandcamp track corpus (reaches the underground/self-released tail — a Bandcamp-only cut returns 0 under `mb`); `all` = both merged per cluster (each result carries a `sources` array). MATCH MODES: default = EXACT (case/punctuation-insensitive whole title); `?fuzzy=true` = case-insensitive SUBSTRING (≥3 chars, so \"angel ech\" finds \"Angel Echoes\"). The response echoes which ran in `mode`. HONEST LIMITS: (1) fuzzy is substring, not typo/phonetic; (2) common titles fan out (e.g. \"Intro\") — the ranking is then from a bounded candidate set with `truncated:true`, so narrow via `?artist=&track=` on /resolve (fuzzy fans out sooner); (3) `mb` coverage is MB-linked catalogue (uneven — thinner for electronic/underground; use `bandcamp`/`all` there). Keyed (X-API-Key); no match → 200 with `results:[]` (honest gap), never 404.","tags":["Search"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","description":"The track title (required). Default (exact) match is case/punctuation-insensitive whole-title equality. With ?fuzzy=true it is a case-insensitive SUBSTRING match."},"required":true,"name":"title","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"fuzzy=true → case-insensitive SUBSTRING match on the raw title (e.g. title=angel echoes&fuzzy=true matches \"Angel Echoes (Jon Hopkins Remix)\"), gin_trgm-indexed; requires ≥3 characters (shorter is refused as an honest empty). Omitted/false = exact whole-title match. Fuzzy fans out more, so `truncated` fires sooner — narrow via /resolve?artist=&track=."},"required":false,"name":"fuzzy","in":"query"},{"schema":{"type":"string","enum":["mb","bandcamp","all"],"description":"Which track corpus to search. `mb` (default) = MusicBrainz-linked Discogs tracklists (catalogued/physical-release music). `bandcamp` = the Bandcamp track corpus (mirror.bandcamp_release_track_v1) — reaches underground/self-released titles that are NOT on a Discogs tracklist (e.g. a Bandcamp-only cut returns 0 under `mb`). `all` = search both and merge per artist cluster (each result carries a `sources` array; `releases`/`recordings` are summed across corpora). For `all`, `state:degraded` means at least one corpus read failed → results may be partial."},"required":false,"name":"source","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Max artist-cluster rows (1–50, default 20)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Ranked artist clusters with a track of that title (or an empty honest-gap list)","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["ok","degraded"],"description":"'ok' = the read ran (an empty results is a genuine no-match); 'degraded' = a substrate read failed (timeout/transient) so results is empty-by-failure, NOT a no-match — retry. For source=all, 'degraded' means ≥1 corpus read failed so results may be partial."},"source":{"type":"string","enum":["mb","bandcamp","all"],"description":"Which corpus was searched (echo of ?source=; default mb). all = both corpora merged per cluster."},"mode":{"type":"string","enum":["exact","fuzzy"],"description":"Which match ran — 'exact' (default) or 'fuzzy' (?fuzzy=true substring)."},"titleQuery":{"type":"string","description":"The title as received (verbatim echo)."},"normalized":{"type":"string","description":"exact: the alphanumeric-normalized form matched; fuzzy: the lowercased substring matched."},"truncated":{"type":"boolean","description":"true when the exact-title match exceeded the candidate cap — the ranking is from a bounded sample; narrow via /resolve?artist=&track=."},"count":{"type":"integer","description":"Number of result rows (artist clusters)."},"results":{"type":"array","items":{"type":"object","properties":{"artistCredit":{"type":["string","null"],"description":"Representative as-credited artist string — descriptive, never a join key."},"clusterId":{"type":"string","description":"The performing artist's 64-hex cluster_id — the canonical onward key."},"releases":{"type":"integer","description":"Distinct releases carrying this artist's track of that title (the ranking signal). For source=all, the sum across corpora (Discogs + Bandcamp)."},"recordings":{"type":"integer","description":"Distinct recordings of that title by this artist (original + remixes/live collapse here)."},"sources":{"type":"array","items":{"type":"string","enum":["mb","bandcamp"]},"description":"Which corpus/corpora this cluster's hit came from — the provenance for source=all (e.g. [\"mb\"], [\"bandcamp\"], or both)."},"_links":{"type":"object","properties":{"artist":{"type":"string"},"artist_dossier":{"type":"string"}},"required":["artist","artist_dossier"],"description":"Onward v2 addresses."}},"required":["artistCredit","clusterId","releases","recordings","sources","_links"]}}},"required":["state","source","mode","titleQuery","normalized","truncated","count","results"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/tracks/resolve":{"post":{"summary":"Batch (artist + title) → duration_s — the structured track resolver","description":"Key-first, structured track resolution: give a batch of up to 50 (artist + title) pairs and get per-track `duration_s`. Built for locating a track inside a full episode/mix without the resolve→dossier→release→scan multi-hop. Match is case/punctuation/spacing-INSENSITIVE (alphanumeric-normalized, matching /resolve + /tracks) with a server-side version-suffix fuzz (drops `(Original Mix)`/`feat.`/remix) and a title-only fallback when `artist` is blank/\"Unknown Artist\". NOTE: normalization STRIPS diacritics rather than folding them, so accented and unaccented spellings do NOT cross-match (\"Beyoncé\" ≠ \"Beyonce\") — send artist+title as they appear in the tracklist. Two corpora, both keyed by artist+title: Bandcamp (duration + `bandcamp_item_id` + `track_url`; supports title-only) and MusicBrainz (`length_ms`; requires an artist). Bandcamp wins ties. A miss is `matched:false` at HTTP 200 (a coverage gap, never a 404). WANT: `duration` is served; `fingerprint` is ACCEPTED but returns `fingerprint:null` + an `unsupported` marker — crate stores no Chromaprint/fpcalc data. Keyed (X-API-Key).","tags":["Search"],"security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"want":{"type":"array","items":{"type":"string","enum":["duration","fingerprint"]},"description":"Subset of [\"duration\",\"fingerprint\"]; default [\"duration\"]. fingerprint is unsupported (see below)."},"tracks":{"type":"array","items":{"type":"object","properties":{"artist":{"type":["string","null"],"description":"Artist as it appears in the tracklist. Blank/omitted → title-only fallback (Bandcamp only)."},"title":{"type":"string","description":"Track title (required, ≤300 chars)."},"year":{"type":["integer","null"],"description":"Accepted for forward-compat; not yet used to disambiguate (the first exact/suffix-stripped match wins)."},"duration_hint_s":{"type":["number","null"],"description":"Accepted for forward-compat; not yet used to disambiguate (the first exact/suffix-stripped match wins)."}},"required":["title"]},"minItems":1,"maxItems":50,"description":"Up to 50 tracks per call (respects the 60/min limit)."}},"required":["tracks"]}}}},"responses":{"200":{"description":"Per-track resolution results (matched hits + honest-gap misses) + any unsupported wants.","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["ok","degraded"],"description":"'ok' = the read ran; 'degraded' = a substrate read failed (results empty-by-failure, retry)."},"results":{"type":"array","items":{"type":"object","properties":{"query_index":{"type":"integer","description":"Index of this result's track in the request array (batch) — 0 for the single GET."},"matched":{"type":"boolean","description":"true = a confident catalogue hit; false (still HTTP 200) = a coverage gap, not an error."},"confidence":{"type":"number","description":"0–1 match confidence — exact artist+title highest, suffix-stripped lower, title-only lowest. Gate on this."},"artist":{"type":["string","null"],"description":"The credited artist of the matched track (echoes the query artist when unmatched)."},"title":{"type":"string","description":"The query title, verbatim."},"release_title":{"type":["string","null"],"description":"Release the matched track sits on (Bandcamp source only)."},"bandcamp_item_id":{"type":["integer","null"],"description":"Bandcamp item id (Bandcamp source only)."},"track_url":{"type":["string","null"],"description":"The track PAGE url (Bandcamp source only) — not a stream."},"duration_s":{"type":["integer","null"],"description":"Track duration in whole seconds (rounded)."},"source":{"type":["string","null"],"enum":["bandcamp","mb"],"description":"Which corpus the duration came from: bandcamp (duration_sec) or mb (mb_recordings.length_ms)."},"fingerprint":{"type":"null","description":"ALWAYS null — crate has no Chromaprint/fpcalc data (see the unsupported marker). Not servable."}},"required":["query_index","matched","confidence","artist","title","release_title","bandcamp_item_id","track_url","duration_s","source","fingerprint"]}},"unsupported":{"type":"array","items":{"type":"object","properties":{"want":{"type":"string"},"reason":{"type":"string"}},"required":["want","reason"]},"description":"Requested `want` values crate cannot serve (e.g. fingerprint) + why."}},"required":["state","results","unsupported"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/track":{"get":{"summary":"Single (artist + title) → duration_s (convenience)","description":"The single-track sibling of POST /api/v2/tracks/resolve — same match logic + per-track shape, for one track / testing. `title` required; `artist` optional (blank → title-only fallback); `want` = comma-separated subset of `duration,fingerprint` (default `duration`; fingerprint → null + unsupported). Keyed (X-API-Key).","tags":["Search"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","description":"Artist as it appears in the tracklist. Omit/blank → title-only fallback."},"required":false,"name":"artist","in":"query"},{"schema":{"type":"string","description":"Track title (required)."},"required":true,"name":"title","in":"query"},{"schema":{"type":"string","description":"Comma-separated subset of duration,fingerprint (default duration)."},"required":false,"name":"want","in":"query"}],"responses":{"200":{"description":"The single resolution result (or a matched:false honest-gap) + any unsupported wants.","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["ok","degraded"]},"result":{"type":["object","null"],"properties":{"query_index":{"type":"integer","description":"Index of this result's track in the request array (batch) — 0 for the single GET."},"matched":{"type":"boolean","description":"true = a confident catalogue hit; false (still HTTP 200) = a coverage gap, not an error."},"confidence":{"type":"number","description":"0–1 match confidence — exact artist+title highest, suffix-stripped lower, title-only lowest. Gate on this."},"artist":{"type":["string","null"],"description":"The credited artist of the matched track (echoes the query artist when unmatched)."},"title":{"type":"string","description":"The query title, verbatim."},"release_title":{"type":["string","null"],"description":"Release the matched track sits on (Bandcamp source only)."},"bandcamp_item_id":{"type":["integer","null"],"description":"Bandcamp item id (Bandcamp source only)."},"track_url":{"type":["string","null"],"description":"The track PAGE url (Bandcamp source only) — not a stream."},"duration_s":{"type":["integer","null"],"description":"Track duration in whole seconds (rounded)."},"source":{"type":["string","null"],"enum":["bandcamp","mb"],"description":"Which corpus the duration came from: bandcamp (duration_sec) or mb (mb_recordings.length_ms)."},"fingerprint":{"type":"null","description":"ALWAYS null — crate has no Chromaprint/fpcalc data (see the unsupported marker). Not servable."}},"required":["query_index","matched","confidence","artist","title","release_title","bandcamp_item_id","track_url","duration_s","source","fingerprint"]},"unsupported":{"type":"array","items":{"type":"object","properties":{"want":{"type":"string"},"reason":{"type":"string"}},"required":["want","reason"]},"description":"Requested `want` values crate cannot serve (e.g. fingerprint) + why."}},"required":["state","result","unsupported"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/facets":{"get":{"summary":"Precomputed facet snapshot","description":"The same shape as SearchResponse.facets, narrowed by optional genre/style/country/year_from/year_to. format/label are not supported here.","tags":["Facets"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"genre","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"style","in":"query"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20}]},"required":false,"name":"country","in":"query"},{"schema":{"type":"integer","minimum":1900,"maximum":2100},"required":false,"name":"year_from","in":"query"},{"schema":{"type":"integer","minimum":1900,"maximum":2100},"required":false,"name":"year_to","in":"query"}],"responses":{"200":{"description":"Facet counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacetCounts"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (past_due customer or suspended key)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getFacets"}},"/api/v2/breakouts":{"get":{"summary":"Breakouts index — emerging artists (machine-readable)","description":"Booking-momentum \"ones to watch\" (seen.artist_emergence cross-validated against ridden press). Keyed; fail-soft (state present/empty/degraded → 200 honest-gap). ?tier= & ?corroboration= filter the bounded list.","tags":["Discovery"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Max rows (clamped to 200)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["breakout","rising"]},"required":false,"name":"tier","in":"query"},{"schema":{"type":"string","enum":["corroborated","booking_ahead"]},"required":false,"name":"corroboration","in":"query"}],"responses":{"200":{"description":"Breakouts index items (or honest-gap state)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreakoutsResponse"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getBreakouts"}},"/api/v2/aura":{"get":{"summary":"Aura index — multi-dimension convergence signals, strongest first","description":"Per-artist \"aura\": how many independent signal dimensions (booking, radio, press, …) are converging on an artist inside an 18-month window, with seen's measured 12-month break odds. Sorted convergence_dim_count DESC, break_odds DESC, latest_appearance_at DESC. Keyed (X-API-Key); ?limit= clamped 1..200 (default 50). Fail-soft: a substrate read failure → 200 state:'degraded' (never a 500). Aggregate-only by construction. Every response carries an `about` legibility block (what the metrics mean, approximate scope/scale, honest limits) — aura measures convergence across the signals hosaka observes (DEEP, not a global popularity meter); `break_odds` is a base rate at a convergence depth, not a per-artist forecast.","tags":["Discovery"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Max rows (clamped 1..200; default 50)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Aura rows strongest-first (state present), or the degraded honest-gap state; plus the `about` legibility block","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["present","degraded"],"description":"'degraded' = the substrate read failed (items empty) — still HTTP 200."},"about":{"type":"object","properties":{"what":{"type":"object","properties":{"convergence_dim_count":{"type":"string"},"break_odds":{"type":"string"},"dimensions":{"type":"string"}},"required":["convergence_dim_count","break_odds","dimensions"],"description":"Plain-English meaning of each metric (no formula disclosure)."},"scope":{"type":"object","properties":{"universe":{"type":"string"},"dimensions":{"type":"integer"},"window_months":{"type":"integer"},"as_of":{"type":"string"}},"required":["universe","dimensions","window_months","as_of"],"description":"Approximate scope/scale of the aura universe — static bands, dated (refreshed on re-measure)."},"honest_limits":{"type":"array","items":{"type":"string"},"description":"DEEP-not-global caveats: how to read — and not over-read — the signal."}},"required":["what","scope","honest_limits"],"description":"Legibility block: what the metrics mean, approximate scope/scale, and honest limits. Identical on every response — cache or ignore it."},"items":{"type":"array","items":{"type":"object","properties":{"cluster_id":{"type":"string","description":"The 64-hex cluster_id — the canonical fleet identity address (resolve names/links via /api/v2/resolve)."},"artist_name":{"type":"string"},"convergence_dim_count":{"type":"integer","description":"Independent convergence dimensions lit in the 18-month window (≥2 by the producer universe rule; a present-state read — the count can decrease)."},"dimensions":{"type":"array","items":{"type":"string"},"description":"The lit dimension names (e.g. booking, radio, press)."},"break_odds":{"type":["number","null"],"description":"Measured probability (percent) of a calibrated break within 12 months — seen's forward-precision curve, keyed on independent dimensions; null when no curve point exists."},"first_signal_at":{"type":"string","description":"Earliest first-appearance date in the window (YYYY-MM-DD)."},"latest_appearance_at":{"type":"string","description":"Most recent date a NEW dimension first appeared — not latest activity (YYYY-MM-DD)."},"has_break_event":{"type":"boolean"},"break_event_at":{"type":["string","null"],"description":"Earliest calibrated break date (YYYY-MM-DD); null when has_break_event=false."}},"required":["cluster_id","artist_name","convergence_dim_count","dimensions","break_odds","first_signal_at","latest_appearance_at","has_break_event","break_event_at"]}}},"required":["state","about","items"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getAura"}},"/api/v2/aura/{cluster}":{"get":{"summary":"Aura for one artist by cluster_id","description":"The same aura row for a single artist, addressed by the 64-hex cluster_id. Malformed key → 400 invalid_locator. A cluster with no row on the substrate surface (single-dimension artists are filtered by the producer universe rule; inactive artists age out of the 18-month window) → 200 present:false state:honest_gap — an empty read is an answer, not an error. A read FAILURE → 200 present:false state:degraded.","tags":["Discovery"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The 64-hex cluster_id (case-insensitive)."},"required":true,"name":"cluster","in":"path"}],"responses":{"200":{"description":"The aura row (present:true), or the honest-gap / degraded state","content":{"application/json":{"schema":{"type":"object","properties":{"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"aura":{"type":["object","null"],"properties":{"cluster_id":{"type":"string","description":"The 64-hex cluster_id — the canonical fleet identity address (resolve names/links via /api/v2/resolve)."},"artist_name":{"type":"string"},"convergence_dim_count":{"type":"integer","description":"Independent convergence dimensions lit in the 18-month window (≥2 by the producer universe rule; a present-state read — the count can decrease)."},"dimensions":{"type":"array","items":{"type":"string"},"description":"The lit dimension names (e.g. booking, radio, press)."},"break_odds":{"type":["number","null"],"description":"Measured probability (percent) of a calibrated break within 12 months — seen's forward-precision curve, keyed on independent dimensions; null when no curve point exists."},"first_signal_at":{"type":"string","description":"Earliest first-appearance date in the window (YYYY-MM-DD)."},"latest_appearance_at":{"type":"string","description":"Most recent date a NEW dimension first appeared — not latest activity (YYYY-MM-DD)."},"has_break_event":{"type":"boolean"},"break_event_at":{"type":["string","null"],"description":"Earliest calibrated break date (YYYY-MM-DD); null when has_break_event=false."}},"required":["cluster_id","artist_name","convergence_dim_count","dimensions","break_odds","first_signal_at","latest_appearance_at","has_break_event","break_event_at"]}},"required":["present","state","aura"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getAuraByCluster"}},"/api/v2/surface":{"get":{"summary":"Surface registry index — the queryable exposure ledger","description":"cycle-096 (carrefour#135): every generic cluster-keyed producer surface crate exposes, with its shape — grain, key (column + keyspace + wire format), keyset (pagination seek columns; null for cluster-row grain), cap, liveness, coverage_note, and advisory semantics. Since 2.20.0 (carrefour#139 Phase-1) each row also carries the producer-authored `semantics` facade (scope, scale bands, honest limits, caveats — verbatim at pin; null until authored) and a `dictionary_url` pointer to the fleet substrate dictionary. Read this before calling GET /api/v2/surface/{name} — it is the complete input contract for that call. Keyed (X-API-Key); pure/static (no DB checkout).","tags":["Surface Registry"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"The surface registry ledger.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["surface.index"]},"surfaces":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Schema-qualified registry key — pass verbatim as {name} to GET /api/v2/surface/{name}."},"producer":{"type":"string","description":"Owning fleet repo (hosaka-fm/<repo>)."},"enabled":{"type":"boolean","description":"crate-side kill switch for this row. false → GET /api/v2/surface/{name} 404s naming the disable (independent of the producer's own kill flag)."},"grain":{"type":"string","enum":["cluster-row","cluster-multirow","cluster-edge-list"],"description":"cluster-row = one row, no paging (cap 1/1); cluster-multirow/cluster-edge-list page via the keyset."},"key":{"type":"object","properties":{"column":{"type":"string"},"type":{"type":"string","enum":["bytea"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"],"description":"Which 64-hex identity keyspace ?cluster= must be drawn from. The keyspaces are disjoint; a key from the wrong one fails soft as an empty honest_gap, never an error."},"wire":{"type":"string","enum":["hex64"]}},"required":["column","type","keyspace","wire"]},"keyset":{"type":["object","null"],"properties":{"columns":{"type":"array","items":{"type":"object","properties":{"column":{"type":"string"},"type":{"type":"string"}},"required":["column","type"]}},"order":{"type":"string","enum":["asc","desc"]}},"required":["columns","order"],"description":"Pagination seek columns for ?after=; null for cluster-row grain (a single-row lookup, never paged)."},"cap":{"type":"object","properties":{"default":{"type":"integer"},"max":{"type":"integer"}},"required":["default","max"],"description":"Row-count clamp for a single ?limit= page."},"coverage_note":{"type":"string"},"advisory":{"type":["string","null"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"semantics":{"type":["object","null"],"additionalProperties":{},"description":"Producer-authored semantics facade (carrefour#139 Phase-1), copied VERBATIM from the producer contract at the pinned sha: audience_blurb, scale_bands, honest_limits, caveats, clock_class, formula_protected, as_of. Formula-protected by construction — crate refuses formula-leaking entries at acceptance. null until the producer authors a block (facades land per-surface as producers publish them)."},"dictionary_url":{"type":["string","null"],"description":"The fleet substrate dictionary (wintermute-compiled, carrefour#139): fleet chapters (clock classes, alias screening, k-anon, grain vocabulary) + producer semantics entries rendered verbatim at pins. One URL for the whole registry — crate-served at GET /api/v2/semantics/dictionary (OQ-4: crate proxies the org-visible producer doc so external consumers can read it)."}},"required":["name","producer","enabled","grain","key","keyset","cap","coverage_note","advisory","liveness","semantics","dictionary_url"],"description":"One registry row — the complete input contract for calling GET /api/v2/surface/{name} on it."}},"count":{"type":"integer"},"generated_at":{"type":"string"}},"required":["object","surfaces","count","generated_at"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getSurfaceIndex"}},"/api/v2/semantics/dictionary":{"get":{"summary":"Fleet substrate dictionary (the semantics glossary behind dictionary_url)","description":"The compiled fleet substrate dictionary — clock classes, alias-screening law, k-anon floors, grain vocabulary, and every producer `semantics` entry rendered verbatim. wintermute compiles it; crate serves it (carrefour#139 OQ-4: the producer repo is org-visible, so crate is the canonical serving path — this is the URL `GET /api/v2/surface`'s `dictionary_url` points at). Body is markdown; the envelope carries `source` + `source_sha` + `vendored_at` so you can tell which pin you're reading. Refreshed on producer pin-bumps (crate re-vendors). Keyed (X-API-Key); pure/static (no DB checkout).","tags":["Surface Registry"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"The dictionary body (markdown) + source provenance.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["semantics.dictionary"]},"format":{"type":"string","enum":["markdown"]},"source":{"type":"string","description":"Producer provenance: <repo>@<sha> <path> (<ref>)."},"source_sha":{"type":"string","description":"The git blob SHA of the vendored dictionary — the freshness key."},"vendored_at":{"type":"string","description":"Date crate last re-vendored the body (YYYY-MM-DD)."},"note":{"type":"string"},"body":{"type":"string","description":"The dictionary, verbatim markdown."}},"required":["object","format","source","source_sha","vendored_at","note","body"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/semantics/dictionary.json":{"get":{"summary":"Fleet substrate dictionary — structured JSON (per-entry, for agents)","description":"The structured-JSON companion to GET /api/v2/semantics/dictionary (markdown): the SAME dictionary emitted per-entry so an agent reads ONE metric's plain-language meaning (`.entries[\"aura\"].audience_blurb`) instead of parsing the ~68 KB markdown. Served VERBATIM — byte-identical to wintermute's dictionary.json, so `entries` is TOP-LEVEL (no crate envelope shadowing it); crate vendor provenance travels in response headers (`x-dictionary-source-sha`, `x-dictionary-vendored-at`). Facade fields only (audience_blurb / scale_bands / honest_limits / caveats / clock_class / formula_protected / as_of / producer / source_sha / surface) — no formula or raw-magnitude field. `schema_version` is 1 (additive-only within a major — pin the major, tolerate unknown additive fields). Keyed (X-API-Key); pure/static (no DB checkout). Refreshed on producer pin-bumps.","tags":["Surface Registry"],"security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"The structured dictionary (schema_version 1): schema_version + generator + per-entry facades under `entries`.","content":{"application/json":{"schema":{"type":"object","properties":{"schema_version":{"type":"integer","description":"Dictionary JSON contract version (1). Additive-only within a major."},"generator":{"type":"string","description":"The wintermute generator that emitted the dictionary."},"entries":{"type":"object","additionalProperties":{"type":"object","properties":{"audience_blurb":{"type":"string","description":"Plain-language definition of what the metric/entry means."},"scale_bands":{"type":"array","items":{"type":"string"},"description":"Human-readable scale/interpretation bands."},"honest_limits":{"type":["string","null"],"description":"What the metric does NOT measure."},"caveats":{"type":"array","items":{"type":"string"},"description":"Interpretation caveats."},"clock_class":{"type":["string","null"],"description":"The refresh clock class."},"formula_protected":{"type":"boolean","description":"Whether the underlying formula is withheld (facade-only)."},"as_of":{"type":["string","null"],"description":"As-of date of the entry."},"producer":{"type":["string","null"],"description":"The producing repo."},"source_sha":{"type":["string","null"],"description":"Per-entry source SHA — the freshness key."},"surface":{"type":["string","null"],"description":"The pins identifier / producer surface this entry maps to."}},"required":["audience_blurb","scale_bands","honest_limits","caveats","clock_class","formula_protected","as_of","producer","source_sha","surface"]},"description":"Per-entry facades keyed by entry name (e.g. \"aura\"). ~35 entries."}},"required":["schema_version","generator","entries"]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v2/surface/{name}":{"get":{"summary":"Generic cluster-keyed surface read (by registry name)","description":"The generic read behind every row in GET /api/v2/surface: one operation serves all 46 registered surfaces (public.spine_artist_temporal_profile, seen.radio_play_v1, seen.dj_champion, seen.radio_co_play, seen.song_station_journey, mirror.wantlist_demand_by_cluster_v1, seen.master_propagation_timeline_cluster, seen.dj_tastemaker_score, seen.performing_entity, seen.artist_dossier, seen.artist_momentum, seen.artist_network_position, seen.artist_tier_presence, seen.artist_festival_co_appearance, seen.artist_cross_tier_network, seen.artist_brokerage, seen.artist_emergence_narrative_public, seen.artist_airplay_first_appearance, seen.artist_djset_first_appearance, seen.artist_emergence_lead_time, seen.artist_dated_appearance, seen.artist_primary_geography, seen.artist_identity_bridge, seen.live_demand, seen.bandcamp_artist_gravity, seen.bandcamp_artist_tastemaker_quality, seen.artist_djset_scout_signal, seen.label_djset_momentum, seen.artist_signal_passport, seen.artist_sc_rights_rollup_v1, archive_api_v1.artist_mention_daily, public.spine_artist_name_published_view, archive_api_v1.artist_press_mentions, seen.artist_signal_known_since, mirror.cluster_authority_ids_v1, sync.placement_claim_by_cluster_v1, deadwax.pressing_provenance_depth_by_cluster_v1, mirror.listen_flow_by_cluster_v1, mirror.cluster_alias_v1, wintermute.cluster_arrival_ledger_v1, seen.radar_only_artists_v1, sync.placement_arrival_by_cluster_v1, wintermute.cluster_arrival_wide_v1, mirror.market_vs_flow_divergence_v1, seen.sc_crossing_v1, mirror.cluster_identity_v1). {name} is the schema-qualified registry key — GET /api/v2/surface for the live list + each name's shape. ?cluster= is the 64-hex identity key in THAT surface's registered keyspace (a key from the wrong keyspace fails soft as an empty honest_gap, not an error — see the index for which keyspace {name} expects). cluster-row grain surfaces (cap 1/1) ignore ?after/?limit and answer with 0 or 1 rows; cluster-multirow/cluster-edge-list grains keyset-paginate via the opaque ?after cursor from a prior page's next_after (never OFFSET — pass it back verbatim, never construct or decode it). ?limit clamps to the surface's registered cap. Unknown {name} → 400 with a hint listing every valid name + doc_url + next (the index call). A per-row crate-side kill (registry enabled:false) → 404; the master kill (env CRATE_SURFACE_ENABLED=false) → 503. state:'degraded' (still HTTP 200, rows:[]) means the dedicated crate_surface_reader read pool is unconfigured or not yet granted on the replica — fail-closed: the code ships ahead of the DB role landing. Cursor durability: cursors are page-iteration handles, NOT bookmarks — some surfaces build them from producer-internal columns that can change across producer re-crawls (seen.radio_play_v1's play_key today), so a stored cursor may silently skip or repeat rows after a re-crawl; re-start from the first page for a fresh read (each surface's coverage_note in GET /api/v2/surface carries the current specifics).","tags":["Surface Registry"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["public.spine_artist_temporal_profile","seen.radio_play_v1","seen.dj_champion","seen.radio_co_play","seen.song_station_journey","mirror.wantlist_demand_by_cluster_v1","seen.master_propagation_timeline_cluster","seen.dj_tastemaker_score","seen.performing_entity","seen.artist_dossier","seen.artist_momentum","seen.artist_network_position","seen.artist_tier_presence","seen.artist_festival_co_appearance","seen.artist_cross_tier_network","seen.artist_brokerage","seen.artist_emergence_narrative_public","seen.artist_airplay_first_appearance","seen.artist_djset_first_appearance","seen.artist_emergence_lead_time","seen.artist_dated_appearance","seen.artist_primary_geography","seen.artist_identity_bridge","seen.live_demand","seen.bandcamp_artist_gravity","seen.bandcamp_artist_tastemaker_quality","seen.artist_djset_scout_signal","seen.label_djset_momentum","seen.artist_signal_passport","seen.artist_sc_rights_rollup_v1","archive_api_v1.artist_mention_daily","public.spine_artist_name_published_view","archive_api_v1.artist_press_mentions","seen.artist_signal_known_since","mirror.cluster_authority_ids_v1","sync.placement_claim_by_cluster_v1","deadwax.pressing_provenance_depth_by_cluster_v1","mirror.listen_flow_by_cluster_v1","mirror.cluster_alias_v1","wintermute.cluster_arrival_ledger_v1","seen.radar_only_artists_v1","sync.placement_arrival_by_cluster_v1","wintermute.cluster_arrival_wide_v1","mirror.market_vs_flow_divergence_v1","seen.sc_crossing_v1","mirror.cluster_identity_v1"],"description":"The schema-qualified registry key, e.g. seen.radio_play_v1. GET /api/v2/surface lists every valid name."},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The 64-hex identity key in this surface's registered keyspace (case-insensitive) — see GET /api/v2/surface for which keyspace {name} expects."},"required":true,"name":"cluster","in":"query"},{"schema":{"type":"string","description":"Opaque keyset cursor from a previous page's next_after. Pass back verbatim; never construct or decode it. Omit for the first page."},"required":false,"name":"after","in":"query"},{"schema":{"type":"integer","minimum":1,"description":"Rows per page, clamped to the surface's registered cap (see GET /api/v2/surface → cap.default / cap.max)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Surface rows (state:'present'), an honest_gap (present:false, rows:[] — never 404), or a degraded read (state:'degraded', the crate_surface_reader pool is unconfigured/ungranted).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["public.spine_artist_temporal_profile"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"resolved_artist_discogs_id":{"type":["number","null"]},"master_count_with_year":{"type":["number","null"]},"earliest_release_year":{"type":["number","null"]},"latest_release_year":{"type":["number","null"]},"active_decades":{"type":"array","items":{"type":["number","null"]}},"year_span":{"type":["number","null"]}},"required":["cluster_id_hex","resolved_artist_discogs_id","master_count_with_year","earliest_release_year","latest_release_year","active_decades","year_span"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.radio_play_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"song_cluster_id_hex":{"type":["string","null"],"pattern":"^[0-9a-f]{64}$"},"source_type":{"type":"string"},"station_key":{"type":"string"},"dj_canonical_name":{"type":["string","null"]},"position":{"type":["number","null"]},"extracted_title":{"type":["string","null"]},"extracted_remix":{"type":["string","null"]},"extracted_label":{"type":["string","null"]},"played_on":{"type":"string"},"date_in_sanity_window":{"type":"boolean"},"is_quarantined":{"type":"boolean"}},"required":["cluster_id_hex","song_cluster_id_hex","source_type","station_key","dj_canonical_name","position","extracted_title","extracted_remix","extracted_label","played_on","date_in_sanity_window","is_quarantined"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.dj_champion"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"dj_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"play_count":{"type":["number","null"]},"play_event_count":{"type":["number","null"]},"first_played_at":{"type":"string"},"latest_played_at":{"type":"string"},"first_corpus_play":{"type":"string"},"days_after_first_corpus_play":{"type":["number","null"]},"is_early_champion":{"type":"boolean"},"is_repeat_champion":{"type":"boolean"}},"required":["dj_cluster_id_hex","play_count","play_event_count","first_played_at","latest_played_at","first_corpus_play","days_after_first_corpus_play","is_early_champion","is_repeat_champion"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.radio_co_play"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"co_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"co_play_count":{"type":["number","null"]},"distinct_stations":{"type":["number","null"]},"first_co_played_at":{"type":"string"},"last_co_played_at":{"type":"string"}},"required":["co_cluster_id_hex","co_play_count","distinct_stations","first_co_played_at","last_co_played_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.song_station_journey"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"station_key":{"type":"string"},"first_played_on":{"type":"string"},"last_played_on":{"type":"string"},"play_count":{"type":["number","null"]},"journey_rank":{"type":["number","null"]}},"required":["station_key","first_played_on","last_played_on","play_count","journey_rank"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["mirror.wantlist_demand_by_cluster_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"representative_name":{"type":["string","null"]},"wantlist_demand":{"type":["number","null"]},"distinct_masters_wanted":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","representative_name","wantlist_demand","distinct_masters_wanted","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.master_propagation_timeline_cluster"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"dj_canonical_name":{"type":["string","null"]},"dj_id":{"type":["number","null"]},"played_at":{"type":"string"},"source":{"type":"string"},"tracklist_id":{"type":"string","format":"uuid"},"entry_id":{"type":["number","null"]}},"required":["cluster_id_hex","dj_canonical_name","dj_id","played_at","source","tracklist_id","entry_id"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.dj_tastemaker_score"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"dj_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"dj_canonical_name":{"type":"string"},"total_masters_played":{"type":["number","null"]},"masters_played_first":{"type":["number","null"]},"masters_played_within_first_7d":{"type":["number","null"]},"cutting_edge_score":{"type":["number","null"]},"first_play_at":{"type":["string","null"]},"latest_play_at":{"type":["string","null"]},"rank":{"type":["number","null"]}},"required":["dj_cluster_id_hex","dj_canonical_name","total_masters_played","masters_played_first","masters_played_within_first_7d","cutting_edge_score","first_play_at","latest_play_at","rank"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.performing_entity"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"normalizer_version":{"type":"string"},"artist_name_display":{"type":"string"},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"resolved_method":{"type":["string","null"]},"resolved_at":{"type":["string","null"]},"event_count":{"type":["number","null"]},"distinct_events":{"type":["number","null"]},"first_seen_at":{"type":"string"},"last_seen_at":{"type":"string"},"distinct_venues":{"type":["number","null"]},"distinct_areas":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","normalizer_version","artist_name_display","resolved_discogs_artist_id","resolved_mbid","resolved_method","resolved_at","event_count","distinct_events","first_seen_at","last_seen_at","distinct_venues","distinct_areas","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_dossier"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"resolved_discogs_artist_id":{"type":["number","null"]},"tracklist_count":{"type":["number","null"]},"distinct_radio_sources":{"type":["number","null"]},"festival_appearances":{"type":["number","null"]},"distinct_festivals":{"type":["number","null"]},"most_recent_festival_at":{"type":["string","null"]},"culture_forward_festival_count":{"type":["number","null"]},"five_factor_score_weighted":{"type":["number","null"]},"event_count":{"type":["number","null"]},"distinct_events":{"type":["number","null"]},"first_seen_at":{"type":["string","null"]},"last_seen_at":{"type":["string","null"]},"distinct_venues":{"type":["number","null"]},"distinct_areas":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"artist_name_display":{"type":["string","null"]},"has_radio_festival_signal":{"type":"boolean"},"has_event_gravity":{"type":"boolean"},"journalism_mention_count":{"type":["number","null"]},"journalism_article_count":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","resolved_discogs_artist_id","tracklist_count","distinct_radio_sources","festival_appearances","distinct_festivals","most_recent_festival_at","culture_forward_festival_count","five_factor_score_weighted","event_count","distinct_events","first_seen_at","last_seen_at","distinct_venues","distinct_areas","resolved_mbid","artist_name_display","has_radio_festival_signal","has_event_gravity","journalism_mention_count","journalism_article_count","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_momentum"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"events_recent":{"type":["number","null"]},"events_baseline":{"type":["number","null"]},"events_total":{"type":["number","null"]},"venues_recent":{"type":["number","null"]},"areas_recent":{"type":["number","null"]},"first_seen_at":{"type":["string","null"]},"last_seen_at":{"type":["string","null"]},"velocity":{"type":["number","null"]},"momentum_score":{"type":["number","null"]},"is_emerging":{"type":"boolean"},"momentum_tier":{"type":"string"},"artist_name_display":{"type":["string","null"]},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"computed_at":{"type":"string"}},"required":["cluster_id_hex","events_recent","events_baseline","events_total","venues_recent","areas_recent","first_seen_at","last_seen_at","velocity","momentum_score","is_emerging","momentum_tier","artist_name_display","resolved_discogs_artist_id","resolved_mbid","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_network_position"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"degree":{"type":["number","null"]},"weighted_degree":{"type":["number","null"]},"resolved_neighbours":{"type":["number","null"]},"resolved_neighbour_ratio":{"type":["number","null"]},"recent_degree":{"type":["number","null"]},"recent_resolved_neighbours":{"type":["number","null"]},"recent_resolved_ratio":{"type":["number","null"]},"establishment_delta":{"type":["number","null"]},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"artist_name_display":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","degree","weighted_degree","resolved_neighbours","resolved_neighbour_ratio","recent_degree","recent_resolved_neighbours","recent_resolved_ratio","establishment_delta","resolved_discogs_artist_id","resolved_mbid","artist_name_display","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_tier_presence"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"club_obs":{"type":["number","null"]},"club_events":{"type":["number","null"]},"first_club_at":{"type":["string","null"]},"last_club_at":{"type":["string","null"]},"festival_obs":{"type":["number","null"]},"festival_editions":{"type":["number","null"]},"first_festival_at":{"type":["string","null"]},"last_festival_at":{"type":["string","null"]},"plays_clubs":{"type":"boolean"},"plays_festivals":{"type":"boolean"},"spans_tiers":{"type":"boolean"},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"artist_name_display":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","club_obs","club_events","first_club_at","last_club_at","festival_obs","festival_editions","first_festival_at","last_festival_at","plays_clubs","plays_festivals","spans_tiers","resolved_discogs_artist_id","resolved_mbid","artist_name_display","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_festival_co_appearance"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"co_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"co_edition_count":{"type":["number","null"]},"co_festival_count":{"type":["number","null"]},"co_country_count":{"type":["number","null"]},"first_co_at":{"type":["string","null"]},"last_co_at":{"type":["string","null"]},"co_resolved_discogs_artist_id":{"type":["number","null"]},"co_resolved_mbid":{"type":["string","null"],"format":"uuid"},"co_artist_name_display":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","co_cluster_id_hex","co_edition_count","co_festival_count","co_country_count","first_co_at","last_co_at","co_resolved_discogs_artist_id","co_resolved_mbid","co_artist_name_display","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_cross_tier_network"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"club_co_artists":{"type":["number","null"]},"festival_co_artists":{"type":["number","null"]},"both_tier_co_artists":{"type":["number","null"]},"total_co_artists":{"type":["number","null"]},"cross_tier_ratio":{"type":["number","null"]},"spans_tiers":{"type":"boolean"},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"artist_name_display":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","club_co_artists","festival_co_artists","both_tier_co_artists","total_co_artists","cross_tier_ratio","spans_tiers","resolved_discogs_artist_id","resolved_mbid","artist_name_display","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_brokerage"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"degree":{"type":["number","null"]},"core_size":{"type":["number","null"]},"core_ego_edges":{"type":["number","null"]},"ego_density":{"type":["number","null"]},"effective_size":{"type":["number","null"]},"efficiency":{"type":["number","null"]},"brokerage_score":{"type":["number","null"]},"brokerage_tier":{"type":"string"},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"artist_name_display":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","degree","core_size","core_ego_edges","ego_density","effective_size","efficiency","brokerage_score","brokerage_tier","resolved_discogs_artist_id","resolved_mbid","artist_name_display","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_emergence_narrative_public"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"resolved_discogs_artist_id":{"type":["number","null"]},"artist_name_display":{"type":["string","null"]},"first_press_date":{"type":["string","null"]},"first_airplay_date":{"type":["string","null"]},"first_booking_date":{"type":["string","null"]},"first_underground_signal_date":{"type":["string","null"]},"first_underground_signal_source":{"type":["string","null"]},"press_to_airplay_days":{"type":["number","null"]},"underground_to_booking_days":{"type":["number","null"]},"total_press_articles":{"type":["number","null"]},"distinct_press_sources":{"type":["number","null"]},"airplay_count":{"type":["number","null"]},"distinct_stations":{"type":["number","null"]},"underground_source_count":{"type":["number","null"]},"source_count":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","resolved_discogs_artist_id","artist_name_display","first_press_date","first_airplay_date","first_booking_date","first_underground_signal_date","first_underground_signal_source","press_to_airplay_days","underground_to_booking_days","total_press_articles","distinct_press_sources","airplay_count","distinct_stations","underground_source_count","source_count","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_airplay_first_appearance"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"first_airplay_date":{"type":["string","null"]},"latest_airplay_date":{"type":["string","null"]},"airplay_count":{"type":["number","null"]},"distinct_stations":{"type":["number","null"]},"distinct_venues":{"type":["number","null"]},"distinct_djs":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","first_airplay_date","latest_airplay_date","airplay_count","distinct_stations","distinct_venues","distinct_djs","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_djset_first_appearance"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"first_djset_date":{"type":["string","null"]},"latest_djset_date":{"type":["string","null"]},"djset_count":{"type":["number","null"]},"distinct_djs":{"type":["number","null"]},"distinct_venues":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","first_djset_date","latest_djset_date","djset_count","distinct_djs","distinct_venues","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_emergence_lead_time"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"first_press_date":{"type":["string","null"]},"first_airplay_date":{"type":["string","null"]},"first_booking_date":{"type":["string","null"]},"first_underground_signal_date":{"type":["string","null"]},"first_underground_signal_source":{"type":["string","null"]},"underground_source_count":{"type":["number","null"]},"source_count":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","first_press_date","first_airplay_date","first_booking_date","first_underground_signal_date","first_underground_signal_source","underground_source_count","source_count","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_dated_appearance"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"source_kind":{"type":"string"},"first_appearance_date":{"type":["string","null"]},"latest_appearance_date":{"type":["string","null"]},"appearance_count":{"type":["number","null"]}},"required":["cluster_id_hex","source_kind","first_appearance_date","latest_appearance_date","appearance_count"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_primary_geography"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"area_id":{"type":"string"},"primary_city":{"type":["string","null"]},"primary_region":{"type":["string","null"]},"primary_country":{"type":["string","null"]},"from_recent_window":{"type":"boolean"},"computed_at":{"type":"string"}},"required":["cluster_id_hex","area_id","primary_city","primary_region","primary_country","from_recent_window","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_identity_bridge"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"ra_artist_id":{"type":"string"},"url_safe_name":{"type":["string","null"]},"ra_display_name":{"type":["string","null"]},"discogs_artist_id_claimed":{"type":["number","null"]},"discogs_url":{"type":["string","null"]},"bandcamp_url":{"type":["string","null"]},"soundcloud_url":{"type":["string","null"]},"instagram_url":{"type":["string","null"]},"website_url":{"type":["string","null"]},"country_name":{"type":["string","null"]},"ra_status":{"type":["string","null"]},"follower_count":{"type":["number","null"]},"first_event_id":{"type":["string","null"]},"first_event_title":{"type":["string","null"]},"first_event_date":{"type":["string","null"]},"fetched_at":{"type":"string"},"spotify_url":{"type":["string","null"]},"youtube_url":{"type":["string","null"]}},"required":["cluster_id_hex","ra_artist_id","url_safe_name","ra_display_name","discogs_artist_id_claimed","discogs_url","bandcamp_url","soundcloud_url","instagram_url","website_url","country_name","ra_status","follower_count","first_event_id","first_event_title","first_event_date","fetched_at","spotify_url","youtube_url"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.live_demand"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"events_tracked":{"type":["number","null"]},"events_sold_out":{"type":["number","null"]},"distinct_providers":{"type":["number","null"]},"distinct_promoters":{"type":["number","null"]},"total_capacity_tracked":{"type":["number","null"]},"avg_fill_fraction":{"type":["number","null"]},"max_sell_through_per_day":{"type":["number","null"]},"max_sold_out_lead_days":{"type":["number","null"]},"max_demand_proxy":{"type":["number","null"]},"first_demand_event_date":{"type":["string","null"]},"latest_demand_event_date":{"type":["string","null"]},"demand_momentum_score":{"type":["number","null"]},"demand_tier":{"type":"string"},"artist_name_display":{"type":["string","null"]},"resolved_discogs_artist_id":{"type":["number","null"]},"resolved_mbid":{"type":["string","null"],"format":"uuid"},"computed_at":{"type":"string"}},"required":["cluster_id_hex","events_tracked","events_sold_out","distinct_providers","distinct_promoters","total_capacity_tracked","avg_fill_fraction","max_sell_through_per_day","max_sold_out_lead_days","max_demand_proxy","first_demand_event_date","latest_demand_event_date","demand_momentum_score","demand_tier","artist_name_display","resolved_discogs_artist_id","resolved_mbid","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.bandcamp_artist_gravity"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"sample_artist_name":{"type":["string","null"]},"owner_reach":{"type":["number","null"]},"wishlist_demand":{"type":["number","null"]},"distinct_releases":{"type":["number","null"]},"demand_lead":{"type":["number","null"]},"demand_ratio":{"type":["number","null"]},"emergence_class":{"type":["string","null"]},"bandcamp_demand_edge_date":{"type":["string","null"]},"bandcamp_earliest_supported_date":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","sample_artist_name","owner_reach","wishlist_demand","distinct_releases","demand_lead","demand_ratio","emergence_class","bandcamp_demand_edge_date","bandcamp_earliest_supported_date","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.bandcamp_artist_tastemaker_quality"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"sample_artist_name":{"type":["string","null"]},"supporter_cohort_size":{"type":["number","null"]},"aesq":{"type":["number","null"]},"aesq_median":{"type":["number","null"]},"aesq_rw":{"type":["number","null"]},"mean_first_buyer_earliness":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","sample_artist_name","supporter_cohort_size","aesq","aesq_median","aesq_rw","mean_first_buyer_earliness","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_djset_scout_signal"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"scouting_dj_count":{"type":["number","null"]},"max_dj_scout_score":{"type":["number","null"]},"max_dj_earliness":{"type":["number","null"]},"max_dj_brokerage":{"type":["number","null"]},"has_broker_cosign":{"type":"boolean"},"has_early_dj_scout_cosign":{"type":"boolean"},"earliest_djset_play":{"type":["string","null"]},"latest_djset_play":{"type":["string","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","scouting_dj_count","max_dj_scout_score","max_dj_earliness","max_dj_brokerage","has_broker_cosign","has_early_dj_scout_cosign","earliest_djset_play","latest_djset_play","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.label_djset_momentum"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"label_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"label_norm":{"type":"string"},"play_count":{"type":["number","null"]},"distinct_djs":{"type":["number","null"]},"distinct_artists":{"type":["number","null"]},"distinct_tracklists":{"type":["number","null"]},"first_played_at":{"type":["string","null"]},"last_played_at":{"type":["string","null"]},"plays_last_365d":{"type":["number","null"]}},"required":["label_cluster_id_hex","label_norm","play_count","distinct_djs","distinct_artists","distinct_tracklists","first_played_at","last_played_at","plays_last_365d"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_signal_passport"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"leading_signal_date":{"type":["string","null"]},"leading_signal_source":{"type":["string","null"]},"leading_known_since":{"type":["string","null"]},"leading_detection_days":{"type":["number","null"]},"leading_detection_class":{"type":"string"},"convergence_dim_count":{"type":["number","null"]},"source_kind_count":{"type":["number","null"]},"velocity_bucket":{"type":["string","null"]},"break_odds":{"type":["number","null"]},"velocity_break_odds":{"type":["number","null"]},"break_odds_basis":{"type":["string","null"]},"first_signal_at":{"type":["string","null"]},"latest_appearance_at":{"type":["string","null"]},"has_break_event":{"type":["boolean","null"]}},"required":["cluster_id_hex","leading_signal_date","leading_signal_source","leading_known_since","leading_detection_days","leading_detection_class","convergence_dim_count","source_kind_count","velocity_bucket","break_odds","velocity_break_odds","break_odds_basis","first_signal_at","latest_appearance_at","has_break_event"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_sc_rights_rollup_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"tracks_with_isrc":{"type":["number","null"]},"tracks_bound":{"type":["number","null"]},"tracks_with_registered_iswc":{"type":["number","null"]},"bind_routes_summary":{"type":["string","null"]}},"required":["cluster_id_hex","tracks_with_isrc","tracks_bound","tracks_with_registered_iswc","bind_routes_summary"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["archive_api_v1.artist_mention_daily"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"source_publisher":{"type":"string"},"mention_date":{"type":"string"},"mention_count":{"type":["number","null"]},"max_extraction_confidence":{"type":["number","null"]}},"required":["cluster_id_hex","source_publisher","mention_date","mention_count","max_extraction_confidence"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["public.spine_artist_name_published_view"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"resolved_artist_mbid":{"type":["string","null"],"format":"uuid"},"resolved_artist_discogs_id":{"type":["number","null"]},"cluster_member_count":{"type":["number","null"]},"cluster_source_repo_count":{"type":["number","null"]},"cluster_first_observed_at":{"type":["string","null"]},"cluster_latest_observed_at":{"type":["string","null"]}},"required":["cluster_id_hex","resolved_artist_mbid","resolved_artist_discogs_id","cluster_member_count","cluster_source_repo_count","cluster_first_observed_at","cluster_latest_observed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.artist_signal_known_since"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"source_kind":{"type":"string"},"first_appearance_date":{"type":"string"},"known_since":{"type":["string","null"]},"backfill_class":{"type":["string","null"]}},"required":["cluster_id_hex","source_kind","first_appearance_date","known_since","backfill_class"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["mirror.cluster_authority_ids_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"qid_count":{"type":["number","null"]},"qid_map":{"type":["array","null"],"items":{"type":"string"}},"clusterid_comergeable":{"type":["boolean","null"]},"isni":{"type":["string","null"]},"isni_ids":{"type":["array","null"],"items":{"type":"string"}},"isni_id_count":{"type":["number","null"]},"viaf":{"type":["string","null"]},"viaf_ids":{"type":["array","null"],"items":{"type":"string"}},"viaf_id_count":{"type":["number","null"]},"gnd":{"type":["string","null"]},"gnd_ids":{"type":["array","null"],"items":{"type":"string"}},"gnd_id_count":{"type":["number","null"]},"lc":{"type":["string","null"]},"lc_ids":{"type":["array","null"],"items":{"type":"string"}},"lc_id_count":{"type":["number","null"]},"discogs":{"type":["string","null"]},"discogs_ids":{"type":["array","null"],"items":{"type":"string"}},"discogs_id_count":{"type":["number","null"]},"discogs_bind_status":{"type":["string","null"]},"discogs_bridge_cluster_id_hex":{"type":["string","null"],"pattern":"^[0-9a-f]{64}$"},"on_mb_spine":{"type":["boolean","null"]},"computed_at":{"type":["string","null"]}},"required":["cluster_id_hex","qid_count","qid_map","clusterid_comergeable","isni","isni_ids","isni_id_count","viaf","viaf_ids","viaf_id_count","gnd","gnd_ids","gnd_id_count","lc","lc_ids","lc_id_count","discogs","discogs_ids","discogs_id_count","discogs_bind_status","discogs_bridge_cluster_id_hex","on_mb_spine","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["sync.placement_claim_by_cluster_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"claim_key":{"type":["string","null"]},"discogs_master_id":{"type":["number","null"]},"recording_mbid":{"type":["string","null"],"format":"uuid"},"screen_title":{"type":["string","null"]},"media_type":{"type":["string","null"]},"resolved_year":{"type":["number","null"]},"placement_tier":{"type":["number","null"]},"published_confidence":{"type":["string","null"]},"confidence_score":{"type":["number","null"]},"resolution_source":{"type":["string","null"]},"convergent":{"type":["boolean","null"]},"tmdb_id":{"type":["number","null"]},"supervisor_name":{"type":["string","null"]},"agency_name":{"type":["string","null"]},"attribution_confidence":{"type":["string","null"]},"attribution_source_url":{"type":["string","null"]}},"required":["cluster_id_hex","claim_key","discogs_master_id","recording_mbid","screen_title","media_type","resolved_year","placement_tier","published_confidence","confidence_score","resolution_source","convergent","tmdb_id","supervisor_name","agency_name","attribution_confidence","attribution_source_url"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["deadwax.pressing_provenance_depth_by_cluster_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"surface_version":{"type":["number","null"]},"distinct_pressings":{"type":["number","null"]},"distinct_masters":{"type":["number","null"]},"distinct_matrix_clusters":{"type":["number","null"]},"pressings_with_audiophile_credit":{"type":["number","null"]},"matrix_authoritative_share":{"type":["number","null"]},"bind_is_heuristic":{"type":["boolean","null"]},"computed_at":{"type":["string","null"]}},"required":["cluster_id_hex","surface_version","distinct_pressings","distinct_masters","distinct_matrix_clusters","pressings_with_audiophile_credit","matrix_authoritative_share","bind_is_heuristic","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["mirror.listen_flow_by_cluster_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"listen_count":{"type":["number","null"]},"distinct_listener_count":{"type":["number","null"]},"distinct_recording_count":{"type":["number","null"]},"first_listened_at":{"type":"string"},"last_listened_at":{"type":"string"},"computed_at":{"type":"string"}},"required":["cluster_id_hex","listen_count","distinct_listener_count","distinct_recording_count","first_listened_at","last_listened_at","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["mirror.cluster_alias_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"alias_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"canonical_cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"bind_arm":{"type":"string"},"computed_at":{"type":"string"}},"required":["alias_cluster_id_hex","canonical_cluster_id_hex","bind_arm","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["wintermute.cluster_arrival_ledger_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"layer":{"type":"string"},"event_time":{"type":["string","null"]},"event_grain":{"type":["string","null"]},"detection_time":{"type":["string","null"]},"detection_class":{"type":["string","null"]},"alias_bridged":{"type":"boolean"},"bind_arms":{"type":["array","null"],"items":{"type":"string"}},"source_relation":{"type":"string"},"source_column":{"type":"string"},"coverage_note":{"type":"string"},"quality_flags":{},"composed_at":{"type":"string"}},"required":["cluster_id_hex","layer","event_time","event_grain","detection_time","detection_class","alias_bridged","bind_arms","source_relation","source_column","coverage_note","composed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["seen.radar_only_artists_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"artist_name":{"type":["string","null"]},"independent_dimensions":{"type":["number","null"]},"dimensions":{"type":["string","null"]},"first_signal":{"type":["string","null"]},"latest_signal":{"type":["string","null"]},"span_days":{"type":["number","null"]},"in_booking_circuit":{"type":["boolean","null"]},"has_authority_record":{"type":"boolean"},"has_seen_discogs_bind":{"type":"boolean"},"institutionally_catalogued":{"type":"boolean"},"has_listen_flow":{"type":"boolean"},"institutional_visibility":{"type":"string"},"listen_count":{"type":["number","null"]},"distinct_listener_count":{"type":["number","null"]},"last_listened_at":{"type":["string","null"]}},"required":["cluster_id_hex","artist_name","independent_dimensions","dimensions","first_signal","latest_signal","span_days","in_booking_circuit","has_authority_record","has_seen_discogs_bind","institutionally_catalogued","has_listen_flow","institutional_visibility","listen_count","distinct_listener_count","last_listened_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["sync.placement_arrival_by_cluster_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":["string","null"],"pattern":"^[0-9a-f]{64}$"},"first_placement_year":{"type":["number","null"]},"grain":{"type":["string","null"]},"source_note":{"type":["string","null"]}},"required":["cluster_id_hex","first_placement_year","grain","source_note"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["wintermute.cluster_arrival_wide_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":["string","null"],"pattern":"^[0-9a-f]{64}$"},"listen_first_at":{"type":["string","null"]},"bandcamp_first_at":{"type":["string","null"]},"attention_first_at":{"type":["string","null"]},"authority_first_at":{"type":["string","null"]},"seen_event_first_at":{"type":["string","null"]},"seen_known_since":{"type":["string","null"]},"editorial_first_at":{"type":["string","null"]},"catalogue_first_at":{"type":["string","null"]},"any_alias_bridged":{"type":["boolean","null"]},"layers_present":{"type":["number","null"]},"placement_first_at":{"type":["string","null"]}},"required":["cluster_id_hex","listen_first_at","bandcamp_first_at","attention_first_at","authority_first_at","seen_event_first_at","seen_known_since","editorial_first_at","catalogue_first_at","any_alias_bridged","layers_present","placement_first_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["mirror.market_vs_flow_divergence_v1"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"representative_name":{"type":["string","null"]},"min_price":{"type":["number","null"]},"total_copies_for_sale":{"type":["number","null"]},"median_sales":{"type":["number","null"]},"median_price":{"type":["number","null"]},"distinct_sellers":{"type":["number","null"]},"listing_count":{"type":["number","null"]},"listen_count":{"type":["number","null"]},"pageview_views_recent":{"type":["number","null"]},"wantlist_demand":{"type":["number","null"]},"price_per_listen":{"type":["number","null"]},"supply_per_attention":{"type":["number","null"]},"computed_at":{"type":"string"}},"required":["cluster_id_hex","representative_name","min_price","total_copies_for_sale","median_sales","median_price","distinct_sellers","listing_count","listen_count","pageview_views_recent","wantlist_demand","price_per_listen","supply_per_attention","computed_at"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]},{"type":"object","properties":{"object":{"type":"string","enum":["surface.rows"]},"surface":{"type":"string","enum":["archive_api_v1.artist_press_mentions"]},"present":{"type":"boolean"},"state":{"type":"string","enum":["present","honest_gap","degraded"]},"keyspace":{"type":"string","enum":["pe-norm-v1-artist","pe-norm-v1-recording","pe-norm-v1-label"]},"liveness":{"type":"string","enum":["populated","advisory","empty"]},"rows":{"type":"array","items":{"type":"object","properties":{"cluster_id_hex":{"type":"string","pattern":"^[0-9a-f]{64}$"},"outlet":{"type":"string"},"title":{"type":["string","null"]},"url":{"type":"string"},"published_at":{"type":"string"},"extraction_confidence":{"type":["number","null"]},"source_repo":{"type":"string"}},"required":["cluster_id_hex","outlet","title","url","published_at","extraction_confidence","source_repo"]}},"next_after":{"type":["string","null"]},"coverage_note":{"type":"string"},"degraded_reason":{"type":["string","null"]},"generated_at":{"type":"string"}},"required":["object","surface","present","state","keyspace","liveness","rows","next_after","coverage_note","degraded_reason","generated_at"]}]}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getSurfaceRows"}},"/api/v2/tastemakers":{"get":{"summary":"Tastemaker leaderboard + ones-to-watch (machine-readable)","description":"crate's richest artist-grain analytics, from the offline-published S3 snapshot (NO DB checkout). Keyed; fail-soft (degraded → 200). ?limit= bounds each array (1..200).","tags":["Tastemakers"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Max rows (1..200)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Tastemaker leaderboard + ones-to-watch (or honest-gap state)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TastemakersResponse"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getTastemakers"}},"/api/v2/tastemakers/ones-to-watch":{"get":{"summary":"Ones-to-watch rising artists (machine-readable slice)","description":"The ones-to-watch slice on its own surface (same derived S3 read as /api/v2/tastemakers, NO DB checkout). Keyed; fail-soft (degraded → 200). ?limit= bounds the array (1..200).","tags":["Tastemakers"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Max rows (1..200)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Ones-to-watch rows (or honest-gap state)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnesToWatchResponse"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getOnesToWatch"}},"/api/v2/label/{key}":{"get":{"summary":"Canonical label dossier by cluster_id hex or slug","description":"The cluster-first canonical label address. {key} is a 64-hex label_cluster_id (resolves DIRECTLY, OBSERVED tier, never re-anchors) OR a name-slug. discogs:/mbid: → 400. Keyed; unresolved → 200 identity:null (honest-gap), not 404.","tags":["Dossier","Identity"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"key","in":"path"}],"responses":{"200":{"description":"Label dossier contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelDossierContract"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getLabelByKey"}},"/api/v2/dossier/artist/{slug}":{"get":{"summary":"Full per-artist dossier by slug (alias of /api/v2/artist/{key})","description":"The canonical artist dossier addressed by human slug; an alias of /api/v2/artist/{key} sharing the same handler (so it also accepts the v2 ?fields= sparse-fieldset). Keyed; unresolved → 200 identity:null (honest-gap).","tags":["Dossier","Identity"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","description":"Comma-separated top-level facets to KEEP (opt-out trim). Omit for the full dossier. Unknown field → 400 invalid_fields with the valid set + an example."},"required":false,"name":"fields","in":"query"}],"responses":{"200":{"description":"Artist dossier contract (default-rich; trimmed when ?fields= is supplied)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtistDossierContract"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getArtistDossier"}},"/api/v2/dossier/label/{slug}":{"get":{"summary":"Full per-label dossier by slug (alias of /api/v2/label/{key})","description":"The cluster-first label dossier addressed by human slug; an alias of /api/v2/label/{key} sharing the same handler. Keyed; unresolved → 200 identity:null (honest-gap).","tags":["Dossier"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Label dossier contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelDossierContract"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getLabelDossier"}},"/api/v2/dossier/festival/{slug}":{"get":{"summary":"Full per-festival dossier by canonical key","description":"The canonical festival dossier: identity (seen.festival_canonical) + consolidated editions⋈lineup, with a classified state + provenance. {slug} is the canonical_key. Keyed; unresolved → 200 identity:null (honest-gap).","tags":["Dossier"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Festival dossier contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FestivalDossierContract"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure (invalid query, malformed body, bad facet name)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database pool exhausted — retry after 5s","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Request deadline (15s) or query timeout exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getFestivalDossier"}},"/api/v2/dossier/manifest":{"get":{"summary":"Cross-grain dossier field manifest (data dictionary)","description":"Every field crate can expose for every v2 entity grain (artist/label/festival) with provenance + the contract endpoint per grain (v2-pathed). master is demoted (cluster-first; surfaced under unavailable_grains). Keyed, pure/static.","tags":["Dossier"],"responses":{"200":{"description":"Dossier field manifest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DossierManifest"}}},"headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getDossierManifest"}},"/api/v2/search-events/observed":{"post":{"summary":"Beacon: a search result was observed (cache-hit telemetry)","description":"Client beacon reporting a search result was observed from cache. Authenticated with the per-search beacon JWT (Authorization: Bearer), NOT the X-API-Key (the token is version-agnostic). Body ≤512 bytes; idempotent.","tags":["Beacon"],"security":[{"BeaconBearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObservedBeaconRequest"}}}},"responses":{"204":{"description":"Beacon accepted (idempotent no-op on duplicate)","headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure — `invalid_json`, `invalid_payload` (with `details` = Zod flattened error object), or `timestamp_skew` (with `skew_ms`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}},"401":{"description":"Beacon-token failure — `missing_token`, `invalid_token`, or `token_search_event_mismatch`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}},"413":{"description":"Payload too large (>512 bytes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}}},"operationId":"recordObservedBeacon"}},"/api/v2/search-events/refined":{"post":{"summary":"Beacon: a search was refined (facet-change telemetry)","description":"Client beacon reporting facet changes that refined a prior search (≤10 changed facets). Authenticated with the per-search beacon JWT (Authorization: Bearer), NOT the X-API-Key. Body ≤512 bytes; idempotent.","tags":["Beacon"],"security":[{"BeaconBearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefinedBeaconRequest"}}}},"responses":{"204":{"description":"Beacon accepted (idempotent no-op on duplicate)","headers":{"X-RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch (seconds) when the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failure — `invalid_json`, `invalid_payload` (with `details` = Zod flattened error object), or `timestamp_skew` (with `skew_ms`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}},"401":{"description":"Beacon-token failure — `missing_token`, `invalid_token`, or `token_search_event_mismatch`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}},"413":{"description":"Payload too large (>512 bytes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconError"}}}}},"operationId":"recordRefinedBeacon"}}},"webhooks":{},"x-concepts":[{"term":"cluster_id","eli5":"crate's one true name for an artist — a pe-norm-v1 hex string that collapses the same artist's Discogs, MusicBrainz, and Bandcamp identities into a single canonical key. Key all artist data off it. It's an opaque string: pass it through verbatim, never numericize it. cluster_id:null is an honest gap (couldn't resolve), not an error.","see":"IdentityResolution (resolveIdentity, GET /api/v2/resolve); ArtistDossierContract.cluster_id (GET /api/v2/artist/{key})"},{"term":"dossier","eli5":"The full picture crate composes for one entity by joining every fleet signal. Each facet carries a classified state plus a provenance manifest (producer, source table, refresh cadence, tier). Read /dossier/manifest to discover the whole field surface without calling every endpoint.","see":"DossierManifest (GET /api/v2/dossier/manifest); ArtistDossierContract / LabelDossierContract / FestivalDossierContract"},{"term":"grain","eli5":"Which kind of agent a dossier is about: artist, label, or festival. Releases are not a top-level grain in v2 — release/master detail attaches to the artist dossier's discography dimension. The dossier manifest also lists deliberately-unavailable grains (e.g. song — no fleet track key; and the demoted master grain).","see":"GET /api/v2/dossier/artist/{slug}, /api/v2/dossier/label/{slug}, /api/v2/dossier/festival/{slug}; ArtistDossierContract.discography; DossierManifest.unavailable_grains"},{"term":"honest gap","eli5":"An unresolved or empty lookup returns HTTP 200 with present:false, a null field, or state:\"honest_gap\" — this is NORMAL, not an error. crate shows what it can see and is explicit about what it can't, rather than 404-ing or faking data. Only 4xx/5xx are errors; branch on the body, not the status.","see":"Error schema (the inverse: only 4xx/5xx); ArtistDossierContract.identity:null; dossier facets with state:\"honest_gap\""},{"term":"resolved_via","eli5":"The binding TIER of a resolved identity: 'discogs' = canonical Discogs-bound (verified); 'cluster' = OBSERVED/unverified from the booking graph (surface flagged-unverified, never as canonical); null = unresolved. A 64-hex cluster_id address is always 'cluster' by design.","see":"IdentityResolution.resolved_via; ArtistDossierContract.resolved_via"},{"term":"resolved_from","eli5":"How you addressed the artist on /resolve: 'url' (pasted link), 'name', or 'locator' (a foreign id). Paired with matched_on (which surface matched) and note (why a recognized link didn't resolve).","see":"IdentityResolution.resolved_from (GET /api/v2/resolve)"},{"term":"cube / cube_quadrant","eli5":"crate's behavioral-signal model: a 3-bit string code (e.g. \"101\") placing a release on three axes — who OWNS it (collector), who PLAYS it (DJ), who WRITES about it (critic). Codes run \"000\" (no signal) to \"111\" (full intersection). null = not yet classified (honest gap). owner_count/dj_count/critic_count give the magnitude. You meet it on the result rows of /api/v2/search.","see":"ResultRow.cube_quadrant + owner_count/dj_count/critic_count + link_to_cube (GET /api/v2/search)"},{"term":"tastemakers","eli5":"A discovery surface of influential curators plus crate's richest artist-grain analytics (rank, own-tier, brokerage score, lead-times, Bandcamp demand). Served from an offline snapshot, fail-soft: state is present/empty/degraded (degraded = 200 honest-gap), stale=true if older than 7 days.","see":"TastemakersResponse (GET /api/v2/tastemakers); OnesToWatchResponse (GET /api/v2/tastemakers/ones-to-watch)"},{"term":"breakouts","eli5":"A discovery surface of emerging artists on the rise ('ones to watch') — booking momentum cross-validated against press. Filter by ?tier=breakout|rising and ?corroboration=corroborated|booking_ahead; ?limit= clamped to 200. Fail-soft state present/empty/degraded.","see":"BreakoutsResponse (GET /api/v2/breakouts)"},{"term":"beacons","eli5":"Client-side search-event telemetry (observed = cache hit, refined = facet change). Authenticated NOT with X-API-Key but with a short-lived per-search JWT bound to one search_event_id, sent as Authorization: Bearer. Bodies <=512 bytes, idempotent (duplicate = 204 no-op).","see":"ObservedBeaconRequest / RefinedBeaconRequest (POST /api/v2/search-events/observed, /refined); BeaconBearerAuth security scheme; BeaconError"},{"term":"fields","eli5":"The v2 artist dossier is default-rich (one round-trip). ?fields=identity,discography TRIMS to the named top-level facets (opt-out, not opt-in); omit it for the full dossier. An unknown field → 400 invalid_fields with the exact valid set + a copy-pasteable example.","see":"GET /api/v2/artist/{key}?fields=...; the invalid_fields error (valid set + example)"},{"term":"bandcamp dimensions","eli5":"In v2 Bandcamp is an ANALYTICAL dimension of the artist dossier (keyed by cluster_id), not a release surface: bandcamp_emergence = purchase-backed demand signals (emergence class, demand lead/ratio, owner reach, wishlist demand), bandcamp_tastemaker = early-supporter quality scores. Per-release Bandcamp listings (and bandcamp_item_id / track_url) are NOT a v2 surface — they belong to the v1 Bandcamp endpoints.","see":"ArtistDossierContract.bandcamp_emergence / bandcamp_tastemaker (signals; GET /api/v2/artist/{key})"},{"term":"link-only (artwork + streams)","eli5":"crate is link-only wherever it carries links: every artwork item is a hotlink url with rehost:false — crate never fetches or re-hosts bytes (a Cover Art Archive URL is best-effort, may 404). crate never stores Bandcamp audio streams (tokenized, expiring, out of ToS).","see":"ArtworkItem (rehost:false) on the dossiers"},{"term":"opaque ids","eli5":"cluster_id is always a string and always opaque — round-trip it verbatim, never parse, increment, or numericize. The same discipline applies to any id crate hands you. This keeps clients correct across id-scheme changes.","see":"IdentityResolution.cluster_id; ArtistDossierContract.cluster_id"},{"term":"versioning","eli5":"The API major version is the URL path (/api/v2). The spec's info.version (2.33.0) bumps on every spec change and is drift-guarded because the document is generated from code. v2 is the cluster-first stable major; the frozen v1 predecessor is in a time-boxed deprecation (migration map at /docs/migration/v1-to-v2). Operations carry stable operationIds; keyed 2xx responses declare X-RateLimit-* headers.","see":"info.version; GET /api/v2/openapi.json; the migration guide at /docs/migration/v1-to-v2"},{"term":"surface registry","eli5":"A generic cluster-keyed read layer over raw producer tables crate hasn't yet composed into a bespoke dossier facet (cycle-096; carrefour#135): GET /api/v2/surface lists every registered surface (grain, key, keyspace, keyset, cap); GET /api/v2/surface/{name}?cluster=<hex64> reads it, keyset-paginated via an opaque ?after cursor. Fail-closed by construction — a column the registry doesn't allowlist can never appear on the wire. state:'degraded' (still HTTP 200) means the dedicated read pool isn't provisioned yet.","see":"GET /api/v2/surface (getSurfaceIndex); GET /api/v2/surface/{name} (getSurfaceRows)"}]}