{"openapi":"3.0.0","paths":{"/playlists/my":{"get":{"deprecated":true,"operationId":"PlaylistsController_getMyPlaylists","parameters":[],"responses":{"200":{"description":"OK: List of playlists retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMyPlaylistsOutput"}}}},"401":{"description":"Unauthorized: User not authenticated"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Get my playlists","tags":["Playlists Owner"]}},"/playlists":{"post":{"operationId":"PlaylistsController_createPlaylist","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePlaylistRequestPayload"}}}},"responses":{"201":{"description":"Created: Playlist created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPlaylistOutput"}}}},"403":{"description":"Forbidden: Playlist creation limit exceeded"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Create a new playlist","tags":["Playlists Owner"]},"get":{"description":"Query parameters must conform to the **GetPlaylistsRequestPayload** schema.","operationId":"PlaylistsPublicController_getPlaylists","parameters":[{"name":"pageNumber","required":false,"in":"query","description":"Page number for pagination (starting from 1)","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Page size for pagination (between 0 and 20)","schema":{"minimum":0,"maximum":20,"default":20,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search term for filtering playlists by name","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Field by which to sort playlists","schema":{"default":"addedAt","type":"string","enum":["addedAt","likesCount"]}},{"name":"sortDirection","required":false,"in":"query","description":"Sort direction (ascending or descending)","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"tagsIds","required":false,"in":"query","description":"Filter by tag IDs. Multiple values allowed, e.g.: tagsIds=tag1&tagsIds=tag2","schema":{"type":"array","items":{"type":"string"}}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID (playlist creator’s ID)","schema":{"type":"string"}},{"name":"trackId","required":false,"in":"query","description":"Filter by track ID – only playlists containing this track will be returned","schema":{"type":"string"}},{"name":"onlyLikedByMe","required":false,"in":"query","description":"If true, return only playlists liked by the current authenticated user","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK: JSON:API list of playlists with pagination","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPlaylistsOutput"}}}}},"summary":"Retrieve all playlists","tags":["Playlists Public"],"security":[{"api-key":[]}]}},"/playlists/{playlistId}":{"put":{"operationId":"PlaylistsController_updatePlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePlaylistRequestPayload"}}}},"responses":{"204":{"description":"No Content: Playlist updated successfully"},"400":{"description":"Bad Request: Validation error (e.g., tag limit exceeded)"},"403":{"description":"Forbidden: You do not have permission to update this playlist"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Update a playlist","tags":["Playlists Owner"]},"delete":{"operationId":"PlaylistsController_deletePlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Playlist deleted successfully"},"403":{"description":"Forbidden: Insufficient permissions to delete this playlist"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete a playlist","tags":["Playlists Owner"]},"get":{"operationId":"PlaylistsPublicController_getPlaylistById","parameters":[{"name":"playlistId","required":true,"in":"path","description":"ID of the playlist","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: Playlist retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPlaylistOutput"}}}},"404":{"description":"Not Found: Playlist with the given ID not found"}},"summary":"Get a single playlist by ID","tags":["Playlists Public"],"security":[{"api-key":[]}]}},"/playlists/{playlistId}/reorder":{"put":{"operationId":"PlaylistsController_reorderPlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderPlaylistsRequestPayload"}}}},"responses":{"204":{"description":"No Content: Playlist order updated successfully"},"404":{"description":"Not Found: Playlist or putAfterItemId not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Reorder playlists","tags":["Playlists Owner"]}},"/playlists/{playlistId}/images/main":{"post":{"description":"Minimum height — 500px; image must be square","operationId":"PlaylistsController_uploadMainImage","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"$ref":"#/components/schemas/BinaryFile","maxLength":1048576,"description":"Maximum size 1 MB; minimum height 500px; image must be square"}},"required":["file"]}}}},"responses":{"200":{"description":"OK: Cover uploaded successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackImages"}}}},"400":{"description":"Bad Request: Invalid image format or dimensions"},"403":{"description":"Forbidden: No permission to upload cover for this playlist"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Upload playlist cover","tags":["Playlists Owner"]},"delete":{"operationId":"PlaylistsController_deleteTrackCover","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Cover deleted successfully"},"403":{"description":"Forbidden: Removing another user’s playlist cover is not allowed"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete playlist cover","tags":["Playlists Owner"]}},"/playlists/tracks":{"get":{"description":"Query-parameters schema → [`GetTracksRequestPayload`](#model-GetTracksRequestPayload)","operationId":"TracksPublicController_getAllTracks","parameters":[{"name":"pageNumber","required":false,"in":"query","description":"Page number for pagination (starting from 1)","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"pageSize","required":false,"in":"query","description":"Page size for pagination (between 0 and 20)","schema":{"minimum":0,"maximum":20,"default":20,"type":"number"}},{"name":"search","required":false,"in":"query","description":"Search term for filtering playlists by name","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Field by which to sort tracks","schema":{"default":"publishedAt","type":"string","enum":["publishedAt","likesCount"]}},{"name":"sortDirection","required":false,"in":"query","description":"Sort direction (ascending or descending)","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"tagsIds","required":false,"in":"query","description":"Filter by tag IDs (multiple values allowed)","schema":{"type":"array","items":{"type":"string"}}},{"name":"artistsIds","required":false,"in":"query","description":"Filter by artist IDs (multiple values allowed)","schema":{"type":"array","items":{"type":"string"}}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID (track creator's ID)","schema":{"type":"string"}},{"name":"includeDrafts","required":false,"in":"query","description":"If true, include unpublished tracks (drafts) of current user if userId === currentUserId","schema":{"type":"boolean"}},{"name":"onlyLikedByMe","required":false,"in":"query","description":"If true, return only tracks liked by the current authenticated user","schema":{"type":"boolean"}},{"name":"paginationType","required":false,"in":"query","description":"Pagination type: \"offset\" for page-number pagination; \"cursor\" for keyset/seek-based pagination.","schema":{"default":"offset","type":"string","enum":["offset","cursor"]}},{"name":"cursor","required":false,"in":"query","description":"Base64-encoded cursor for keyset pagination. Used only if paginationType is \"cursor\".","schema":{"nullable":true,"type":"string"}}],"responses":{"200":{"description":"OK: Paginated list of tracks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTrackListOutput"}}}},"400":{"description":"Bad Request: invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonApiErrorDocument"}}}}},"summary":"Get list of all tracks in all playlists.","tags":["Tracks Public"],"security":[{"api-key":[]}]}},"/playlists/{playlistId}/tracks":{"get":{"operationId":"TracksPublicController_getPlaylistTracks","parameters":[{"name":"playlistId","required":true,"in":"path","description":"ID of the playlist to retrieve tracks for","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: List of tracks in the playlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTracksForPlaylistOutput"}}}},"404":{"description":"Not Found: Playlist with the specified ID not found"}},"summary":"Get list of tracks in a playlist","tags":["Tracks Public"],"security":[{"api-key":[]}]}},"/playlists/tracks/{trackId}":{"get":{"operationId":"TracksPublicController_getTrackDetails","parameters":[{"name":"trackId","required":true,"in":"path","description":"ID of the track to retrieve details for","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: Track details with attachments","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTrackDetailsOutput"}}}},"404":{"description":"Not Found: Track with the specified ID not found"}},"summary":"Get track details by ID","tags":["Tracks Public"],"security":[{"api-key":[]}]},"put":{"operationId":"TracksController_updateTrack","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTrackRequestPayload"}}}},"responses":{"200":{"description":"OK: Track updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTrackDetailsOutput"}}}},"400":{"description":"Bad Request: Tag or artist limit exceeded"},"403":{"description":"Forbidden: Editing another user’s track is not allowed"},"404":{"description":"Not Found: Track or playlist not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Update track information","tags":["Tracks Owner"]},"delete":{"operationId":"TracksController_deleteTrackCompletely","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Track permanently deleted"},"403":{"description":"Forbidden: Deleting another user’s track is not allowed"},"404":{"description":"Not Found: Track not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Permanently delete a track","tags":["Tracks Owner"]}},"/playlists/tracks/{trackId}/likes":{"post":{"operationId":"TracksPublicController_likeTrack","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Created: User reaction recorded and counters updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"400":{"description":"Bad Request: Invalid track ID"},"401":{"description":"Unauthorized: User not authenticated"},"404":{"description":"Not Found: Track not found"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Like or toggle like on a track","tags":["Tracks Public"]}},"/playlists/tracks/{trackId}/dislikes":{"post":{"operationId":"TracksPublicController_dislikeTrack","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Created: User reaction recorded and counters updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"400":{"description":"Bad Request: Invalid track ID"},"401":{"description":"Unauthorized: User not authenticated"},"404":{"description":"Not Found: Track not found"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Dislike or toggle dislike on a track","tags":["Tracks Public"]}},"/playlists/tracks/{trackId}/reactions":{"delete":{"operationId":"TracksPublicController_removeTrackReaction","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: Reaction removed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"401":{"description":"Unauthorized: User not authenticated"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Remove user reaction from a track","tags":["Tracks Public"]}},"/playlists/{playlistId}/likes":{"post":{"operationId":"PlaylistsPublicController_likePlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Created: Like recorded successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"400":{"description":"Bad Request: Invalid playlist ID"},"401":{"description":"Unauthorized"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Like a playlist","tags":["Playlists Public"]}},"/playlists/{playlistId}/dislikes":{"post":{"operationId":"PlaylistsPublicController_dislikePlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"Created: Dislike recorded successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"400":{"description":"Bad Request: Invalid playlist ID"},"401":{"description":"Unauthorized"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Dislike a playlist","tags":["Playlists Public"]}},"/playlists/{playlistId}/reactions":{"delete":{"operationId":"PlaylistsPublicController_removePlaylistReaction","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: Reaction removed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOutput"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Remove user reaction from a playlist","tags":["Playlists Public"]}},"/playlists/{playlistId}/tracks/{trackId}/reorder":{"put":{"operationId":"TracksController_reorderTrack","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}},{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderTracksRequestPayload"}}}},"responses":{"200":{"description":"OK: Track order updated successfully"},"400":{"description":"Bad Request: Cannot place a track after itself"},"403":{"description":"Forbidden: No access to the playlist"},"404":{"description":"Not Found: Track or putAfterItemId not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Reorder tracks in a playlist","tags":["Tracks Owner"]}},"/playlists/{playlistId}/relationships/tracks":{"post":{"operationId":"TracksController_addTrackToPlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTrackToPlaylistRequestPayload"}}}},"responses":{"204":{"description":"No Content: Track added to the playlist successfully"},"403":{"description":"Forbidden: No access to the playlist or track limit exceeded (max 10 tracks)"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Add a track to your playlist","tags":["Tracks Owner"]}},"/playlists/{playlistId}/relationships/tracks/{trackId}":{"delete":{"operationId":"TracksController_unbindTrackFromPlaylist","parameters":[{"name":"playlistId","required":true,"in":"path","schema":{"type":"string"}},{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Track removed from the playlist"},"403":{"description":"Forbidden: No access to the playlist"},"404":{"description":"Not Found: Playlist not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Remove a track from your playlist","tags":["Tracks Owner"]}},"/playlists/tracks/{trackId}/actions/publish":{"post":{"operationId":"TracksController_publishTrack","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Track published successfully"},"403":{"description":"Forbidden: Publishing another user’s track is not allowed"},"404":{"description":"Not Found: Track with the specified ID not found"},"409":{"description":"Conflict: Track is already published"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Publish a track (make it publicly available)","tags":["Tracks Owner"]}},"/playlists/tracks/{trackId}/cover":{"post":{"operationId":"TracksController_uploadTrackCover","parameters":[{"name":"trackId","required":true,"in":"path","description":"ID of the track for which the cover is being uploaded","schema":{"example":"b25f3f43-e7e1-4cdd-9b77-01b3b8fa9888","type":"string"}}],"requestBody":{"required":true,"description":"Image file:<br/>\n        • Field name — <code>cover</code><br/>\n        • Allowed MIME types — <code>image/jpeg</code>, <code>image/png</code>, <code>image/gif</code><br/>\n        • Maximum size — <code>100 KB</code>","content":{"multipart/form-data":{"schema":{"type":"object","properties":{"cover":{"type":"string","format":"binary"}},"required":["cover"]},"examples":{"jpeg":{"summary":"JPEG example","value":{"cover":"cover.jpg"}}}}}},"responses":{"200":{"description":"OK: Cover uploaded successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackImages"}}}},"400":{"description":"Bad Request: Invalid file or size exceeded"},"403":{"description":"Forbidden: Cannot upload a cover for another user’s track"},"404":{"description":"Not Found: Track not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Upload track cover","tags":["Tracks Owner"]},"delete":{"operationId":"TracksController_deleteTrackCover","parameters":[{"name":"trackId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Cover deleted successfully"},"403":{"description":"Forbidden: Removing another user's track cover is not allowed"},"404":{"description":"Not Found: Track not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete track cover","tags":["Tracks Owner"]}},"/playlists/tracks/upload":{"post":{"operationId":"TracksController_uploadTrackMp3","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"title":{"type":"string","example":"My cool track"},"file":{"type":"string","format":"binary"}},"required":["title","file"]}}}},"responses":{"200":{"description":"OK: Track created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTrackDetailsOutput"}}}},"400":{"description":"Bad Request: Invalid file format or file size exceeded"},"500":{"description":"Internal Server Error: Error saving file or track"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Create a track with MP3 file upload. Allowed file extensions: mp3, max size: 1 MB","tags":["Tracks Owner"]}},"/artists":{"post":{"operationId":"ArtistsController_createArtist","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateArtistRequestPayload"}}}},"responses":{"201":{"description":"Created: Artist created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtistRef"}}}},"400":{"description":"Bad Request: Validation error or invalid input"},"401":{"description":"Unauthorized: User not authenticated"},"403":{"description":"Forbidden: Limit of 100 artists per user reached"},"409":{"description":"Conflict: Artist with the given name already exists"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Create a new artist","tags":["Artists"]}},"/artists/search":{"get":{"operationId":"ArtistsController_searchArtist","parameters":[{"name":"search","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: List of artists matching the search","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ArtistRef"}}}}}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Search artists by substring","tags":["Artists"]}},"/artists/{id}":{"delete":{"operationId":"ArtistsController_deleteArtist","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Artist deleted successfully"},"403":{"description":"Forbidden: Artist is attached to tracks or was created by another user"},"404":{"description":"Not Found: Artist with the specified ID not found"}},"security":[{"basic":[]},{"bearer":[]},{"bearer":[]},{"api-key":[]}],"summary":"Delete an artist by ID","tags":["Artists"]}},"/auth/oauth-redirect":{"get":{"description":"The callback URL to redirect after granting access, <a target=\"_blank\" href=\"https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=musicfun&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid\">https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=musicfun&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid</a>","operationId":"AuthController_OauthRedirect","parameters":[{"name":"callbackUrl","required":true,"in":"query","description":"The callback URL to redirect after grand access,\n     https://oauth.apihub.it-incubator.io/realms/apihub/protocol/openid-connect/auth?client_id=musicfun&response_type=code&redirect_uri=http://localhost:3000/oauth2/callback&scope=openid","schema":{"example":"http://localhost:3000/oauth2/callback","type":"string"}}],"responses":{"200":{"description":"OK: Redirect executed successfully"}},"summary":"OAuth redirect","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/login":{"post":{"operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOutput"}}}},"400":{"description":"Bad Request: Invalid request format or required parameters are missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"401":{"description":"Unauthorized: Code is invalid, expired, missing, or redirectUri does not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Log in using the code received after OAuth authorization redirect","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/refresh":{"post":{"operationId":"AuthController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair refreshed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOutput"}}}},"401":{"description":"Unauthorized: Refresh token is invalid, expired, or missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Refresh refresh/access token pair","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/logout":{"post":{"operationId":"AuthController_logout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutRequestPayload"}}}},"responses":{"204":{"description":"No Content: Refresh token deactivated; access token remains valid."}},"summary":"Deactivate refresh token","tags":["Authentication"],"security":[{"api-key":[]}]}},"/auth/me":{"get":{"operationId":"AuthController_getMe","parameters":[],"responses":{"200":{"description":"OK: Successfully retrieved user information","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMeOutput"}}}},"401":{"description":"Unauthorized: access token is missing or invalid"}},"security":[{"basic":[]},{"bearer":[]},{"api-key":[]}],"summary":"Get current user by access token","tags":["Authentication"]}},"/auth/simple/login":{"post":{"operationId":"SimpleAuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleLoginRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleLoginOutput"}}}},"401":{"description":"Unauthorized: Invalid login or password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Simple login with login/password, returns access and refresh tokens","tags":["Simple Authentication"],"security":[{"api-key":[]}]}},"/auth/simple/refresh":{"post":{"operationId":"SimpleAuthController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleRefreshRequestPayload"}}}},"responses":{"200":{"description":"OK: Token pair refreshed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleLoginOutput"}}}},"401":{"description":"Unauthorized: Refresh token is invalid, expired, or revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnauthorizedException"}}}}},"summary":"Simple refresh: rotate access and refresh tokens","tags":["Simple Authentication"],"security":[{"api-key":[]}]}},"/auth/simple/logout":{"post":{"operationId":"SimpleAuthController_logout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimpleLogoutRequestPayload"}}}},"responses":{"204":{"description":"No Content: Refresh token has been revoked"}},"summary":"Simple logout: revoke refresh token","tags":["Simple Authentication"],"security":[{"api-key":[]}]}},"/tags":{"post":{"operationId":"TagsController_createTag","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagRequestPayload"}}}},"responses":{"201":{"description":"Created: Tag created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTagOutput"}}}},"400":{"description":"Bad Request: Validation error"},"401":{"description":"Unauthorized: User not authenticated"},"403":{"description":"Forbidden: Limit of 100 tags per user reached"},"409":{"description":"Conflict: Tag with the given name already exists"}},"security":[{"bearer":[]},{"api-key":[]}],"summary":"Create a new tag","tags":["Tags"]}},"/tags/search":{"get":{"operationId":"TagsController_searchTags","parameters":[{"name":"search","required":true,"in":"query","description":"Substring to search tags by (using normalized name)","schema":{"type":"string"}}],"responses":{"200":{"description":"OK: List of matching tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTagsOutput"}}}},"400":{"description":"Bad Request: Invalid search query"}},"summary":"Search tags by substring","tags":["Tags"],"security":[{"api-key":[]}]}},"/tags/{id}":{"delete":{"operationId":"TagsController_deleteTag","parameters":[{"name":"id","required":true,"in":"path","description":"ID of the tag to delete","schema":{"type":"string"}}],"responses":{"204":{"description":"No Content: Tag deleted successfully"},"401":{"description":"Unauthorized: User not authenticated"},"403":{"description":"Forbidden: Tag was created by another user or is attached to tracks or playlists"},"404":{"description":"Not Found: Tag with the specified ID not found"}},"summary":"Delete a tag by ID","tags":["Tags"],"security":[{"api-key":[]}]}}},"info":{"title":"MusicFun API","description":"API for learning. Create your own analogue of a popular music service, such as SoundCloud or Spotify.\n\n<h4>mp3 examples:</h4> \n🔈: https://musicfun.it-incubator.app/api/samurai-way-soundtrack.mp3   \n🔈: https://musicfun.it-incubator.app/api/samurai-way-soundtrack-instrumental.mp3","version":"1.0","contact":{}},"tags":[{"name":"Tracks Public"},{"name":"Playlists Public"},{"name":"Simple Authentication"},{"name":"Authentication"},{"name":"Playlists Owner"},{"name":"Tracks Owner"},{"name":"Tags"},{"name":"Artists"},{"name":"Artists"}],"servers":[{"url":"https://musicfun.it-incubator.app/api/1.0"}],"components":{"securitySchemes":{"api-key":{"type":"apiKey","in":"header","name":"api-key","description":"API key required to access endpoints"},"basic":{"type":"http","scheme":"basic"},"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"UserRef":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the user"},"name":{"type":"string","description":"Name of the user"}},"required":["id","name"]},"ImageSizeType":{"type":"string","enum":["original","thumbnail","medium"],"description":"Type of the image size (e.g., original, thumbnail variants)"},"ImageVariant":{"type":"object","properties":{"type":{"description":"Type of the image size (e.g., original, thumbnail variants)","allOf":[{"$ref":"#/components/schemas/ImageSizeType"}]},"width":{"type":"number","description":"Image width in pixels"},"height":{"type":"number","description":"Image height in pixels"},"fileSize":{"type":"number","description":"Image file size in bytes"},"url":{"type":"string","description":"Full public URL of the image"}},"required":["type","width","height","fileSize","url"]},"PlaylistImagesOutputDTO":{"type":"object","properties":{"main":{"description":"Original images and thumbnail previews","type":"array","items":{"$ref":"#/components/schemas/ImageVariant"}}}},"TagRef":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the tag"},"name":{"type":"string","description":"Original name of the tag"}},"required":["id","name"]},"ReactionValue":{"type":"number","enum":[0,1,-1],"description":"User reaction: 0 – guest or no reaction; 1 – like; -1 – dislike"},"PlaylistListItemAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Title of the playlist"},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the playlist was added (ISO 8601)"},"updatedAt":{"type":"string","format":"date-time","description":"Date and time when the playlist was last updated (ISO 8601)"},"order":{"type":"number","description":"Order index of the playlist"},"user":{"description":"User who created the playlist","allOf":[{"$ref":"#/components/schemas/UserRef"}]},"images":{"description":"Images associated with the playlist","allOf":[{"$ref":"#/components/schemas/PlaylistImagesOutputDTO"}]},"tags":{"description":"Tags linked to the playlist","type":"array","items":{"$ref":"#/components/schemas/TagRef"}},"likesCount":{"type":"number","description":"Total number of likes for this playlist"},"dislikesCount":{"type":"number","description":"Total number of dislikes for this playlist"},"currentUserReaction":{"description":"User reaction: 0 – guest or no reaction; 1 – like; -1 – dislike","allOf":[{"$ref":"#/components/schemas/ReactionValue"}]},"tracksCount":{"type":"number","description":"Total number of tracks in the playlist"},"duration":{"type":"number","description":"Total duration of the playlist in seconds"}},"required":["title","addedAt","updatedAt","order","user","images","tags","likesCount","dislikesCount","currentUserReaction","tracksCount","duration"]},"PlaylistListItemResource":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the playlist"},"type":{"type":"string","description":"Resource type (should be \"playlists\")","example":"playlists"},"attributes":{"$ref":"#/components/schemas/PlaylistListItemAttributes"}},"required":["id","type","attributes"]},"GetMyPlaylistsOutput":{"type":"object","properties":{"data":{"description":"Array of playlist resource objects owned by the current user","type":"array","items":{"$ref":"#/components/schemas/PlaylistListItemResource"}}},"required":["data"]},"CreatePlaylistAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Playlist title (1 to 100 characters)","minLength":1,"maxLength":100},"description":{"type":"string","description":"Playlist description (up to 1000 characters)","maxLength":1000,"nullable":true}},"required":["title","description"]},"CreatePlaylistData":{"type":"object","properties":{"type":{"type":"string","example":"playlists"},"attributes":{"$ref":"#/components/schemas/CreatePlaylistAttributes"}},"required":["type","attributes"]},"CreatePlaylistRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreatePlaylistData"}},"required":["data"]},"PlaylistAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Title of the playlist"},"description":{"type":"string","description":"Description of the playlist","nullable":true},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the playlist was added (ISO 8601)"},"updatedAt":{"type":"string","format":"date-time","description":"Date and time when the playlist was last updated (ISO 8601)"},"order":{"type":"number","description":"Order index of the playlist"},"user":{"description":"User who created the playlist","allOf":[{"$ref":"#/components/schemas/UserRef"}]},"images":{"description":"Images associated with the playlist","allOf":[{"$ref":"#/components/schemas/PlaylistImagesOutputDTO"}]},"tags":{"description":"Tags linked to the playlist","type":"array","items":{"$ref":"#/components/schemas/TagRef"}},"likesCount":{"type":"number","description":"Total number of likes for this playlist"},"dislikesCount":{"type":"number","description":"Total number of dislikes for this playlist"},"currentUserReaction":{"description":"User reaction: 0 – guest or no reaction; 1 – like; -1 – dislike","allOf":[{"$ref":"#/components/schemas/ReactionValue"}]},"tracksCount":{"type":"number","description":"Total number of tracks in the playlist"},"duration":{"type":"number","description":"Total duration of the playlist in seconds"}},"required":["title","description","addedAt","updatedAt","order","user","images","tags","likesCount","dislikesCount","currentUserReaction","tracksCount","duration"]},"PlaylistResource":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the playlist"},"type":{"type":"string","description":"Resource type (should be \"playlists\")","example":"playlists"},"attributes":{"$ref":"#/components/schemas/PlaylistAttributes"}},"required":["id","type","attributes"]},"GetPlaylistOutput":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PlaylistResource"}},"required":["data"]},"UpdatePlaylistAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Playlist title (1 – 100 characters)","minLength":1,"maxLength":100},"description":{"type":"string","description":"Playlist description (up to 1000 characters)","maxLength":1000,"nullable":true,"example":"Cool playlist"},"tagIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Tag IDs to associate with the playlist (0 – 5 items; [] = clear tags)","maxItems":5}},"required":["title","description","tagIds"]},"UpdatePlaylistData":{"type":"object","properties":{"type":{"type":"string","example":"playlists"},"attributes":{"$ref":"#/components/schemas/UpdatePlaylistAttributes"}},"required":["type","attributes"]},"UpdatePlaylistRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UpdatePlaylistData"}},"required":["data"]},"ReorderPlaylistsRequestPayload":{"type":"object","properties":{"putAfterItemId":{"type":"string","format":"uuid","nullable":true,"description":"ID of the playlist after which the current playlist should be inserted. Send null to place the playlist at the beginning of the list."}},"required":["putAfterItemId"]},"TrackImages":{"type":"object","properties":{"main":{"description":"List of original images and thumbnail versions (e.g., original, 320x180, etc.)","type":"array","items":{"$ref":"#/components/schemas/ImageVariant"}}}},"GetTracksRequestPayload":{"type":"object","properties":{"pageNumber":{"type":"number","description":"Page number for pagination (starting from 1)","default":1,"minimum":1},"pageSize":{"type":"number","description":"Page size for pagination (between 0 and 20)","default":20,"minimum":0,"maximum":20},"search":{"type":"string","description":"Search term for filtering playlists by name"},"sortBy":{"type":"string","enum":["publishedAt","likesCount"],"default":"publishedAt","description":"Field by which to sort tracks"},"sortDirection":{"type":"string","enum":["asc","desc"],"default":"desc","description":"Sort direction (ascending or descending)"},"tagsIds":{"description":"Filter by tag IDs (multiple values allowed)","type":"array","items":{"type":"string"}},"artistsIds":{"description":"Filter by artist IDs (multiple values allowed)","type":"array","items":{"type":"string"}},"userId":{"type":"string","description":"Filter by user ID (track creator's ID)"},"includeDrafts":{"type":"boolean","description":"If true, include unpublished tracks (drafts) of current user if userId === currentUserId"},"onlyLikedByMe":{"type":"boolean","description":"If true, return only tracks liked by the current authenticated user"},"paginationType":{"type":"string","enum":["offset","cursor"],"default":"offset","description":"Pagination type: \"offset\" for page-number pagination; \"cursor\" for keyset/seek-based pagination."},"cursor":{"type":"string","nullable":true,"description":"Base64-encoded cursor for keyset pagination. Used only if paginationType is \"cursor\"."}}},"JsonApiErrorSource":{"type":"object","properties":{"pointer":{"type":"string","description":"e.g. \"/data/attributes/field\"","example":"/data/attributes/field"},"parameter":{"type":"string","description":"e.g. \"?queryParam\"","example":"?queryParam"}}},"JsonApiError":{"type":"object","properties":{"status":{"type":"string","description":"HTTP status code as a string","example":"404"},"code":{"type":"object","description":"Application-specific error code","example":"E123"},"title":{"type":"string","description":"Short, human-readable summary","example":"Not Found"},"detail":{"type":"string","description":"Detailed explanation","example":"User with ID 123 not found"},"source":{"description":"Pointer to the associated entity in the request","allOf":[{"$ref":"#/components/schemas/JsonApiErrorSource"}]},"meta":{"type":"object","description":"Any extra data"}},"required":["status"]},"JsonApiErrorDocument":{"type":"object","properties":{"errors":{"description":"Array of one or more errors","type":"array","items":{"$ref":"#/components/schemas/JsonApiError"}},"meta":{"type":"object","description":"e.g. timestamp, path, traceId, etc."}},"required":["errors"]},"TrackAttachment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the entity"},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the entity was added"},"updatedAt":{"type":"string","format":"date-time","description":"Date and time when the entity was last updated"},"version":{"type":"number","description":"Version number of the entity (for concurrency control)"},"url":{"type":"string","example":"https://cdn.example.com/uploads/track123/cover.jpg","description":"Public URL to access the uploaded file"},"contentType":{"type":"string","example":"image/jpeg","description":"MIME type of the file"},"originalName":{"type":"string","example":"cover.jpg","description":"Original filename uploaded by the user"},"fileSize":{"type":"number","example":34872,"description":"Size of the file in bytes"}},"required":["id","addedAt","updatedAt","version","url","contentType","originalName","fileSize"]},"TrackListItemAttributes":{"type":"object","properties":{"title":{"type":"string"},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the track was added (ISO 8601)"},"likesCount":{"type":"number"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/TrackAttachment"}},"images":{"$ref":"#/components/schemas/TrackImages"},"user":{"$ref":"#/components/schemas/UserRef"},"currentUserReaction":{"type":"number","enum":[0,1,-1],"description":"0 – не залогинен или не реагировал; 1 – лайк; −1 – дизлайк"},"isPublished":{"type":"boolean"},"publishedAt":{"type":"string","format":"date-time","description":"Date and time when the track was published (ISO 8601)","nullable":true},"duration":{"type":"number","description":"Duration of the track in seconds"}},"required":["title","addedAt","likesCount","attachments","images","user","currentUserReaction","isPublished","duration"]},"ArtistRelationship":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type"]},"ArtistsRelationship":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ArtistRelationship"}}},"required":["data"]},"TrackRelationships":{"type":"object","properties":{"artists":{"$ref":"#/components/schemas/ArtistsRelationship"}},"required":["artists"]},"TrackListItemResource":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"tracks"},"attributes":{"$ref":"#/components/schemas/TrackListItemAttributes"},"relationships":{"$ref":"#/components/schemas/TrackRelationships"}},"required":["id","type","attributes","relationships"]},"JsonApiMetaWithPagingAndCursor":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"totalCount":{"type":"number","nullable":true,"description":"Total count may be absent when using keyset pagination"},"pagesCount":{"type":"number","nullable":true,"description":"Total number of pages"},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page"}},"required":["page","pageSize","totalCount","pagesCount","nextCursor"]},"OmitTypeClass":{"type":"object","properties":{"name":{"type":"string","description":"Name of the artist"}},"required":["name"]},"IncludedArtistOutput":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"attributes":{"$ref":"#/components/schemas/OmitTypeClass"}},"required":["id","type","attributes"]},"GetTrackListOutput":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackListItemResource"}},"meta":{"$ref":"#/components/schemas/JsonApiMetaWithPagingAndCursor"},"included":{"type":"array","items":{"$ref":"#/components/schemas/IncludedArtistOutput"}}},"required":["data","meta","included"]},"TrackListItemAttributesForPlaylist":{"type":"object","properties":{"title":{"type":"string","description":"Title of the track"},"order":{"type":"number","description":"Order index of the track in the playlist"},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the track was added (ISO 8601)"},"updatedAt":{"type":"string","format":"date-time","description":"Date and time when the track was last updated (ISO 8601)"},"attachments":{"description":"Attachments related to the track","type":"array","items":{"$ref":"#/components/schemas/TrackAttachment"}},"images":{"description":"Images associated with the track","allOf":[{"$ref":"#/components/schemas/TrackImages"}]},"currentUserReaction":{"type":"number","enum":[0,1,-1],"nullable":true,"description":"User reaction: 0 – guest or no reaction; 1 – liked; -1 – disliked"},"publishedAt":{"type":"string","format":"date-time","description":"Date and time when the track was published (ISO 8601)","nullable":true},"duration":{"type":"number","description":"Duration of the track in seconds"}},"required":["title","order","addedAt","updatedAt","attachments","images","currentUserReaction","duration"]},"TrackListItemResourceForPlaylist":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","example":"tracks"},"attributes":{"$ref":"#/components/schemas/TrackListItemAttributesForPlaylist"},"relationships":{"$ref":"#/components/schemas/TrackRelationships"}},"required":["id","type","attributes","relationships"]},"JsonApiMeta":{"type":"object","properties":{"totalCount":{"type":"number"}},"required":["totalCount"]},"GetTracksForPlaylistOutput":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackListItemResourceForPlaylist"}},"meta":{"$ref":"#/components/schemas/JsonApiMeta"},"included":{"type":"array","items":{"$ref":"#/components/schemas/IncludedArtistOutput"}}},"required":["data","meta","included"]},"ArtistRef":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the artist"},"name":{"type":"string","description":"Name of the artist"}},"required":["id","name"]},"TrackDetailsAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Track title","maxLength":100},"lyrics":{"type":"string","description":"Track lyrics text","nullable":true,"maxLength":5000},"releaseDate":{"type":"string","format":"date-time","description":"Release date in ISO 8601 format","nullable":true},"addedAt":{"type":"string","format":"date-time","description":"Date and time when the track was added (ISO 8601)"},"updatedAt":{"type":"string","format":"date-time","description":"Date and time when the track was last updated (ISO 8601)"},"duration":{"type":"number","description":"Duration of the track in seconds"},"likesCount":{"type":"number","description":"Total number of likes for this track"},"dislikesCount":{"type":"number","deprecated":true,"description":"Total number of dislikes for this track"},"attachments":{"description":"List of attachments related to the track","type":"array","items":{"$ref":"#/components/schemas/TrackAttachment"}},"images":{"$ref":"#/components/schemas/TrackImages"},"tags":{"description":"Tags associated with the track","type":"array","items":{"$ref":"#/components/schemas/TagRef"}},"artists":{"description":"Artists associated with the track","type":"array","items":{"$ref":"#/components/schemas/ArtistRef"}},"user":{"$ref":"#/components/schemas/UserRef"},"isPublished":{"type":"boolean","description":"Publication status of the track"},"publishedAt":{"type":"string","format":"date-time","description":"Publication date in ISO 8601 format","nullable":true},"currentUserReaction":{"type":"number","enum":[0,1,-1],"description":"User reaction: 0 – guest or no reaction; 1 – user liked; -1 – user disliked"}},"required":["title","addedAt","updatedAt","duration","likesCount","dislikesCount","attachments","images","tags","artists","user","isPublished","currentUserReaction"]},"TrackDetailsResource":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the track"},"type":{"type":"string","description":"Resource type (should be \"tracks\")","example":"tracks"},"attributes":{"$ref":"#/components/schemas/TrackDetailsAttributes"}},"required":["id","type","attributes"]},"GetTrackDetailsOutput":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TrackDetailsResource"}},"required":["data"]},"ReactionOutput":{"type":"object","properties":{"objectId":{"type":"string"},"value":{"type":"number","enum":[0,1,-1]},"likes":{"type":"number"},"dislikes":{"type":"number"}},"required":["objectId","value","likes","dislikes"]},"GetPlaylistsRequestPayload":{"type":"object","properties":{"pageNumber":{"type":"number","description":"Page number for pagination (starting from 1)","default":1,"minimum":1},"pageSize":{"type":"number","description":"Page size for pagination (between 0 and 20)","default":20,"minimum":0,"maximum":20},"search":{"type":"string","description":"Search term for filtering playlists by name"},"sortBy":{"type":"string","enum":["addedAt","likesCount"],"default":"addedAt","description":"Field by which to sort playlists"},"sortDirection":{"type":"string","enum":["asc","desc"],"default":"desc","description":"Sort direction (ascending or descending)"},"tagsIds":{"description":"Filter by tag IDs. Multiple values allowed, e.g.: tagsIds=tag1&tagsIds=tag2","type":"array","items":{"type":"string"}},"userId":{"type":"string","description":"Filter by user ID (playlist creator’s ID)"},"trackId":{"type":"string","description":"Filter by track ID – only playlists containing this track will be returned"},"onlyLikedByMe":{"type":"boolean","description":"If true, return only playlists liked by the current authenticated user"}}},"JsonApiMetaWithPaging":{"type":"object","properties":{"totalCount":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"},"pagesCount":{"type":"number"}},"required":["totalCount","page","pageSize","pagesCount"]},"GetPlaylistsOutput":{"type":"object","properties":{"data":{"description":"Array of playlist resource objects","type":"array","items":{"$ref":"#/components/schemas/PlaylistListItemResource"}},"meta":{"$ref":"#/components/schemas/JsonApiMetaWithPaging"}},"required":["data","meta"]},"ReorderTracksRequestPayload":{"type":"object","properties":{"putAfterItemId":{"type":"string","format":"uuid","nullable":true,"description":"ID of the track after which the current track should be inserted. Send null to place the track at the beginning of the list.","example":"a1b2c3d4-e5f6-7890-abcd-1234567890ef"}},"required":["putAfterItemId"]},"UpdateTrackAttributes":{"type":"object","properties":{"title":{"type":"string","description":"Track title (1 to 100 characters)","minLength":1,"maxLength":100},"lyrics":{"type":"string","description":"Track lyrics (up to 5000 characters)","maxLength":5000,"nullable":true},"releaseDate":{"type":"string","format":"date-time","description":"Release date in ISO 8601 format","nullable":true},"tagIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":5,"description":"Array of tag IDs to associate with the track (up to 5)"},"artistsIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":5,"description":"Array of artist IDs to associate with the track (up to 5)"}},"required":["title","lyrics","releaseDate","tagIds","artistsIds"]},"UpdateTrackData":{"type":"object","properties":{"type":{"type":"string","example":"tracks"},"attributes":{"$ref":"#/components/schemas/UpdateTrackAttributes"}},"required":["type","attributes"]},"UpdateTrackRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UpdateTrackData"}},"required":["data"]},"AddTrackToPlaylistAttributes":{"type":"object","properties":{"trackId":{"type":"string","description":"ID of the track to add to the playlist"}},"required":["trackId"]},"AddTrackToPlaylistData":{"type":"object","properties":{"type":{"type":"string","example":"playlist-tracks"},"attributes":{"$ref":"#/components/schemas/AddTrackToPlaylistAttributes"}},"required":["type","attributes"]},"AddTrackToPlaylistRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AddTrackToPlaylistData"}},"required":["data"]},"CreateArtistAttributes":{"type":"object","properties":{"name":{"type":"string","description":"Artist name (must be between 2 and 30 characters)","minLength":2,"maxLength":30}},"required":["name"]},"CreateArtistData":{"type":"object","properties":{"type":{"type":"string","example":"artists"},"attributes":{"$ref":"#/components/schemas/CreateArtistAttributes"}},"required":["type","attributes"]},"CreateArtistRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreateArtistData"}},"required":["data"]},"LoginRequestPayload":{"type":"object","properties":{"code":{"type":"string","description":"Authorization code received from OAuth server after redirect"},"redirectUri":{"type":"string","description":"Specify the same redirect URI used in the initial OAuth server request","example":"http://localhost:3000/oauth2/callback"},"accessTokenTTL":{"type":"string","description":"Access token lifetime (default \"3m\"); must be a string like \"60s\", \"3m\", \"2h\", or \"1d\"","example":"3m"},"rememberMe":{"type":"boolean","description":"Refresh token lifetime: if true, 30 days; if false, 30 minutes. accessTokenTTL must not exceed the refresh token lifetime"}},"required":["code","redirectUri","rememberMe"]},"RefreshOutput":{"type":"object","properties":{"refreshToken":{"type":"string"},"accessToken":{"type":"string"}},"required":["refreshToken","accessToken"]},"BadRequestException":{"type":"object","properties":{}},"UnauthorizedException":{"type":"object","properties":{}},"RefreshRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"LogoutRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"GetMeOutput":{"type":"object","properties":{"userId":{"type":"string"},"login":{"type":"string"}},"required":["userId","login"]},"SimpleLoginRequestPayload":{"type":"object","properties":{"login":{"type":"string","description":"User login"},"password":{"type":"string","description":"User password"},"rememberMe":{"type":"boolean","description":"Refresh token lifetime: if true, 7 days; if false, 30 minutes","default":true}},"required":["login","password","rememberMe"]},"SimpleLoginOutput":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}},"required":["accessToken","refreshToken"]},"SimpleRefreshRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token"}},"required":["refreshToken"]},"SimpleLogoutRequestPayload":{"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token to invalidate"}},"required":["refreshToken"]},"CreateTagAttributes":{"type":"object","properties":{"name":{"type":"string","description":"Tag name (2 to 30 characters)","minLength":2,"maxLength":30}},"required":["name"]},"CreateTagData":{"type":"object","properties":{"type":{"type":"string","example":"tags"},"attributes":{"$ref":"#/components/schemas/CreateTagAttributes"}},"required":["type","attributes"]},"CreateTagRequestPayload":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreateTagData"}},"required":["data"]},"TagAttributes":{"type":"object","properties":{"name":{"type":"string","description":"Original name of the tag"}},"required":["name"]},"TagResource":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the tag"},"type":{"type":"string","description":"Resource type (should be \"tags\")","example":"tags"},"attributes":{"$ref":"#/components/schemas/TagAttributes"}},"required":["id","type","attributes"]},"GetTagOutput":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TagResource"}},"required":["data"]},"GetTagsOutput":{"type":"object","properties":{"data":{"description":"Array of tag resource objects","type":"array","items":{"$ref":"#/components/schemas/TagResource"}}},"required":["data"]},"BinaryFile":{"type":"string","format":"binary","description":"Файл в multipart/form-data"}}}}