A master (release-group) dossier, addressed under its artist
const url = 'https://crate.hosaka.fm/api/v2/artist/example/master/example';const options = {method: 'GET', headers: {'X-API-Key': '<X-API-Key>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://crate.hosaka.fm/api/v2/artist/example/master/example \ --header 'X-API-Key: <X-API-Key>'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.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Responses
Section titled “ Responses ”The master dossier (present:true) or an honest-gap (present:false — unknown, or not filed under this artist)
object
object
object
object
object
object
object
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.
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.
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).
Delivered pixel height — see width.
object
object
object
Example
{ "object": "master.dossier", "present": true, "master": { "grain": "master", "artwork": [ { "source": "bandcamp", "grain": "artist", "rehost": false } ] }}Headers
Section titled “Headers ”Requests allowed in the current window.
Requests remaining in the current window.
Unix epoch (seconds) when the current window resets.
Validation failure (invalid query, malformed body, bad facet name)
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".
| code | HTTP | when thrown | fix |
|---|---|---|---|
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} |
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) |
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) |
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) |
missing_locator | 400 | /resolve called with none of q/cluster/discogs/mbid | pass exactly one locator |
invalid_locator | 400 | a /resolve locator is malformed for its type | fix the format, or fall back to ?q=<name> |
invalid_query | 400 | /search ?q= missing/empty, or any Zod validation failure (details[] attached) | pass ?q=<text>; fix each details entry |
invalid_facet | 400 | an unknown facet filter name was supplied | GET /api/v2/facets for valid names + values |
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 |
object
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.
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).
Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with next.
Deep link to this code’s docs: https://crate.hosaka.fm/docs/api#error-. Auto-populated for catalogued codes.
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.
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.
Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached.
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.
Echoed on master_not_found (404) — the master id that did not resolve.
Example
{ "error": "invalid_query"}Authentication failure
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".
| code | HTTP | when thrown | fix |
|---|---|---|---|
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} |
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) |
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) |
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) |
missing_locator | 400 | /resolve called with none of q/cluster/discogs/mbid | pass exactly one locator |
invalid_locator | 400 | a /resolve locator is malformed for its type | fix the format, or fall back to ?q=<name> |
invalid_query | 400 | /search ?q= missing/empty, or any Zod validation failure (details[] attached) | pass ?q=<text>; fix each details entry |
invalid_facet | 400 | an unknown facet filter name was supplied | GET /api/v2/facets for valid names + values |
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 |
object
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.
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).
Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with next.
Deep link to this code’s docs: https://crate.hosaka.fm/docs/api#error-. Auto-populated for catalogued codes.
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.
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.
Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached.
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.
Echoed on master_not_found (404) — the master id that did not resolve.
Example
{ "error": "invalid_query"}Rate limit exceeded — see Retry-After + X-RateLimit-* headers
object
Example
{ "error": "rate_limited"}Internal server error
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".
| code | HTTP | when thrown | fix |
|---|---|---|---|
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} |
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) |
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) |
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) |
missing_locator | 400 | /resolve called with none of q/cluster/discogs/mbid | pass exactly one locator |
invalid_locator | 400 | a /resolve locator is malformed for its type | fix the format, or fall back to ?q=<name> |
invalid_query | 400 | /search ?q= missing/empty, or any Zod validation failure (details[] attached) | pass ?q=<text>; fix each details entry |
invalid_facet | 400 | an unknown facet filter name was supplied | GET /api/v2/facets for valid names + values |
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 |
object
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.
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).
Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with next.
Deep link to this code’s docs: https://crate.hosaka.fm/docs/api#error-. Auto-populated for catalogued codes.
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.
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.
Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached.
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.
Echoed on master_not_found (404) — the master id that did not resolve.
Example
{ "error": "invalid_query"}Database pool exhausted — retry after 5s
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".
| code | HTTP | when thrown | fix |
|---|---|---|---|
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} |
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) |
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) |
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) |
missing_locator | 400 | /resolve called with none of q/cluster/discogs/mbid | pass exactly one locator |
invalid_locator | 400 | a /resolve locator is malformed for its type | fix the format, or fall back to ?q=<name> |
invalid_query | 400 | /search ?q= missing/empty, or any Zod validation failure (details[] attached) | pass ?q=<text>; fix each details entry |
invalid_facet | 400 | an unknown facet filter name was supplied | GET /api/v2/facets for valid names + values |
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 |
object
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.
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).
Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with next.
Deep link to this code’s docs: https://crate.hosaka.fm/docs/api#error-. Auto-populated for catalogued codes.
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.
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.
Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached.
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.
Echoed on master_not_found (404) — the master id that did not resolve.
Example
{ "error": "invalid_query"}Request deadline (15s) or query timeout exceeded
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".
| code | HTTP | when thrown | fix |
|---|---|---|---|
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} |
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) |
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) |
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) |
missing_locator | 400 | /resolve called with none of q/cluster/discogs/mbid | pass exactly one locator |
invalid_locator | 400 | a /resolve locator is malformed for its type | fix the format, or fall back to ?q=<name> |
invalid_query | 400 | /search ?q= missing/empty, or any Zod validation failure (details[] attached) | pass ?q=<text>; fix each details entry |
invalid_facet | 400 | an unknown facet filter name was supplied | GET /api/v2/facets for valid names + values |
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 |
object
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.
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).
Actionable remediation in human terms — what to DO next, often naming the exact endpoint (a template with next.
Deep link to this code’s docs: https://crate.hosaka.fm/docs/api#error-. Auto-populated for catalogued codes.
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.
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.
Structured validation breakdown — on Zod 400s (invalid_query), an array of { path, message }, one per failed field. Present only when validation specifics are attached.
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.
Echoed on master_not_found (404) — the master id that did not resolve.
Example
{ "error": "invalid_query"}