Skip to content

spotify

9 endpoints at a glance
MethodPathSummary
GET/spotify/auth/callbackAuth Callback
DELETE/spotify/meUnlink Spotify
GET/spotify/meGet My Spotify Link
POST/spotify/me/auth/startStart Auth
POST/spotify/me/import-favoritesImport Liked Songs To Favorites
GET/spotify/me/playlistsList My Playlists
GET/spotify/me/playlists/{spotify_playlist_id}Get My Playlist
GET/spotify/me/playlists/{spotify_playlist_id}/plex-coverageGet Playlist Plex Coverage
POST/spotify/me/syncSync My Library

spotify


Get My Spotify Link

GET
/spotify/me

Return the caller's Spotify identity, or {link: null} if
not linked.

Mirrors the shape of GET /plex/me so the SPA can use the same
"is this service connected?" pattern.

Fetches fresh from Mongo rather than trusting the dependency dict
— Spotify access tokens rotate (refresh flow writes back to prefs
on every refresh), so a request-scoped snapshot can lag behind
reality. The cost is one extra single-doc read per call which is
negligible for a per-user endpoint.

Successful Response

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

Unlink Spotify

DELETE
/spotify/me

Drop all Spotify creds + identity from the caller's prefs.

Doesn't revoke on Spotify's side — that's only possible by the
user visiting accounts.spotify.com/apps. The refresh token becomes
inert as soon as they do; until then we just stop using it.

Successful Response

application/json
JSON
{
  
"additionalProperties": true
}

Start Auth

POST
/spotify/me/auth/start

Begin the OAuth flow — returns the URL the SPA should redirect to.

The SPA does the actual navigation; we don't 302 directly because
that would lose the user's session cookie context (Spotify needs
to redirect back to our origin, where the same cookie is in
play).

Parameters

Responses

Successful Response

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

Playground

Samples


Auth Callback

GET
/spotify/auth/callback

OAuth callback — Spotify redirects here after the user consents.

Three possible shapes:

  • ?code=...&state=... — success path; exchange and persist.
  • ?error=... — user clicked Cancel on Spotify's consent
    screen, or Spotify rejected the request. Redirect with the
    reason so the Settings view can show a meaningful message.
  • Missing everything — someone hit the URL directly. 400.

On any failure we redirect to the SPA's Settings page with a
spotify=error&reason=... query so the user sees the failure
on the same page they started the flow.

Parameters

Query Parameters

code
state
error

Responses

Successful Response

application/json
JSON
[
]

Playground

Variables
Key
Value

Samples


Sync My Library

POST
/spotify/me/sync

Kick off a Spotify library + playlists + recently-played sync.

Fire-and-forget: returns a task_id immediately so the SPA's
HTTP client doesn't time out while the sync walks pages of saved
tracks / albums / followed-artists / playlists. The SPA polls
GET /admin/task-history/{task_id} (or uses the
useBackgroundTask composable) to surface progress + the
final summary.

Sync time is roughly linear in (saved-tracks + Σ playlist tracks),
plus one /me/player/recently-played call. A typical user with ~1k
saved tracks + 30 playlists lands in 30–60 seconds — well past
the SPA's 15s axios timeout, hence the background-task shape.

412 if the user hasn't linked Spotify.

Parameters

Responses

Successful Response

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

Playground

Samples


Get Playlist Plex Coverage

GET
/spotify/me/playlists/{spotify_playlist_id}/plex-coverage

Report how many tracks in a Spotify playlist resolve to Plex.

Used by the SPA's "preview before sync" UI: before turning a
Spotify playlist into a Plex playlist, the user sees X of Y tracks have a Plex match; Z are Spotify-only. Doesn't touch
Plex — everything resolves from local refs.

The special id __liked_songs__ reports against the user's
Spotify Liked Songs (which lives in spotify_saved_tracks,
not spotify_playlists).

Parameters

Path Parameters

spotify_playlist_id*
Type
string
Required

Responses

Successful Response

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

Playground

Variables
Key
Value

Samples


List My Playlists

GET
/spotify/me/playlists

List the caller's Spotify playlists from the local mirror.

owned_only=True (default) hides playlists the user follows
but didn't create — matches Settings's default. Pass
owned_only=false to include followed playlists.

Reads from spotify_playlists only; doesn't call Spotify. Run
POST /spotify/me/sync first to populate the mirror.

Parameters

Query Parameters

owned_only
Type
boolean
Default
true

Responses

Successful Response

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

Playground

Variables
Key
Value

Samples


Get My Playlist

GET
/spotify/me/playlists/{spotify_playlist_id}

Get a single Spotify playlist with its embedded items.

Items carry canonical_track_id when our sync resolved them
against an existing canonical. Resolution happens at sync time
— calling this endpoint never triggers a Spotify network call.

Parameters

Path Parameters

spotify_playlist_id*
Type
string
Required

Responses

Successful Response

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

Playground

Variables
Key
Value

Samples


Import Liked Songs To Favorites

POST
/spotify/me/import-favorites

One-shot: import the caller's Spotify Liked Songs into
canonical favorites.

Walks spotify_saved_tracks and upserts each row with a
resolved canonical onto media_favorites. Idempotent —
already-favorited tracks count separately in the summary so the
SPA can render "imported 12 new; 35 already favorited".

Prerequisite: a Spotify sync must have run already (so saved
tracks exist in the mirror with canonical_id stamped). If the
user has just linked Spotify but hasn't synced yet, they get
candidates: 0 and nothing happens — the SPA should suggest
"click Sync first".

Parameters

Responses

Successful Response

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

Playground

Samples


Media — Blue Sapphire Software.