Skip to content

canonical ​

56 endpoints at a glance
MethodPathSummary
POST/admin/canonical/albums/{target_id}/mergeAdmin Merge Albums
POST/admin/canonical/artists/{target_id}/mergeAdmin Merge Artists
GET/admin/canonical/duplicatesAdmin Canonical Duplicates
GET/admin/canonical/enrich/album-track-coverageAdmin Album Track Coverage
POST/admin/canonical/enrich/artist-reprojectAdmin Enrich Artist Reproject
GET/admin/canonical/enrich/artist-reproject-coverageAdmin Artist Reproject Coverage
POST/admin/canonical/enrich/audio-qualityAdmin Enrich Audio Quality
GET/admin/canonical/enrich/audio-quality-coverageAdmin Audio Quality Coverage
GET/admin/canonical/enrich/deezer-coverageAdmin Deezer Coverage
GET/admin/canonical/enrich/discogs-coverageAdmin Discogs Coverage
POST/admin/canonical/enrich/discogs-discoverAdmin Discogs Discover
POST/admin/canonical/enrich/discogs-reprojectAdmin Discogs Reproject
POST/admin/canonical/enrich/discover-deezerAdmin Discover Deezer
POST/admin/canonical/enrich/discover-itunesAdmin Discover Itunes
DELETE/admin/canonical/enrich/errorsAdmin Clear Enrich Errors
GET/admin/canonical/enrich/errorsAdmin Enrich Errors
POST/admin/canonical/enrich/hydrate-mbAdmin Hydrate Mb
GET/admin/canonical/enrich/itunes-coverageAdmin Itunes Coverage
POST/admin/canonical/enrich/link-album-tracksAdmin Link Album Tracks
POST/admin/canonical/enrich/lyricsAdmin Enrich Lyrics
GET/admin/canonical/enrich/lyrics-coverageAdmin Lyrics Coverage
GET/admin/canonical/enrich/mb-coverageAdmin Mb Coverage
POST/admin/canonical/enrich/mb-creditsAdmin Enrich Mb Credits
GET/admin/canonical/enrich/mb-credits-coverageAdmin Mb Credits Coverage
POST/admin/canonical/enrich/mb-relationsAdmin Enrich Mb Relations
GET/admin/canonical/enrich/mb-relations-coverageAdmin Mb Relations Coverage
POST/admin/canonical/enrich/promote-lastfm-mbidsAdmin Promote Lastfm Mbids
POST/admin/canonical/enrich/tagsAdmin Enrich Tags
GET/admin/canonical/enrich/tags-coverageAdmin Tags Coverage
POST/admin/canonical/migrateAdmin Run Migration
POST/admin/canonical/rebuildAdmin Run Rebuild
GET/admin/canonical/statsAdmin Canonical Stats
POST/admin/canonical/tracks/{target_id}/mergeAdmin Merge Tracks
GET/albums/{album_id}Get Album
GET/albums/{album_id}/deezer-releasesAlbum Other Deezer Releases
POST/albums/{album_id}/refsLink Album Source
DELETE/albums/{album_id}/refs/{source}/{external_id}Unlink Album Source
GET/albums/{album_id}/tracksGet Album Tracks
POST/albums/refs/checkCheck Album Refs
GET/albums/resolveResolve Album Endpoint
GET/albums/searchSearch Albums
GET/artists/{artist_id}Get Artist
GET/artists/{artist_id}/discographyGet Artist Discography
POST/artists/{artist_id}/refsLink Artist Source
DELETE/artists/{artist_id}/refs/{source}/{external_id}Unlink Artist Source
GET/artists/{artist_id}/relatedGet Artist Related
POST/artists/refs/checkCheck Artist Refs
GET/artists/resolveResolve Artist Endpoint
GET/artists/searchSearch Artists
GET/tracks/{track_id}Get Track
GET/tracks/{track_id}/lyricsGet Track Lyrics
POST/tracks/{track_id}/refsLink Track Source
DELETE/tracks/{track_id}/refs/{source}/{external_id}Unlink Track Source
POST/tracks/refs/checkCheck Track Refs
GET/tracks/resolveResolve Track Endpoint
GET/tracks/searchSearch Tracks

canonical​


Search Artists​

GET
/artists/search

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

q*
Type
string
Required
limit
Type
integer
Default
10

Responses​

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground​

Headers
Variables
Key
Value

Samples​


Search Albums​

GET
/albums/search

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

q*
Type
string
Required
limit
Type
integer
Default
10

Responses​

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground​

Headers
Variables
Key
Value

Samples​


Search Tracks​

GET
/tracks/search

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

q*
Type
string
Required
limit
Type
integer
Default
10

Responses​

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground​

Headers
Variables
Key
Value

Samples​


Resolve Album Endpoint​

GET
/albums/resolve

Resolve any source identifier(s) to the canonical album.

When deezer=N is supplied and no canonical exists yet, the
endpoint seeds the canonical by fetching + caching the Deezer
album (which triggers the eager canonical reconcile via the catalog
upsert hook), then re-resolves and returns. This makes click-through
from Deezer-fallback search results a single HTTP call — no
"ensure-then-resolve" dance on the client.

lastfm=<artist::title> works the same way: we fetch via the
LastFM dedup layer (which writes the cache + triggers the
canonical reconcile), then re-resolve. Used by the artist-
discography import flow for LastFM-only fallback rows.

Returns the canonical doc + every known external ref. 404 if we
can't resolve from any provided signal (e.g. unknown MBID with no
Deezer ID to seed from).

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

deezer
mbid
upc
plex_server
plex_rating_key
lastfm

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Album​

GET
/albums/{album_id}

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

album_id*
Type
string
Required

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Album Tracks​

GET
/albums/{album_id}/tracks

Canonical tracklist for an album — source-agnostic.

Returns the album's tracks straight from the canonical media_tracks
collection (keyed by album_id), so an album gets a tracklist
whatever source minted those tracks — Plex, MusicBrainz, Spotify,
iTunes or Deezer — rather than only when a live Deezer fetch is
available. Ordered by disc then track number (nulls last).

Follows the album's merged_into chain and excludes merged-away
tracks. Each row carries what the SPA needs to render + route
playback: sources (Plex-playability gate) and preview_url /
deezer_id for the 30s-preview fallback.

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

album_id*
Type
string
Required

Responses​

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground​

Headers
Variables
Key
Value

Samples​


Resolve Artist Endpoint​

GET
/artists/resolve

Resolve-or-seed. See resolve_album_endpoint for the pattern.

lastfm=<lowercased_name> mints a LastFM-only canonical when no
other source has the artist (used by the related-artist import
flow). Composite shape NOT applicable for artists — just the
lowercased name (see :func:resolver.lastfm_artist_id).

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

deezer
mbid
plex_server
plex_rating_key
lastfm

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Artist​

GET
/artists/{artist_id}

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

artist_id*
Type
string
Required

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Artist Discography​

GET
/artists/{artist_id}/discography

Grouped + classified discography for an artist.

Canonical albums first (full sources bitmap, primary_type +
secondary_types carried through). Augmented with MusicBrainz
release-groups, Deezer artist albums, and Last.fm top-albums that
aren't yet in the canonical layer — each surfaced as a fallback
card with a + Import affordance on the SPA.

Five buckets: Studio / Live / Compilations / EPs & Singles /
Other. Year-descending within each. See
:mod:api.canonical.discography for the pipeline.

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

artist_id*
Type
string
Required

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Artist Related​

GET
/artists/{artist_id}/related

Related artists (Deezer + LastFM, deduped) + MB affiliations.

sort ∈ {relevance, alpha} — default ranks multi-source +
high-match first; alpha is straight alphabetical. in_library_only
drops fallback rows so the user can see only canonicals.

Affiliations are MB structural relationships (member-of, subgroup,
founder, collaborator, tribute, family) — always returned, never
filtered, since they're factual rather than fuzzy.

See :mod:api.canonical.related for the pipeline.

Header Parameters

authorization
x-api-key

Path Parameters

artist_id*
Type
string
Required

Query Parameters

sort
Type
string
Default
"relevance"
in_library_only
Type
boolean
Default
false

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}
Headers
Variables
Key
Value

Resolve Track Endpoint​

GET
/tracks/resolve

Resolve-or-seed. See resolve_album_endpoint for the pattern.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

deezer
mbid
isrc
plex_server
plex_rating_key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Track​

GET
/tracks/{track_id}

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

track_id*
Type
string
Required

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Get Track Lyrics​

GET
/tracks/{track_id}/lyrics

Lyrics for a canonical track (plain + parsed synced lines).

Reads the lyrics_cache. On a cache miss (or a stale negative
hit), enrich=true does a one-off on-demand fetch (Plex harvest →
LRCLIB) so opening a never-seen track Just Works; the bulk drain
handles coverage at scale. Returns a not_found envelope rather
than 404 when there genuinely are no lyrics, so the SPA can render an
empty state without treating it as an error.

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

track_id*
Type
string
Required

Query Parameters

enrich
Type
boolean
Default
true

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Album Other Deezer Releases​

GET
/albums/{album_id}/deezer-releases

Other Deezer releases of this album.

Deezer issues more than one album id per release — regional
editions, reissues, a deluxe cut. Whichever one you linked, the
artist listing may answer with a different one, and that used to
surface as a phantom "Deezer-only" row beside your album in the
discography. Those rows are suppressed now, which also removed the
accidental way the extra ids got attached (clicking one ran the
fallback import). This is the deliberate replacement.

Candidates come from the artist's Deezer album listing rather than a
text search, so what is offered here is exactly the set the
discography would otherwise have hidden. Ids already on this
canonical are omitted — the question is what else exists.

Picker-shaped rows, so the existing link modal renders them; the
caller links one with POST /albums/{album_id}/refs as usual and
gets the same 409-on-conflict behaviour.

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

album_id*
Type
string
Required

Query Parameters

limit
Type
integer
Default
25

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Link Album Source​

POST
/albums/{album_id}/refs

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

album_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Unlink Album Source​

DELETE
/albums/{album_id}/refs/{source}/{external_id}

Header Parameters

authorization
x-api-key

Path Parameters

album_id*
Type
string
Required
source*
Type
string
Required
external_id*
Type
string
Required

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}
Headers
Variables
Key
Value

Link Artist Source​

POST
/artists/{artist_id}/refs

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

artist_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Unlink Artist Source​

DELETE
/artists/{artist_id}/refs/{source}/{external_id}

Header Parameters

authorization
x-api-key

Path Parameters

artist_id*
Type
string
Required
source*
Type
string
Required
external_id*
Type
string
Required

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}
Headers
Variables
Key
Value

Link Track Source​

POST
/tracks/{track_id}/refs

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

track_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Unlink Track Source​

DELETE
/tracks/{track_id}/refs/{source}/{external_id}

Header Parameters

authorization
x-api-key

Path Parameters

track_id*
Type
string
Required
source*
Type
string
Required
external_id*
Type
string
Required

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}
Headers
Variables
Key
Value

Check Album Refs​

POST
/albums/refs/check

Parameters​

Header Parameters

authorization
x-api-key

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_ids": [
  
  
"string"
  
]
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Body

Samples​


Check Artist Refs​

POST
/artists/refs/check

Parameters​

Header Parameters

authorization
x-api-key

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_ids": [
  
  
"string"
  
]
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Body

Samples​


Check Track Refs​

POST
/tracks/refs/check

Parameters​

Header Parameters

authorization
x-api-key

Request Body​

application/json
JSON
{
  
"source": "string",
  
"external_ids": [
  
  
"string"
  
]
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Body

Samples​


Admin Canonical Duplicates​

GET
/admin/canonical/duplicates

Library-wide duplicate-canonical groups for cleanup.

Aggregates media_artists (or media_albums / media_tracks)
by the dedup key — name_folded for artists, (artist_id, title_folded) for albums/tracks (artist-scoped so "Greatest Hits"
by different artists doesn't collapse). Returns groups with ≥2
non-tombstoned candidates, ordered by candidate-count desc so the
worst offenders surface first.

Each candidate carries the data the SPA needs to render a row
(picture/cover, source_count, sources bitmap, year). Pagination
is via page + limit; has_more indicates whether
more pages exist.

q is an optional substring filter applied BEFORE the
$group stage so it searches the whole collection — not just
the current page. For artists it matches name_folded; for
albums/tracks it matches title_folded OR the raw
artist_name (case-insensitive) so the user can find duplicates
by either title or performer.

No scrobble-count enrichment in v1 — would require a per-
candidate $lookup which dominates query time for canonicals with
many scrobbles. Can add later as a separate enrichment endpoint
called only when the user expands a group.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

kind*
Type
string
Required
page
Type
integer
Default
1
limit
Type
integer
Default
25
q

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Merge Albums​

POST
/admin/canonical/albums/{target_id}/merge

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

target_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Admin Merge Artists​

POST
/admin/canonical/artists/{target_id}/merge

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

target_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Admin Merge Tracks​

POST
/admin/canonical/tracks/{target_id}/merge

Parameters​

Header Parameters

authorization
x-api-key

Path Parameters

target_id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"source_id": "string"
}

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value
Body

Samples​


Admin Run Migration​

POST
/admin/canonical/migrate

Full one-shot migration: backfill canonical + re-key user data.

Use after deploying the canonical layer onto an environment with
existing legacy user-data rows (those still carrying deezer_id
instead of canonical_id). Idempotent — safe to re-run; the
backfill phase just refreshes already-canonicalized entities.

Fire-and-forget: full migrations walk every source-cache entity
(typically thousands of docs) so they routinely take minutes.
The SPA polls /admin/task-history/{task_id} for progress.

Safety: source caches (lastfm_scrobbles, plex_*,
deezer_*, musicbrainz_*) are read-only during the entire
run. The only deletions are unresolvable rows in the user-action
collections (media_album_status + media_favorites). See
:mod:api.canonical.migrate for the full safety contract.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Run Rebuild​

POST
/admin/canonical/rebuild

Backfill-only: recompute every canonical doc from current sources.

Use after editing the priority tables or projector functions when
the existing canonical entities' merged shape needs to catch up.
Doesn't touch user-data (wishlist / owned / favorites) — those
already point at canonical IDs. Much cheaper than /migrate when
you only need to refresh the projection.

Fire-and-forget — see admin_run_migration for the rationale.

Safety: source caches are read-only; nothing is ever deleted. This
endpoint only writes to media_* and *_external_refs.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Canonical Stats​

GET
/admin/canonical/stats

Health view of the canonical layer.

Returns counts per canonical collection, total external refs, plus
a breakdown of refs by source (so admins can see "we know about N
Deezer albums but only M MusicBrainz release-groups"). Cheap reads
only — runs on every Migrations panel render.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Mb Coverage​

GET
/admin/canonical/enrich/mb-coverage

Per-kind MusicBrainz coverage: total canonicals, how many have an
MBID available to hydrate from, how many are already MB-cached.

The gap (with_mb_ref − mb_entities_cached) is the hydration backlog
"Hydrate all" drains.

mb_coverage is synchronous PyMongo (a few bulk reads + an
in-memory join). We offload it to a worker thread so it never blocks
the event loop — otherwise this admin page, which fires
/musicbrainz/admin/health and this endpoint together, would stall
the sibling request behind it (the cause of the earlier 15s timeout).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Hydrate Mb​

POST
/admin/canonical/enrich/hydrate-mb

Kick off MB hydration for kind as a background task.

Fetches the MusicBrainz entity for every canonical that carries an
MB ref (e.g. stamped from a Plex mbid:// Guid) but has no cached
MB data yet. Each fetch caches the entity and fires the existing
reconcile, so the canonical re-merges with real MB metadata.

drain=False (default): one limit-sized batch (capped 1000) —
a quick top-up. drain=True: the "hydrate all" path — a single
task that walks the entire backlog (bounded by
HYDRATE_DRAIN_CAP) so the user doesn't have to click repeatedly.

MusicBrainz-throttled (1 rps); offloaded to a background task and
polled via /admin/task-history/{task_id}. Re-run to continue;
already-hydrated refs are skipped.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

kind
Type
string
Default
"track"
limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Promote Lastfm Mbids​

POST
/admin/canonical/enrich/promote-lastfm-mbids

Promote LastFM-held MBIDs to MusicBrainz refs for kind.

For canonicals minted from LastFM that carry an MBID in their
LastFM cache doc but no MusicBrainz ref, stamp the MB ref onto the
existing canonical (conflict-guarded — skips MBIDs already owned by
another canonical) and fetch the entity, so the canonical re-merges
with real MB data.

Only artist and track are eligible — LastFM album MBIDs are
release-vs-release-group-ambiguous and are handled in the discovery
phase instead.

MusicBrainz-throttled background task; poll
/admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

kind
Type
string
Default
"track"
limit
Type
integer
Default
200

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Deezer Coverage​

GET
/admin/canonical/enrich/deezer-coverage

Per-kind canonical-out Deezer coverage: total canonicals, how many
are Deezer-linked, how many are discoverable now (have an ISRC / UPC,
or — for artists — a Deezer-linked sibling).

Synchronous PyMongo offloaded to a worker thread so it never blocks
the event loop (see admin_mb_coverage).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Discover Deezer​

POST
/admin/canonical/enrich/discover-deezer

Attach Deezer entities to canonicals by exact identifier.

Tracks match on ISRC, albums on UPC, artists cascade off a
Deezer-linked sibling — each stamped onto the existing canonical
(conflict-guarded), no name guessing. drain=True walks the whole
backlog in one task. Deezer-paced background task; poll
/admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

kind
Type
string
Default
"track"
limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Itunes Coverage​

GET
/admin/canonical/enrich/itunes-coverage

Per-kind canonical-out iTunes coverage: total canonicals, how many
are iTunes-linked, how many are discoverable now (album has a UPC,
track's album is iTunes-linked, or artist has an iTunes-linked sibling).

Synchronous PyMongo offloaded to a worker thread so it never blocks
the event loop (see admin_mb_coverage).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Discover Itunes​

POST
/admin/canonical/enrich/discover-itunes

Attach iTunes entities to canonicals.

Albums match on UPC (exact barcode), artists cascade off an
iTunes-linked sibling, tracks match album-position-wise within an
already-linked album — each stamped onto the existing canonical
(conflict-guarded). drain=True walks the whole backlog in one
task. iTunes-paced (~3s/candidate, 20 req/min cap); poll
/admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

kind
Type
string
Default
"album"
limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Album Track Coverage​

GET
/admin/canonical/enrich/album-track-coverage

Per-source linkable backlog for album-anchored track linking: how
many albums carry the source ref, how many of those have unlinked
tracks, and the unlinked-track upper bound a "Link all" run targets.

Synchronous PyMongo offloaded to a worker thread (see
admin_mb_coverage).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Link Album Tracks​

POST
/admin/canonical/enrich/link-album-tracks

Link an album's unlinked tracks to Deezer / MusicBrainz by matching
the source's tracklist to ours by (disc, track) position.

Edition-gated: a whole album is skipped unless its source tracklist
has matching track + disc counts and aligned titles — so partial /
wrong-edition mislinks don't happen. Harvests ISRCs as a side effect
(the matched track's full entity is fetched + re-merged).
title_fallback=True additionally links, inside albums the
edition gate rejects, tracks whose title is unique on both sides —
no position and no edition assumption. That is what links a local
27-track rip against a 25-track streaming edition, which the gate
alone can never do. Off by default: it is looser evidence, and the
summary counts it separately (title_matched).

drain=True walks the whole backlog. Source-paced background task;
poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

source
Type
string
Default
"deezer"
limit
Type
integer
Default
200
drain
Type
boolean
Default
false
title_fallback
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Enrich Errors​

GET
/admin/canonical/enrich/errors

Deduped log of individual enrichment calls that failed.

One row per <source>:<kind>:<canonical_id> (the repeat-failing
entity, not a pile of duplicate rows) — so you can see which calls
are broken, not just an aggregate count. Rows clear themselves when
the entity later enriches successfully. source is the operation
(hydrate_mb / promote_lastfm / deezer_discover /
itunes_discover / isrc_link). Returns the rows plus a
counts map (per-source totals) for the section badges.

Synchronous PyMongo offloaded to a worker thread so it never blocks
the event loop.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

source
kind
limit
Type
integer
Default
200

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Clear Enrich Errors​

DELETE
/admin/canonical/enrich/errors

Clear the enrichment error log — all of it, or one source.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

source

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Lyrics Coverage​

GET
/admin/canonical/enrich/lyrics-coverage

Library-wide lyrics coverage: eligible tracks, how many have
lyrics (and synced), instrumentals, and the not-yet-tried backlog.

Synchronous PyMongo offloaded to a worker thread (see
admin_mb_coverage).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Lyrics​

POST
/admin/canonical/enrich/lyrics

Fill the per-track lyrics cache (Plex harvest first, then LRCLIB).

drain=True walks the whole backlog in one task. LRCLIB-paced
background task; poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Mb Relations Coverage​

GET
/admin/canonical/enrich/mb-relations-coverage

How many MB-linked canonical artists have had their relations
(external links + members) fetched vs. the remaining backlog.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Mb Relations​

POST
/admin/canonical/enrich/mb-relations

Backfill MB external links + band membership onto canonical artists.

Re-fetches each MB-linked artist with the relation includes (MB-paced,
~1 req/s), re-projecting external_links / members /
member_of. drain=True walks the whole backlog in one task.
Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Mb Credits Coverage​

GET
/admin/canonical/enrich/mb-credits-coverage

How many MB-linked canonical tracks have had their writer credits
resolved vs. the remaining backlog.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Mb Credits​

POST
/admin/canonical/enrich/mb-credits

Backfill composer/lyricist/writer credits onto canonical tracks.

Walks recording→work→artist (≥2 MB requests per track, MB-paced), so
this is the heaviest enrich drain. drain=True walks the whole
backlog. Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
100
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Audio Quality Coverage​

GET
/admin/canonical/enrich/audio-quality-coverage

How many Plex-sourced canonical tracks carry an audio_quality
descriptor vs. the remaining (un-reprojected) backlog.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Audio Quality​

POST
/admin/canonical/enrich/audio-quality

Re-project local-file audio quality (codec / bit-depth / lossless)
onto Plex-sourced canonical tracks. No network — reads the already-
cached Plex mirror — so it's fast. drain=True walks the whole
backlog. Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
1000
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Tags Coverage​

GET
/admin/canonical/enrich/tags-coverage

How many canonical tracks + albums carry the unified tags field
vs. the remaining (un-reprojected) backlog.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Tags​

POST
/admin/canonical/enrich/tags

Re-project the unified tags / moods / styles onto canonical tracks

  • albums. No network — combines already-cached Plex / MusicBrainz /
    LastFM / AcousticBrainz data — so it's fast. drain=True walks the
    whole backlog. Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
1000
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Artist Reproject Coverage​

GET
/admin/canonical/enrich/artist-reproject-coverage

How many canonical artists have been re-projected (carry the
alt_names field) vs. the remaining backlog.

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Enrich Artist Reproject​

POST
/admin/canonical/enrich/artist-reproject

Re-project canonical artists from the already-cached source data.

No network, so fast: MusicBrainz alternate names, and — for any
artist whose cached MB row carries the relation includes — external
links and band membership too, re-parsed from raw. Only a row
that never had the includes needs the paced MB-relations backfill.

drain=True walks the whole backlog.

force=True re-projects every artist, not just those never
projected before. The coverage marker records that a re-projection
happened, not which projectors it ran, so without this a projector
fix reaches nobody while coverage reports 100%. Use it after changing
what a projector emits.

Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
2000
drain
Type
boolean
Default
false
force
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Discogs Coverage​

GET
/admin/canonical/enrich/discogs-coverage

Coverage for both Discogs backfills: barcode auto-link (UPC-bearing
albums → Discogs-linked) and re-project (counts of Discogs-linked
albums + artists).

Parameters​

Header Parameters

authorization
x-api-key

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers

Samples​


Admin Discogs Discover​

POST
/admin/canonical/enrich/discogs-discover

Barcode auto-link: attach Discogs releases to UPC-bearing albums.

MusicBrainz-style network drain (Discogs paces to ~1 req/s). drain
walks the whole backlog. Poll /admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
200
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Admin Discogs Reproject​

POST
/admin/canonical/enrich/discogs-reproject

Re-project Discogs-linked albums + artists from already-cached data.
No network — fast. drain walks the whole backlog. Poll
/admin/task-history/{task_id}.

Parameters​

Header Parameters

authorization
x-api-key

Query Parameters

limit
Type
integer
Default
1000
drain
Type
boolean
Default
false

Responses​

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground​

Headers
Variables
Key
Value

Samples​


Media — Blue Sapphire Software.