# API Reference All endpoints are served by the Fastify backend on port **8085** (proxied through port 8006 in the default Docker setup). Unless stated otherwise, endpoints that modify data require authentication. **Authentication**: Include `Authorization: Bearer {token}` on every authenticated request. For WebSocket upgrades, use `?token={token}` as a query parameter. --- ## Authentication ### Login `POST /api/auth/login` ```json { "profileName": "Alice", "pin": "1234" } ``` **Response 200** ```json { "token": "uuid", "expiresAt": 1748563200000, "profile": { "id": 1, "name": "Alice", "avatarId": 0 } } ``` **Response 401** — wrong PIN or account locked. --- ### Logout `POST /api/auth/logout` — Auth required Invalidates the current token. Returns `204 No Content`. --- ### Get Session `GET /api/auth/session` — Auth required ```json { "session": { "token": "...", "profileId": 1, "expiresAt": 1748563200000 }, "profile": { "id": 1, "name": "Alice" } } ``` --- ### Account Recovery `POST /api/auth/recover` ```json { "name": "Alice", "recoveryPhrase": "blue sky 42", "newPin": "5678" } ``` Returns the updated profile on success, `401` if phrase is wrong. --- ## Profiles ### List Profiles `GET /api/profiles` — No auth Returns all profiles (safe fields only — no PIN data). --- ### Get Profile `GET /api/profiles/:id` — No auth --- ### Create Profile `POST /api/profiles` — Requires `ADMIN` ```json { "name": "Bob", "pin": "0000", "recoveryPhrase": "my phrase", "recoveryPhraseHint": "favourite colour + number", "avatarId": 2 } ``` --- ### Update Profile `PATCH /api/profiles/:id` — Requires `ADMIN` Partial update — send only the fields to change. --- ### Delete Profile `DELETE /api/profiles/:id` — Requires `ADMIN` Deletes the profile and invalidates all its sessions. Returns `204`. --- ## Library ### Search Songs `GET /api/library` | Parameter | Type | Description | |---|---|---| | `q` | string | Full-text search (artist, title, album) | | `page` | int | Page (0-indexed, default 0) | | `size` | int | Per page (max 200, default 50) | | `sort` | string | `artist` \| `artist-desc` \| `title` \| `title-desc` \| `recent` \| `tuning` \| `year` \| `year-desc` | | `format` | string | `psarc` \| `sloppak` \| `loose` | | `arrangements_has` | string[] | Must include these parts (Lead, Rhythm, Bass, Combo) | | `arrangements_lacks` | string[] | Must not include these parts | | `stems_has` | string[] | Must include these stems (guitar, bass, drums, vocals) | | `stems_lacks` | string[] | Must not include these stems | | `has_lyrics` | bool | Only songs with lyrics | | `tunings` | string[] | Filter by tuning names | | `favorites` | bool | Only favourites for the current profile | **Response** ```json { "songs": [ SongMeta ], "total": 452, "page": 0, "size": 50 } ``` --- ### Artists Tree `GET /api/library/artists` Returns songs grouped by first letter then artist name. --- ### Library Stats `GET /api/library/stats` ```json { "totalSongs": 452, "totalArtists": 87, "letters": { "A": 34, "B": 21 } } ``` --- ### Tuning Names `GET /api/library/tuning-names` ```json [{ "name": "E Standard", "count": 312 }, ...] ``` --- ### Trigger Scan `POST /api/rescan` — Auth required Starts an incremental scan (skips unchanged files). Returns `202 Accepted`. `POST /api/rescan/full` — Auth required Re-processes all files. Returns `202 Accepted`. --- ### Scan Status `GET /api/scan-status` ```json { "status": "scanning", "stage": "importing", "current": "Artist - Title.psarc", "done": 42, "total": 150 } ``` --- ### Cleanup Orphans `POST /api/library/cleanup-orphans` — Auth required Removes `Song` records with no corresponding `Track`. Returns `204`. --- ## Tracks ### Track Metadata `GET /api/tracks/:trackId` Returns full `Track` record. --- ### Track Data `GET /api/tracks/:trackId/data` Returns `TrackData` (arrangement list, storage IDs). --- ### Cover Image `GET /api/tracks/:trackId/cover` Returns PNG image. `404` if no cover is stored. --- ### Audio Stream `GET /api/tracks/:trackId/audio` Returns OGG audio with full HTTP range support (206 Partial Content). Suitable for `