From 5e912be7daf4a5c098c2d1894d1d8c309792be1f Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 11 Feb 2026 13:41:10 -0800 Subject: [PATCH 1/3] Migrate v1 tracks to full tracks (#629) Drops min-track Renames full-track to track Updates usage everywhere Updates swagger confirmed consistency in api diff --- api/dbv1/full_playlists.go | 10 +- api/dbv1/full_tracks.go | 278 ------ api/dbv1/models.go | 88 +- api/dbv1/parallel.go | 10 +- api/dbv1/tracks.go | 202 +++++ api/response_helpers.go | 8 +- api/swagger/swagger-v1.yaml | 976 ++++++++++++++++++++- api/v1_explore_best_selling_test.go | 2 +- api/v1_playlist_tracks.go | 2 +- api/v1_search.go | 6 +- api/v1_track.go | 2 +- api/v1_track_access_info.go | 2 +- api/v1_track_download.go | 4 +- api/v1_track_inspect.go | 6 +- api/v1_track_remixes.go | 2 +- api/v1_track_remixing.go | 2 +- api/v1_track_stream.go | 2 +- api/v1_track_test.go | 10 +- api/v1_tracks.go | 2 +- api/v1_tracks_feeling_lucky.go | 2 +- api/v1_tracks_feeling_lucky_test.go | 2 +- api/v1_tracks_most_shared.go | 2 +- api/v1_tracks_recent_comments.go | 2 +- api/v1_tracks_recent_premium.go | 2 +- api/v1_tracks_test.go | 2 +- api/v1_tracks_trending.go | 2 +- api/v1_tracks_trending_test.go | 6 +- api/v1_tracks_trending_underground.go | 2 +- api/v1_tracks_trending_underground_test.go | 2 +- api/v1_tracks_usdc_purchase.go | 2 +- api/v1_tracks_usdc_purchase_test.go | 2 +- api/v1_users_history.go | 8 +- api/v1_users_library_tracks.go | 2 +- api/v1_users_recommended_tracks.go | 2 +- api/v1_users_recommended_tracks_test.go | 2 +- api/v1_users_tracks.go | 2 +- api/v1_users_tracks_ai_attributed.go | 2 +- api/v1_users_tracks_ai_attributed_test.go | 2 +- api/v1_users_tracks_test.go | 4 +- sqlc.yaml | 1 + 40 files changed, 1260 insertions(+), 407 deletions(-) delete mode 100644 api/dbv1/full_tracks.go create mode 100644 api/dbv1/tracks.go diff --git a/api/dbv1/full_playlists.go b/api/dbv1/full_playlists.go index 7a443120..047797d6 100644 --- a/api/dbv1/full_playlists.go +++ b/api/dbv1/full_playlists.go @@ -21,7 +21,7 @@ type FullPlaylist struct { Artwork *SquareImage `json:"artwork"` UserID trashid.HashId `json:"user_id"` User User `json:"user"` - Tracks []FullTrack `json:"tracks"` + Tracks []Track `json:"tracks"` TrackCount int32 `json:"track_count"` Access Access `json:"access"` Permalink string `json:"permalink"` @@ -89,7 +89,7 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam continue } - var tracks = make([]FullTrack, 0, len(playlist.PlaylistContents.TrackIDs)) + var tracks = make([]Track, 0, len(playlist.PlaylistContents.TrackIDs)) for _, t := range playlist.PlaylistContents.TrackIDs { if track, ok := loaded.TrackMap[int32(t.Track)]; ok { tracks = append(tracks, track) @@ -184,9 +184,9 @@ type MinPlaylist struct { } func ToMinPlaylist(fullPlaylist FullPlaylist) MinPlaylist { - minTracks := make([]MinTrack, len(fullPlaylist.Tracks)) - for i, track := range fullPlaylist.Tracks { - minTracks[i] = ToMinTrack(track) + minTracks := make([]Track, len(fullPlaylist.Tracks)) + for i, track := range fullPlaylist.Tracks { + minTracks[i] = track } return MinPlaylist{ diff --git a/api/dbv1/full_tracks.go b/api/dbv1/full_tracks.go deleted file mode 100644 index c6725d19..00000000 --- a/api/dbv1/full_tracks.go +++ /dev/null @@ -1,278 +0,0 @@ -package dbv1 - -import ( - "context" - "encoding/json" - "fmt" - - "api.audius.co/trashid" - "github.com/jackc/pgx/v5/pgtype" -) - -type FullTracksParams struct { - GetTracksParams -} - -type FullTrack struct { - GetTracksRow - - Permalink string `json:"permalink"` - IsStreamable bool `json:"is_streamable"` - Artwork *SquareImage `json:"artwork"` - Stream *MediaLink `json:"stream"` - Download *MediaLink `json:"download"` - Preview *MediaLink `json:"preview"` - UserID trashid.HashId `json:"user_id"` - User User `json:"user"` - Access Access `json:"access"` - - FolloweeReposts []*FolloweeRepost `json:"followee_reposts"` - FolloweeFavorites []*FolloweeFavorite `json:"followee_favorites"` - RemixOf FullRemixOf `json:"remix_of"` - StreamConditions *AccessGate `json:"stream_conditions"` - DownloadConditions *AccessGate `json:"download_conditions"` -} - -func (q *Queries) FullTracksKeyed(ctx context.Context, arg FullTracksParams) (map[int32]FullTrack, error) { - rawTracks, err := q.GetTracks(ctx, GetTracksParams(arg.GetTracksParams)) - if err != nil { - return nil, err - } - - userIds := []int32{} - collectSplitUserIds := func(usage *AccessGate) { - if usage == nil || usage.UsdcPurchase == nil { - return - } - for _, split := range usage.UsdcPurchase.Splits { - userIds = append(userIds, split.UserID) - } - } - - for _, track := range rawTracks { - userIds = append(userIds, track.UserID) - - var remixOf RemixOf - json.Unmarshal(track.RemixOf, &remixOf) - for _, r := range remixOf.Tracks { - userIds = append(userIds, r.ParentUserId) - } - - collectSplitUserIds(track.StreamConditions) - collectSplitUserIds(track.DownloadConditions) - } - - userMap, err := q.UsersKeyed(ctx, GetUsersParams{ - MyID: arg.MyID.(int32), - Ids: userIds, - }) - if err != nil { - return nil, err - } - - // Convert rawTracks to pointers - trackPtrs := make([]*GetTracksRow, len(rawTracks)) - for i := range rawTracks { - trackPtrs[i] = &rawTracks[i] - } - - // Convert userMap to pointers - userPtrMap := make(map[int32]*User) - for id, user := range userMap { - userCopy := user // Create a copy to avoid modifying the original - userPtrMap[id] = &userCopy - } - - // Get bulk access for all tracks - accessMap, err := q.GetBulkTrackAccess(ctx, arg.MyID.(int32), trackPtrs, userPtrMap) - if err != nil { - return nil, err - } - - trackMap := map[int32]FullTrack{} - for _, track := range rawTracks { - track.ID, _ = trashid.EncodeHashId(int(track.TrackID)) - user, ok := userMap[track.UserID] - if !ok { - continue - } - - if track.FieldVisibility == nil || string(track.FieldVisibility) == "null" { - track.FieldVisibility = []byte(`{ - "mood":null, - "tags":null, - "genre":null, - "share":null, - "play_count":null, - "remixes":null - }`) - } - - var remixOf RemixOf - var fullRemixOf FullRemixOf - json.Unmarshal(track.RemixOf, &remixOf) - fullRemixOf = FullRemixOf{ - Tracks: make([]FullRemixOfTrack, len(remixOf.Tracks)), - } - for idx, r := range remixOf.Tracks { - trackId, _ := trashid.EncodeHashId(int(r.ParentTrackId)) - fullRemixOf.Tracks[idx] = FullRemixOfTrack{ - HasRemixAuthorReposted: r.HasRemixAuthorReposted, - HasRemixAuthorSaved: r.HasRemixAuthorSaved, - ParentTrackId: trackId, - User: userMap[r.ParentUserId], - } - } - - // Get access from the bulk access map - access := accessMap[track.TrackID] - - id3Tags := &Id3Tags{ - Title: track.Title.String, - Artist: user.Name.String, - } - - var stream *MediaLink - if access.Stream { - stream, err = mediaLink(track.TrackCid.String, track.TrackID, arg.MyID.(int32), id3Tags) - if err != nil { - return nil, err - } - } - - var download *MediaLink - if track.IsDownloadable && access.Download { - cid := track.OrigFileCid.String - if cid == "" { - cid = track.TrackCid.String - } - download, err = mediaLink(cid, track.TrackID, arg.MyID.(int32), nil) - if err != nil { - return nil, err - } - } - - var preview *MediaLink - if track.PreviewCid.String != "" { - preview, err = mediaLink(track.PreviewCid.String, track.TrackID, arg.MyID.(int32), id3Tags) - if err != nil { - return nil, err - } - } - - fullTrack := FullTrack{ - GetTracksRow: track, - IsStreamable: !track.IsDelete && !user.IsDeactivated, - Permalink: fmt.Sprintf("/%s/%s", user.Handle.String, track.Slug.String), - Artwork: squareImageStruct(track.CoverArtSizes, track.CoverArt), - Stream: stream, - Download: download, - Preview: preview, - User: user, - UserID: user.ID, - FolloweeFavorites: fullFolloweeFavorites(track.FolloweeFavorites), - FolloweeReposts: fullFolloweeReposts(track.FolloweeReposts), - RemixOf: fullRemixOf, - StreamConditions: track.StreamConditions, - DownloadConditions: track.DownloadConditions, - Access: access, - } - trackMap[track.TrackID] = fullTrack - } - - return trackMap, nil -} - -func (q *Queries) FullTracks(ctx context.Context, arg FullTracksParams) ([]FullTrack, error) { - trackMap, err := q.FullTracksKeyed(ctx, arg) - if err != nil { - return nil, err - } - - // return in same order as input list of ids - // some ids may be not found... - fullTracks := []FullTrack{} - for _, id := range arg.Ids { - if t, found := trackMap[id]; found { - fullTracks = append(fullTracks, t) - } - } - - return fullTracks, nil -} - -type MinTrack struct { - ID string `json:"id"` - Title pgtype.Text `json:"title"` - User User `json:"user"` - Artwork *SquareImage `json:"artwork"` - Duration pgtype.Int4 `json:"duration"` - Description pgtype.Text `json:"description"` - Genre pgtype.Text `json:"genre"` - TrackCid pgtype.Text `json:"track_cid"` - PreviewCid pgtype.Text `json:"preview_cid"` - OrigFileCid pgtype.Text `json:"orig_file_cid"` - OrigFilename pgtype.Text `json:"orig_filename"` - IsOriginalAvailable bool `json:"is_original_available"` - Mood pgtype.Text `json:"mood"` - ReleaseDate interface{} `json:"release_date"` - Isrc *pgtype.Text `json:"isrc"` - RemixOf interface{} `json:"remix_of"` - RepostCount int32 `json:"repost_count"` - FavoriteCount int32 `json:"favorite_count"` - CommentCount pgtype.Int4 `json:"comment_count"` - Tags pgtype.Text `json:"tags"` - IsDownloadable bool `json:"is_downloadable"` - PlayCount int64 `json:"play_count"` - PinnedCommentID pgtype.Int4 `json:"pinned_comment_id"` - PlaylistsContainingTrack []int32 `json:"playlists_containing_track"` - AlbumBacklink interface{} `json:"album_backlink"` - IsStreamable bool `json:"is_streamable"` - Permalink string `json:"permalink"` -} - -func ToMinTrack(fullTrack FullTrack) MinTrack { - var isrc *pgtype.Text - if fullTrack.Isrc.String != "" { - isrc = &fullTrack.Isrc - } else { - isrc = nil - } - return MinTrack{ - ID: fullTrack.ID, - Title: fullTrack.Title, - User: fullTrack.User, - Artwork: fullTrack.Artwork, - Duration: fullTrack.Duration, - Description: fullTrack.Description, - Genre: fullTrack.Genre, - TrackCid: fullTrack.TrackCid, - PreviewCid: fullTrack.PreviewCid, - OrigFileCid: fullTrack.OrigFileCid, - OrigFilename: fullTrack.OrigFilename, - IsOriginalAvailable: fullTrack.IsOriginalAvailable, - Mood: fullTrack.Mood, - ReleaseDate: fullTrack.ReleaseDate, - Isrc: isrc, - RemixOf: fullTrack.RemixOf, - RepostCount: fullTrack.RepostCount, - FavoriteCount: fullTrack.FavoriteCount, - CommentCount: fullTrack.CommentCount, - Tags: fullTrack.Tags, - IsDownloadable: fullTrack.IsDownloadable, - PlayCount: fullTrack.PlayCount, - PinnedCommentID: fullTrack.PinnedCommentID, - PlaylistsContainingTrack: fullTrack.PlaylistsContainingTrack, - AlbumBacklink: nil, - IsStreamable: !fullTrack.IsDelete && !fullTrack.User.IsDeactivated, - Permalink: fmt.Sprintf("/%s/%s", fullTrack.User.Handle.String, fullTrack.Slug.String), - } -} - -func ToMinTracks(fullTracks []FullTrack) []MinTrack { - result := make([]MinTrack, len(fullTracks)) - for i, track := range fullTracks { - result[i] = ToMinTrack(track) - } - return result -} diff --git a/api/dbv1/models.go b/api/dbv1/models.go index 3e20b4ec..cd5d69fc 100644 --- a/api/dbv1/models.go +++ b/api/dbv1/models.go @@ -2203,7 +2203,50 @@ type TagTrackUser struct { OwnerID int32 `json:"owner_id"` } -type Track struct { +type TrackDelistStatus struct { + CreatedAt pgtype.Timestamptz `json:"created_at"` + TrackID int32 `json:"track_id"` + OwnerID int32 `json:"owner_id"` + TrackCid string `json:"track_cid"` + Delisted bool `json:"delisted"` + Reason DelistTrackReason `json:"reason"` +} + +type TrackDownload struct { + Txhash string `json:"txhash"` + Blocknumber int32 `json:"blocknumber"` + ParentTrackID int32 `json:"parent_track_id"` + TrackID int32 `json:"track_id"` + UserID pgtype.Int4 `json:"user_id"` + CreatedAt time.Time `json:"created_at"` + City pgtype.Text `json:"city"` + Region pgtype.Text `json:"region"` + Country pgtype.Text `json:"country"` +} + +type TrackPriceHistory struct { + TrackID int32 `json:"track_id"` + Splits json.RawMessage `json:"splits"` + TotalPriceCents int64 `json:"total_price_cents"` + Blocknumber int32 `json:"blocknumber"` + BlockTimestamp time.Time `json:"block_timestamp"` + CreatedAt time.Time `json:"created_at"` + Access UsdcPurchaseAccessType `json:"access"` +} + +type TrackRoute struct { + Slug string `json:"slug"` + TitleSlug string `json:"title_slug"` + CollisionID int32 `json:"collision_id"` + OwnerID int32 `json:"owner_id"` + TrackID int32 `json:"track_id"` + IsCurrent bool `json:"is_current"` + Blockhash string `json:"blockhash"` + Blocknumber int32 `json:"blocknumber"` + Txhash string `json:"txhash"` +} + +type TrackRow struct { Blockhash pgtype.Text `json:"blockhash"` TrackID int32 `json:"track_id"` IsCurrent bool `json:"is_current"` @@ -2283,49 +2326,6 @@ type Track struct { TerritoryCodes []string `json:"territory_codes"` } -type TrackDelistStatus struct { - CreatedAt pgtype.Timestamptz `json:"created_at"` - TrackID int32 `json:"track_id"` - OwnerID int32 `json:"owner_id"` - TrackCid string `json:"track_cid"` - Delisted bool `json:"delisted"` - Reason DelistTrackReason `json:"reason"` -} - -type TrackDownload struct { - Txhash string `json:"txhash"` - Blocknumber int32 `json:"blocknumber"` - ParentTrackID int32 `json:"parent_track_id"` - TrackID int32 `json:"track_id"` - UserID pgtype.Int4 `json:"user_id"` - CreatedAt time.Time `json:"created_at"` - City pgtype.Text `json:"city"` - Region pgtype.Text `json:"region"` - Country pgtype.Text `json:"country"` -} - -type TrackPriceHistory struct { - TrackID int32 `json:"track_id"` - Splits json.RawMessage `json:"splits"` - TotalPriceCents int64 `json:"total_price_cents"` - Blocknumber int32 `json:"blocknumber"` - BlockTimestamp time.Time `json:"block_timestamp"` - CreatedAt time.Time `json:"created_at"` - Access UsdcPurchaseAccessType `json:"access"` -} - -type TrackRoute struct { - Slug string `json:"slug"` - TitleSlug string `json:"title_slug"` - CollisionID int32 `json:"collision_id"` - OwnerID int32 `json:"owner_id"` - TrackID int32 `json:"track_id"` - IsCurrent bool `json:"is_current"` - Blockhash string `json:"blockhash"` - Blocknumber int32 `json:"blocknumber"` - Txhash string `json:"txhash"` -} - type TrackTrendingScore struct { TrackID int32 `json:"track_id"` Type string `json:"type"` diff --git a/api/dbv1/parallel.go b/api/dbv1/parallel.go index 7b893c0a..cfdece3f 100644 --- a/api/dbv1/parallel.go +++ b/api/dbv1/parallel.go @@ -15,7 +15,7 @@ type ParallelParams struct { type ParallelResult struct { UserMap map[int32]User - TrackMap map[int32]FullTrack + TrackMap map[int32]Track PlaylistMap map[int32]FullPlaylist } @@ -23,7 +23,7 @@ func (q *Queries) Parallel(ctx context.Context, arg ParallelParams) (*ParallelRe g, ctx := errgroup.WithContext(ctx) var userMap map[int32]User - var trackMap map[int32]FullTrack + var trackMap map[int32]Track var playlistMap map[int32]FullPlaylist if len(arg.UserIds) > 0 { @@ -40,7 +40,7 @@ func (q *Queries) Parallel(ctx context.Context, arg ParallelParams) (*ParallelRe if len(arg.TrackIds) > 0 { g.Go(func() error { var err error - trackMap, err = q.FullTracksKeyed(ctx, FullTracksParams{ + trackMap, err = q.TracksKeyed(ctx, TracksParams{ GetTracksParams: GetTracksParams{ Ids: arg.TrackIds, MyID: arg.MyID, @@ -85,8 +85,8 @@ func (r *ParallelResult) UserList() []User { return userList } -func (r *ParallelResult) TrackList() []FullTrack { - trackList := make([]FullTrack, 0, len(r.TrackMap)) +func (r *ParallelResult) TrackList() []Track { + trackList := make([]Track, 0, len(r.TrackMap)) for _, t := range r.TrackMap { trackList = append(trackList, t) } diff --git a/api/dbv1/tracks.go b/api/dbv1/tracks.go new file mode 100644 index 00000000..e2a0f57e --- /dev/null +++ b/api/dbv1/tracks.go @@ -0,0 +1,202 @@ +package dbv1 + +import ( + "context" + "encoding/json" + "fmt" + + "api.audius.co/trashid" +) + +type TracksParams struct { + GetTracksParams +} + +// Track is the standard track type containing all track data +type Track struct { + GetTracksRow + + Permalink string `json:"permalink"` + IsStreamable bool `json:"is_streamable"` + Artwork *SquareImage `json:"artwork"` + Stream *MediaLink `json:"stream"` + Download *MediaLink `json:"download"` + Preview *MediaLink `json:"preview"` + UserID trashid.HashId `json:"user_id"` + User User `json:"user"` + Access Access `json:"access"` + + FolloweeReposts []*FolloweeRepost `json:"followee_reposts"` + FolloweeFavorites []*FolloweeFavorite `json:"followee_favorites"` + RemixOf FullRemixOf `json:"remix_of"` + StreamConditions *AccessGate `json:"stream_conditions"` + DownloadConditions *AccessGate `json:"download_conditions"` +} + +func (q *Queries) TracksKeyed(ctx context.Context, arg TracksParams) (map[int32]Track, error) { + rawTracks, err := q.GetTracks(ctx, GetTracksParams(arg.GetTracksParams)) + if err != nil { + return nil, err + } + + userIds := []int32{} + collectSplitUserIds := func(usage *AccessGate) { + if usage == nil || usage.UsdcPurchase == nil { + return + } + for _, split := range usage.UsdcPurchase.Splits { + userIds = append(userIds, split.UserID) + } + } + + for _, rawTrack := range rawTracks { + userIds = append(userIds, rawTrack.UserID) + + var remixOf RemixOf + json.Unmarshal(rawTrack.RemixOf, &remixOf) + for _, r := range remixOf.Tracks { + userIds = append(userIds, r.ParentUserId) + } + + collectSplitUserIds(rawTrack.StreamConditions) + collectSplitUserIds(rawTrack.DownloadConditions) + } + + userMap, err := q.UsersKeyed(ctx, GetUsersParams{ + MyID: arg.MyID.(int32), + Ids: userIds, + }) + if err != nil { + return nil, err + } + + // Convert rawTracks to pointers + trackPtrs := make([]*GetTracksRow, len(rawTracks)) + for i := range rawTracks { + trackPtrs[i] = &rawTracks[i] + } + + // Convert userMap to pointers + userPtrMap := make(map[int32]*User) + for id, user := range userMap { + userCopy := user // Create a copy to avoid modifying the original + userPtrMap[id] = &userCopy + } + + // Get bulk access for all tracks + accessMap, err := q.GetBulkTrackAccess(ctx, arg.MyID.(int32), trackPtrs, userPtrMap) + if err != nil { + return nil, err + } + + trackMap := map[int32]Track{} + for _, rawTrack := range rawTracks { + rawTrack.ID, _ = trashid.EncodeHashId(int(rawTrack.TrackID)) + user, ok := userMap[rawTrack.UserID] + if !ok { + continue + } + + if rawTrack.FieldVisibility == nil || string(rawTrack.FieldVisibility) == "null" { + rawTrack.FieldVisibility = []byte(`{ + "mood":null, + "tags":null, + "genre":null, + "share":null, + "play_count":null, + "remixes":null + }`) + } + + var remixOf RemixOf + var fullRemixOf FullRemixOf + json.Unmarshal(rawTrack.RemixOf, &remixOf) + fullRemixOf = FullRemixOf{ + Tracks: make([]FullRemixOfTrack, len(remixOf.Tracks)), + } + for idx, r := range remixOf.Tracks { + trackId, _ := trashid.EncodeHashId(int(r.ParentTrackId)) + fullRemixOf.Tracks[idx] = FullRemixOfTrack{ + HasRemixAuthorReposted: r.HasRemixAuthorReposted, + HasRemixAuthorSaved: r.HasRemixAuthorSaved, + ParentTrackId: trackId, + User: userMap[r.ParentUserId], + } + } + + // Get access from the bulk access map + access := accessMap[rawTrack.TrackID] + + id3Tags := &Id3Tags{ + Title: rawTrack.Title.String, + Artist: user.Name.String, + } + + var stream *MediaLink + if access.Stream { + stream, err = mediaLink(rawTrack.TrackCid.String, rawTrack.TrackID, arg.MyID.(int32), id3Tags) + if err != nil { + return nil, err + } + } + + var download *MediaLink + if rawTrack.IsDownloadable && access.Download { + cid := rawTrack.OrigFileCid.String + if cid == "" { + cid = rawTrack.TrackCid.String + } + download, err = mediaLink(cid, rawTrack.TrackID, arg.MyID.(int32), nil) + if err != nil { + return nil, err + } + } + + var preview *MediaLink + if rawTrack.PreviewCid.String != "" { + preview, err = mediaLink(rawTrack.PreviewCid.String, rawTrack.TrackID, arg.MyID.(int32), id3Tags) + if err != nil { + return nil, err + } + } + + track := Track{ + GetTracksRow: rawTrack, + IsStreamable: !rawTrack.IsDelete && !user.IsDeactivated, + Permalink: fmt.Sprintf("/%s/%s", user.Handle.String, rawTrack.Slug.String), + Artwork: squareImageStruct(rawTrack.CoverArtSizes, rawTrack.CoverArt), + Stream: stream, + Download: download, + Preview: preview, + User: user, + UserID: user.ID, + FolloweeFavorites: fullFolloweeFavorites(rawTrack.FolloweeFavorites), + FolloweeReposts: fullFolloweeReposts(rawTrack.FolloweeReposts), + RemixOf: fullRemixOf, + StreamConditions: rawTrack.StreamConditions, + DownloadConditions: rawTrack.DownloadConditions, + Access: access, + } + trackMap[rawTrack.TrackID] = track + } + + return trackMap, nil +} + +func (q *Queries) Tracks(ctx context.Context, arg TracksParams) ([]Track, error) { + trackMap, err := q.TracksKeyed(ctx, arg) + if err != nil { + return nil, err + } + + // return in same order as input list of ids + // some ids may be not found... + tracks := []Track{} + for _, id := range arg.Ids { + if t, found := trackMap[id]; found { + tracks = append(tracks, t) + } + } + + return tracks, nil +} diff --git a/api/response_helpers.go b/api/response_helpers.go index cead703f..349279ac 100644 --- a/api/response_helpers.go +++ b/api/response_helpers.go @@ -51,25 +51,25 @@ func v1PlaylistsResponse(c *fiber.Ctx, playlists []dbv1.FullPlaylist) error { }) } -func v1TrackResponse(c *fiber.Ctx, track dbv1.FullTrack) error { +func v1TrackResponse(c *fiber.Ctx, track dbv1.Track) error { if c.Locals("isFull").(bool) { return c.JSON(fiber.Map{ "data": track, }) } return c.JSON(fiber.Map{ - "data": dbv1.ToMinTrack(track), + "data": track, }) } -func v1TracksResponse(c *fiber.Ctx, tracks []dbv1.FullTrack) error { +func v1TracksResponse(c *fiber.Ctx, tracks []dbv1.Track) error { if c.Locals("isFull").(bool) { return c.JSON(fiber.Map{ "data": tracks, }) } return c.JSON(fiber.Map{ - "data": dbv1.ToMinTracks(tracks), + "data": tracks, }) } diff --git a/api/swagger/swagger-v1.yaml b/api/swagger/swagger-v1.yaml index 27720356..5e4e6342 100644 --- a/api/swagger/swagger-v1.yaml +++ b/api/swagger/swagger-v1.yaml @@ -923,6 +923,11 @@ paths: type: array items: type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string responses: "200": description: Success @@ -1003,6 +1008,108 @@ paths: application/json: schema: $ref: '#/components/schemas/tracks_response' + /tracks/recommended: + get: + tags: + - tracks + description: Get recommended tracks + operationId: Get Recommended Tracks + parameters: + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + - name: exclusion_list + in: query + description: List of track ids to exclude + style: form + explode: true + schema: + type: array + items: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /tracks/recommended/{version}: + get: + tags: + - tracks + description: Get recommended tracks using the given trending strategy version + operationId: Get Recommended Tracks With Version + parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: + type: string + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + - name: exclusion_list + in: query + description: List of track ids to exclude + style: form + explode: true + schema: + type: array + items: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' /tracks/feeling-lucky: get: tags: @@ -1023,6 +1130,13 @@ paths: default: 10 minimum: 1 maximum: 100 + - name: with_users + deprecated: true + in: query + description: Boolean to include user info with tracks + schema: + type: boolean + default: false - name: min_followers in: query description: Fetch tracks from users with at least this number of followers @@ -1129,6 +1243,107 @@ paths: "500": description: Server error content: {} + /tracks/usdc-purchase: + get: + tags: + - tracks + description: Gets the top trending (most popular) USDC purchase tracks on Audius + operationId: Get Trending USDC Purchase Tracks + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} + /tracks/usdc-purchase/{version}: + get: + tags: + - tracks + description: Gets the top trending (most popular) USDC purchase tracks on Audius using a given trending strategy version + operationId: Get Trending USDC Purchase Tracks With Version + parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' /tracks/search: get: tags: @@ -1214,10 +1429,147 @@ paths: in: query description: Only include tracks that have a bpm greater than or equal to schema: - type: string - - name: bpm_max + type: string + - name: bpm_max + in: query + description: Only include tracks that have a bpm less than or equal to + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/track_search' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} + /tracks/trending: + get: + tags: + - tracks + description: Gets the top 100 trending (most popular) tracks on Audius + operationId: Get Trending Tracks + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} + /tracks/trending/{version}: + get: + tags: + - tracks + description: Gets the top 100 trending (most popular) tracks on Audius using a given trending strategy version + operationId: Get Trending Tracks With Version + parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: genre + in: query + description: Filter trending to a specified genre + schema: + type: string + - name: time + in: query + description: Calculate trending over a specified time range + schema: + type: string + enum: + - week + - month + - year + - allTime + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /tracks/trending/ids: + get: + tags: + - tracks + description: Gets the track IDs of the top trending tracks on Audius + operationId: Get Trending Track IDs + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: genre in: query - description: Only include tracks that have a bpm less than or equal to + description: Filter trending to a specified genre schema: type: string responses: @@ -1226,24 +1578,29 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/track_search' + $ref: '#/components/schemas/trending_ids_response' "400": description: Bad request content: {} "500": description: Server error content: {} - /tracks/trending: + /tracks/trending/ids/{version}: get: tags: - tracks - description: Gets the top 100 trending (most popular) tracks on Audius - operationId: Get Trending Tracks + description: Gets the track IDs of the top trending tracks on Audius based on the given trending strategy version + operationId: Get Trending Tracks IDs With Version parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: + type: string - name: offset in: query - description: The number of items to skip. Useful for pagination (page number - * limit) + description: The number of items to skip. Useful for pagination (page number * limit) schema: type: integer - name: limit @@ -1256,23 +1613,13 @@ paths: description: Filter trending to a specified genre schema: type: string - - name: time - in: query - description: Calculate trending over a specified time range - schema: - type: string - enum: - - week - - month - - year - - allTime responses: "200": description: Success content: application/json: schema: - $ref: '#/components/schemas/tracks_response' + $ref: '#/components/schemas/trending_ids_response' "400": description: Bad request content: {} @@ -1297,6 +1644,46 @@ paths: description: The number of items to fetch schema: type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /tracks/trending/underground/{version}: + get: + tags: + - tracks + description: Gets the top 100 trending underground tracks on Audius using a given trending strategy version + operationId: Get Underground Trending Tracks With Version + parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string responses: "200": description: Success @@ -1317,6 +1704,11 @@ paths: required: true schema: type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string responses: "200": description: Success @@ -1470,6 +1862,182 @@ paths: "500": description: Server error content: {} + /tracks/{track_id}/favorites: + get: + tags: + - tracks + description: Get users that favorited a track + operationId: Get Users From Favorites + parameters: + - name: track_id + in: path + description: A Track ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/track_favorites_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} + /tracks/{track_id}/remixes: + get: + tags: + - tracks + description: Get all tracks that remix the given track + operationId: Get Track Remixes + parameters: + - name: track_id + in: path + description: A Track ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: sort_method + in: query + description: The sort method to use + schema: + type: string + default: recent + enum: + - likes + - plays + - recent + - name: only_cosigns + in: query + description: Only remixes cosigned by the original artist + schema: + type: boolean + - name: only_contest_entries + in: query + description: Only entries to a remix contest + schema: + type: boolean + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/remixes_response' + /tracks/{track_id}/remixing: + get: + tags: + - tracks + description: Gets all the tracks that the given track remixes + operationId: Get Track Remix Parents + parameters: + - name: track_id + in: path + description: A Track ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/remixing_response' + /tracks/{track_id}/reposts: + get: + tags: + - tracks + description: Get the users that reposted a track + operationId: Get Users From Reposts + parameters: + - name: track_id + in: path + description: A Track ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/track_reposts_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} /tracks/{track_id}/download: get: tags: @@ -1789,19 +2357,117 @@ paths: description: A User handle required: true schema: - type: string + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/user_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} + /users/handle/{handle}/tracks: + get: + tags: + - users + description: Gets the tracks created by a user using the user's handle + operationId: Get Tracks by User Handle + parameters: + - name: handle + in: path + description: A User handle + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer - name: user_id in: query description: The user ID of the user making the request schema: type: string + - name: sort + in: query + description: "[Deprecated] Field to sort by" + schema: + type: string + default: date + enum: + - date + - plays + - name: query + in: query + description: The filter query + schema: + type: string + - name: sort_method + in: query + description: The sort method + schema: + type: string + enum: + - title + - artist_name + - release_date + - last_listen_date + - added_date + - plays + - reposts + - saves + - most_listens_by_user + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: filter_tracks + in: query + description: Filter by public tracks + schema: + type: string + default: all + enum: + - all + - public + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: "The signature of data, used for signature recovery" + schema: + type: string responses: "200": description: Success content: application/json: schema: - $ref: '#/components/schemas/user_response' + $ref: '#/components/schemas/tracks_response' "400": description: Bad request content: {} @@ -3479,6 +4145,72 @@ paths: "500": description: Server error content: {} + /users/{id}/tracks/count: + get: + tags: + - users + description: Gets the count of tracks created by a user + operationId: Get Tracks Count by User + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: filter_tracks + in: query + description: Filter by public tracks + schema: + type: string + default: all + enum: + - all + - public + - name: gate_condition + in: query + description: Filter by gate conditions (can be repeated) + style: form + explode: true + schema: + type: array + items: + type: string + enum: + - ungated + - usdc_purchase + - follow + - tip + - nft + - token + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: "The signature of data, used for signature recovery" + schema: + type: string + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_count_response' + "400": + description: Bad request + content: {} + "500": + description: Server error + content: {} /users/{id}/tracks/remixed: get: tags: @@ -4486,6 +5218,120 @@ components: type: integer album_backlink: $ref: '#/components/schemas/album_backlink' + access: + $ref: '#/components/schemas/track_access_info' + ai_attribution_user_id: + type: integer + allowed_api_keys: + type: array + items: + type: string + artists: + type: object + audio_analysis_error_count: + type: integer + audio_upload_id: + type: string + blocknumber: + type: integer + bpm: + type: number + comments_disabled: + type: boolean + copyright_line: + type: object + cover_art: + type: string + cover_art_sizes: + type: string + cover_original_artist: + type: string + cover_original_song_title: + type: string + create_date: + type: string + created_at: + type: string + format: date-time + credits_splits: + type: string + ddex_release_ids: + type: object + download: + $ref: '#/components/schemas/media_link' + download_conditions: + $ref: '#/components/schemas/access_gate' + field_visibility: + type: object + followee_favorites: + type: array + items: + type: object + followee_reposts: + type: array + items: + type: object + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + indirect_resource_contributors: + type: object + is_available: + type: boolean + is_custom_bpm: + type: boolean + is_custom_musical_key: + type: boolean + is_delete: + type: boolean + is_download_gated: + type: boolean + is_owned_by_user: + type: boolean + is_scheduled_release: + type: boolean + is_stream_gated: + type: boolean + is_unlisted: + type: boolean + iswc: + type: string + license: + type: string + musical_key: + type: string + parental_warning_type: + type: string + playlists_previously_containing_track: + type: object + preview: + $ref: '#/components/schemas/media_link' + preview_start_seconds: + type: number + producer_copyright_line: + type: object + resource_contributors: + type: object + rights_controller: + type: object + slug: + type: string + stem_of: + type: array + items: + type: object + stream: + $ref: '#/components/schemas/media_link' + stream_conditions: + $ref: '#/components/schemas/access_gate' + track_id: + type: integer + track_segments: + type: object + updated_at: + type: string + format: date-time track_artwork: type: object properties: @@ -5410,6 +6256,28 @@ components: description: Must hold an NFT of the given collection to unlock allOf: - $ref: '#/components/schemas/nft_collection' + media_link: + type: object + properties: + url: + type: string + mirrors: + type: array + items: + type: string + access_gate: + type: object + properties: + usdc_purchase: + $ref: '#/components/schemas/extended_purchase_gate' + follow_user_id: + type: integer + tip_user_id: + type: integer + nft_collection: + type: object + token_gate: + $ref: '#/components/schemas/extended_token_gate' nft_collection: required: - address @@ -5471,6 +6339,70 @@ components: type: array items: $ref: '#/components/schemas/comment' + trending_ids_response: + type: object + properties: + data: + $ref: '#/components/schemas/trending_times_ids' + trending_times_ids: + type: object + properties: + week: + type: array + items: + $ref: '#/components/schemas/track_id' + month: + type: array + items: + $ref: '#/components/schemas/track_id' + year: + type: array + items: + $ref: '#/components/schemas/track_id' + track_id: + required: + - id + type: object + properties: + id: + type: string + track_favorites_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user' + track_reposts_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user' + remixes_response: + required: + - count + type: object + properties: + count: + type: integer + tracks: + type: array + items: + $ref: '#/components/schemas/Track' + remixing_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Track' + tracks_count_response: + type: object + properties: + data: + type: integer track_comment_count_response: type: object properties: diff --git a/api/v1_explore_best_selling_test.go b/api/v1_explore_best_selling_test.go index 06366425..4bc49f22 100644 --- a/api/v1_explore_best_selling_test.go +++ b/api/v1_explore_best_selling_test.go @@ -284,7 +284,7 @@ func TestExploreBestSelling(t *testing.T) { var BestSellingResponse struct { Data []BestSellingItem Related struct { - Tracks []dbv1.FullTrack + Tracks []dbv1.Track Playlists []dbv1.FullPlaylist } } diff --git a/api/v1_playlist_tracks.go b/api/v1_playlist_tracks.go index 4332b065..1003cdb9 100644 --- a/api/v1_playlist_tracks.go +++ b/api/v1_playlist_tracks.go @@ -56,7 +56,7 @@ func (app *ApiServer) v1PlaylistTracks(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_search.go b/api/v1_search.go index 63315bdc..f15cb09e 100644 --- a/api/v1_search.go +++ b/api/v1_search.go @@ -15,7 +15,7 @@ func (app *ApiServer) v1SearchFull(c *fiber.Ctx) error { g := errgroup.Group{} var users = []dbv1.User{} - var tracks = []dbv1.FullTrack{} + var tracks = []dbv1.Track{} var playlists = []dbv1.FullPlaylist{} var albums = []dbv1.FullPlaylist{} @@ -117,7 +117,7 @@ func (app *ApiServer) searchUsers(c *fiber.Ctx) ([]dbv1.User, error) { return users, err } -func (app *ApiServer) searchTracks(c *fiber.Ctx) ([]dbv1.FullTrack, error) { +func (app *ApiServer) searchTracks(c *fiber.Ctx) ([]dbv1.Track, error) { isTagSearch := strings.Contains(c.Route().Path, "search/tags") isFullSearch := strings.Contains(c.Route().Path, "search/full") limit := c.QueryInt("limit", 10) @@ -169,7 +169,7 @@ func (app *ApiServer) searchTracks(c *fiber.Ctx) ([]dbv1.FullTrack, error) { myId = 0 } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: tracksIds, MyID: myId, diff --git a/api/v1_track.go b/api/v1_track.go index a9c7e387..abb733d8 100644 --- a/api/v1_track.go +++ b/api/v1_track.go @@ -9,7 +9,7 @@ func (app *ApiServer) v1Track(c *fiber.Ctx) error { myId := app.getMyId(c) trackId := c.Locals("trackId").(int) - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: []int32{int32(trackId)}, diff --git a/api/v1_track_access_info.go b/api/v1_track_access_info.go index f2c81571..74307fc2 100644 --- a/api/v1_track_access_info.go +++ b/api/v1_track_access_info.go @@ -54,7 +54,7 @@ func (app *ApiServer) v1TrackAccessInfo(c *fiber.Ctx) error { trackId := c.Locals("trackId").(int) // Get the track with extended information - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: []int32{int32(trackId)}, diff --git a/api/v1_track_download.go b/api/v1_track_download.go index 719c1bf3..c82bb04b 100644 --- a/api/v1_track_download.go +++ b/api/v1_track_download.go @@ -5,7 +5,7 @@ import ( "github.com/gofiber/fiber/v2" ) -func createFilename(track *dbv1.FullTrack) string { +func createFilename(track *dbv1.Track) string { filename := track.OrigFilename.String if filename == "" && track.OrigFileCid.String == "" { filename = track.Title.String + ".mp3" @@ -25,7 +25,7 @@ func (app *ApiServer) v1TrackDownload(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: []int32{int32(trackId)}, diff --git a/api/v1_track_inspect.go b/api/v1_track_inspect.go index ff9d4a60..05f308f8 100644 --- a/api/v1_track_inspect.go +++ b/api/v1_track_inspect.go @@ -21,7 +21,7 @@ type inspectResponse struct { ContentType string `json:"content_type"` } -func inspectTrack(track dbv1.FullTrack, original bool) (*inspectResponse, error) { +func inspectTrack(track dbv1.Track, original bool) (*inspectResponse, error) { var cid string if original { cid = track.OrigFileCid.String @@ -73,7 +73,7 @@ func (app *ApiServer) v1TrackInspect(c *fiber.Ctx) error { trackId := c.Locals("trackId").(int) original := c.Query("original") == "true" - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: []int32{int32(trackId)}, @@ -103,7 +103,7 @@ func (app *ApiServer) v1TracksInspect(c *fiber.Ctx) error { ids := decodeIdList(c) original := c.Query("original") == "true" - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: ids, diff --git a/api/v1_track_remixes.go b/api/v1_track_remixes.go index e767a78b..8970351a 100644 --- a/api/v1_track_remixes.go +++ b/api/v1_track_remixes.go @@ -116,7 +116,7 @@ func (app *ApiServer) v1TrackRemixes(c *fiber.Ctx) error { ids[i] = result.TrackID } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: ids, MyID: myId, diff --git a/api/v1_track_remixing.go b/api/v1_track_remixing.go index 5a7c3b60..e05af564 100644 --- a/api/v1_track_remixing.go +++ b/api/v1_track_remixing.go @@ -48,7 +48,7 @@ func (app *ApiServer) v1TrackRemixing(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_track_stream.go b/api/v1_track_stream.go index 6f318c3c..649a7b93 100644 --- a/api/v1_track_stream.go +++ b/api/v1_track_stream.go @@ -9,7 +9,7 @@ func (app *ApiServer) v1TrackStream(c *fiber.Ctx) error { myId := app.getMyId(c) trackId := c.Locals("trackId").(int) - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: myId, Ids: []int32{int32(trackId)}, diff --git a/api/v1_track_test.go b/api/v1_track_test.go index 3a9e66c5..6d2803bf 100644 --- a/api/v1_track_test.go +++ b/api/v1_track_test.go @@ -11,7 +11,7 @@ import ( func TestGetTrack(t *testing.T) { app := testAppWithFixtures(t) var trackResponse struct { - Data dbv1.FullTrack + Data dbv1.Track } status, body := testGet(t, app, "/v1/full/tracks/eYJyn", &trackResponse) @@ -27,7 +27,7 @@ func TestGetTrack(t *testing.T) { func TestGetTrackFollowDownloadAcess(t *testing.T) { app := testAppWithFixtures(t) var trackResponse struct { - Data dbv1.FullTrack + Data dbv1.Track } // No access _, body1 := testGet(t, app, "/v1/full/tracks/eYRWn", &trackResponse) @@ -54,7 +54,7 @@ func TestGetTrackFollowDownloadAcess(t *testing.T) { func TestGetTrackTipStreamAccess(t *testing.T) { app := testAppWithFixtures(t) var trackResponse struct { - Data dbv1.FullTrack + Data dbv1.Track } // No access _, body1 := testGet(t, app, "/v1/full/tracks/L5x7n", &trackResponse) @@ -81,7 +81,7 @@ func TestGetTrackTipStreamAccess(t *testing.T) { func TestGetTrackUsdcPurchaseStreamAccess(t *testing.T) { app := testAppWithFixtures(t) var trackResponse struct { - Data dbv1.FullTrack + Data dbv1.Track } // No access _, body1 := testGet(t, app, "/v1/full/tracks/ebdJL", &trackResponse) @@ -108,7 +108,7 @@ func TestGetTrackUsdcPurchaseStreamAccess(t *testing.T) { func TestGetTrackUsdcPurchaseSelfAccess(t *testing.T) { app := testAppWithFixtures(t) var trackResponse struct { - Data dbv1.FullTrack + Data dbv1.Track } // No access. User 3 is the owner, but has not signed authorization status, _ := testGet( diff --git a/api/v1_tracks.go b/api/v1_tracks.go index 846837bb..1f4ebd4a 100644 --- a/api/v1_tracks.go +++ b/api/v1_tracks.go @@ -48,7 +48,7 @@ func (app *ApiServer) v1Tracks(c *fiber.Ctx) error { ids = append(ids, newIds...) } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ MyID: int32(myId), Ids: ids, diff --git a/api/v1_tracks_feeling_lucky.go b/api/v1_tracks_feeling_lucky.go index 2f76345d..a7915fed 100644 --- a/api/v1_tracks_feeling_lucky.go +++ b/api/v1_tracks_feeling_lucky.go @@ -61,7 +61,7 @@ func (app *ApiServer) v1TracksFeelingLucky(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: app.getMyId(c), diff --git a/api/v1_tracks_feeling_lucky_test.go b/api/v1_tracks_feeling_lucky_test.go index 4a334744..e240d81a 100644 --- a/api/v1_tracks_feeling_lucky_test.go +++ b/api/v1_tracks_feeling_lucky_test.go @@ -77,7 +77,7 @@ func TestV1TracksFeelingLucky(t *testing.T) { { var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, body := testGet(t, app, "/v1/tracks/feeling-lucky", &resp) diff --git a/api/v1_tracks_most_shared.go b/api/v1_tracks_most_shared.go index 7784d461..d42043e0 100644 --- a/api/v1_tracks_most_shared.go +++ b/api/v1_tracks_most_shared.go @@ -67,7 +67,7 @@ func (app *ApiServer) v1TracksMostShared(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_recent_comments.go b/api/v1_tracks_recent_comments.go index 1ce72b8f..d9722410 100644 --- a/api/v1_tracks_recent_comments.go +++ b/api/v1_tracks_recent_comments.go @@ -49,7 +49,7 @@ func (app *ApiServer) v1TracksRecentComments(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_recent_premium.go b/api/v1_tracks_recent_premium.go index 44ab00e6..83d5cf74 100644 --- a/api/v1_tracks_recent_premium.go +++ b/api/v1_tracks_recent_premium.go @@ -51,7 +51,7 @@ func (app *ApiServer) v1TracksRecentPremium(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_test.go b/api/v1_tracks_test.go index b405cbe6..8352bb2c 100644 --- a/api/v1_tracks_test.go +++ b/api/v1_tracks_test.go @@ -10,7 +10,7 @@ import ( func TestTracksEndpoint(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, body := testGet(t, app, "/v1/full/tracks?id=eYZmn", &resp) diff --git a/api/v1_tracks_trending.go b/api/v1_tracks_trending.go index 3889efaf..ac1a5969 100644 --- a/api/v1_tracks_trending.go +++ b/api/v1_tracks_trending.go @@ -32,7 +32,7 @@ func (app *ApiServer) v1TracksTrending(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_trending_test.go b/api/v1_tracks_trending_test.go index faca90bd..fb8afc42 100644 --- a/api/v1_tracks_trending_test.go +++ b/api/v1_tracks_trending_test.go @@ -11,7 +11,7 @@ import ( func TestGetTrending(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, _ := testGet(t, app, "/v1/tracks/trending", &resp) assert.Equal(t, 200, status) @@ -29,7 +29,7 @@ func TestGetTrending(t *testing.T) { func TestGetTrendingElectronic(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, _ := testGet(t, app, "/v1/tracks/trending?genre=Electronic", &resp) assert.Equal(t, 200, status) @@ -44,7 +44,7 @@ func TestGetTrendingElectronic(t *testing.T) { func TestGetTrendingAllTime(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, _ := testGet(t, app, "/v1/tracks/trending?time=allTime", &resp) assert.Equal(t, 200, status) diff --git a/api/v1_tracks_trending_underground.go b/api/v1_tracks_trending_underground.go index f7b6fb54..659f3de7 100644 --- a/api/v1_tracks_trending_underground.go +++ b/api/v1_tracks_trending_underground.go @@ -82,7 +82,7 @@ func (app *ApiServer) v1TracksTrendingUnderground(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_trending_underground_test.go b/api/v1_tracks_trending_underground_test.go index c08ed9b4..a20eb6c0 100644 --- a/api/v1_tracks_trending_underground_test.go +++ b/api/v1_tracks_trending_underground_test.go @@ -11,7 +11,7 @@ import ( func TestGetTrendingUnderground(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, body := testGet(t, app, "/v1/tracks/trending/underground", &resp) diff --git a/api/v1_tracks_usdc_purchase.go b/api/v1_tracks_usdc_purchase.go index 62ba7034..ca7fd80f 100644 --- a/api/v1_tracks_usdc_purchase.go +++ b/api/v1_tracks_usdc_purchase.go @@ -57,7 +57,7 @@ func (app *ApiServer) v1TracksUsdcPurchase(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_tracks_usdc_purchase_test.go b/api/v1_tracks_usdc_purchase_test.go index 1a24a0df..a5cacbd2 100644 --- a/api/v1_tracks_usdc_purchase_test.go +++ b/api/v1_tracks_usdc_purchase_test.go @@ -11,7 +11,7 @@ import ( func TestGetUsdcPurchase(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, body := testGet(t, app, "/v1/tracks/usdc-purchase", &resp) diff --git a/api/v1_users_history.go b/api/v1_users_history.go index a8b91aa7..bc93ce85 100644 --- a/api/v1_users_history.go +++ b/api/v1_users_history.go @@ -131,7 +131,7 @@ func (app *ApiServer) v1UsersHistory(c *fiber.Ctx) error { } // get tracks - tracks, err := app.queries.FullTracksKeyed(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.TracksKeyed(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, @@ -146,11 +146,7 @@ func (app *ApiServer) v1UsersHistory(c *fiber.Ctx) error { filteredItems := []Activity{} for _, item := range items { if t, ok := tracks[item.ItemID]; ok { - if app.getIsFull(c) { - item.Item = t - } else { - item.Item = dbv1.ToMinTrack(t) - } + item.Item = t filteredItems = append(filteredItems, item) } } diff --git a/api/v1_users_library_tracks.go b/api/v1_users_library_tracks.go index a3a18853..0b6598d8 100644 --- a/api/v1_users_library_tracks.go +++ b/api/v1_users_library_tracks.go @@ -157,7 +157,7 @@ func (app *ApiServer) v1UsersLibraryTracks(c *fiber.Ctx) error { } // get tracks - include unlisted tracks since they may be in the library (e.g., purchases) - tracks, err := app.queries.FullTracksKeyed(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.TracksKeyed(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_users_recommended_tracks.go b/api/v1_users_recommended_tracks.go index 10d7a63c..b13556ff 100644 --- a/api/v1_users_recommended_tracks.go +++ b/api/v1_users_recommended_tracks.go @@ -97,7 +97,7 @@ func (app *ApiServer) v1UsersRecommendedTracks(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: trackIds, MyID: myId, diff --git a/api/v1_users_recommended_tracks_test.go b/api/v1_users_recommended_tracks_test.go index 5507519e..4ed702ba 100644 --- a/api/v1_users_recommended_tracks_test.go +++ b/api/v1_users_recommended_tracks_test.go @@ -64,7 +64,7 @@ func TestV1UsersRecommendedTracks(t *testing.T) { }) var response struct { - Data []dbv1.FullTrack + Data []dbv1.Track } status, body := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks", &response) diff --git a/api/v1_users_tracks.go b/api/v1_users_tracks.go index adac32aa..944b9eff 100644 --- a/api/v1_users_tracks.go +++ b/api/v1_users_tracks.go @@ -98,7 +98,7 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: ids, MyID: myId, diff --git a/api/v1_users_tracks_ai_attributed.go b/api/v1_users_tracks_ai_attributed.go index 3165d14d..ebfb32a2 100644 --- a/api/v1_users_tracks_ai_attributed.go +++ b/api/v1_users_tracks_ai_attributed.go @@ -93,7 +93,7 @@ func (app *ApiServer) v1UserTracksAiAttributed(c *fiber.Ctx) error { return err } - tracks, err := app.queries.FullTracks(c.Context(), dbv1.FullTracksParams{ + tracks, err := app.queries.Tracks(c.Context(), dbv1.TracksParams{ GetTracksParams: dbv1.GetTracksParams{ Ids: ids, MyID: myId, diff --git a/api/v1_users_tracks_ai_attributed_test.go b/api/v1_users_tracks_ai_attributed_test.go index 9219de7a..ec57b0e9 100644 --- a/api/v1_users_tracks_ai_attributed_test.go +++ b/api/v1_users_tracks_ai_attributed_test.go @@ -118,7 +118,7 @@ func TestGetUserTracksAiAttributed(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userTracksResponse struct { - Data []dbv1.FullTrack + Data []dbv1.Track } baseUrl := "/v1/full/users/handle/testuser1/tracks/ai_attributed" diff --git a/api/v1_users_tracks_test.go b/api/v1_users_tracks_test.go index 621084b5..11ad9bfc 100644 --- a/api/v1_users_tracks_test.go +++ b/api/v1_users_tracks_test.go @@ -14,7 +14,7 @@ func TestGetUserTracks(t *testing.T) { app := testAppWithFixtures(t) var userTracksResponse struct { - Data []dbv1.FullTrack + Data []dbv1.Track } // Test support for handle @@ -255,7 +255,7 @@ func TestGetUserTracksWithGateConditionFilter(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userTracksResponse struct { - Data []dbv1.FullTrack + Data []dbv1.Track } baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks", trashid.MustEncodeHashID(600)) diff --git a/sqlc.yaml b/sqlc.yaml index d54ec8af..81b0c887 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -12,6 +12,7 @@ sql: sql_package: "pgx/v5" rename: user: UserRow + track: TrackRow overrides: - db_type: "pg_catalog.timestamp" engine: "postgresql" From cf4d9cbd944ffc3f8819ad3bfb5ae848541f44ed Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 11 Feb 2026 15:27:48 -0800 Subject: [PATCH 2/3] Migrate collection to full (#636) Drops min_playlist Renames full_playlist to playlist uses playlist for full and default endpoints adds user_id to default endpoints as needed --- api/dbv1/get_playlists.sql.go | 8 ++ api/dbv1/models.go | 26 +++--- api/dbv1/parallel.go | 10 +- api/dbv1/{full_playlists.go => playlists.go} | 96 ++++--------------- api/dbv1/queries/get_playlists.sql | 6 ++ api/response_helpers.go | 19 +--- api/swagger/swagger-v1.yaml | 97 ++++++++++++++++++++ api/v1_explore_best_selling_test.go | 2 +- api/v1_playlist.go | 2 +- api/v1_playlist_by_permalink.go | 2 +- api/v1_playlist_stream.go | 2 +- api/v1_playlist_test.go | 8 +- api/v1_playlists.go | 2 +- api/v1_playlists_test.go | 4 +- api/v1_playlists_top.go | 2 +- api/v1_playlists_trending.go | 2 +- api/v1_search.go | 12 +-- api/v1_users_albums.go | 2 +- api/v1_users_albums_test.go | 10 +- api/v1_users_library_playlists.go | 2 +- api/v1_users_playlists.go | 2 +- api/v1_users_playlists_test.go | 10 +- sqlc.yaml | 1 + 23 files changed, 185 insertions(+), 142 deletions(-) rename api/dbv1/{full_playlists.go => playlists.go} (53%) diff --git a/api/dbv1/get_playlists.sql.go b/api/dbv1/get_playlists.sql.go index 94f4c244..c98c7fca 100644 --- a/api/dbv1/get_playlists.sql.go +++ b/api/dbv1/get_playlists.sql.go @@ -58,6 +58,12 @@ SELECT updated_at, release_date, + ( + SELECT COALESCE(SUM(ap.count), 0)::bigint + FROM jsonb_array_elements(COALESCE(p.playlist_contents->'track_ids', '[]'::jsonb)) AS e(item) + LEFT JOIN aggregate_plays ap ON ap.play_item_id = (e.item->>'track')::int + ) AS total_play_count, + ( SELECT count(*) > 0 FROM reposts @@ -159,6 +165,7 @@ type GetPlaylistsRow struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` ReleaseDate *time.Time `json:"release_date"` + TotalPlayCount int64 `json:"total_play_count"` HasCurrentUserReposted bool `json:"has_current_user_reposted"` HasCurrentUserSaved bool `json:"has_current_user_saved"` FolloweeReposts json.RawMessage `json:"followee_reposts"` @@ -200,6 +207,7 @@ func (q *Queries) GetPlaylists(ctx context.Context, arg GetPlaylistsParams) ([]G &i.CreatedAt, &i.UpdatedAt, &i.ReleaseDate, + &i.TotalPlayCount, &i.HasCurrentUserReposted, &i.HasCurrentUserSaved, &i.FolloweeReposts, diff --git a/api/dbv1/models.go b/api/dbv1/models.go index cd5d69fc..340496ee 100644 --- a/api/dbv1/models.go +++ b/api/dbv1/models.go @@ -1394,7 +1394,19 @@ type Play struct { Country pgtype.Text `json:"country"` } -type Playlist struct { +type PlaylistRoute struct { + Slug string `json:"slug"` + TitleSlug string `json:"title_slug"` + CollisionID int32 `json:"collision_id"` + OwnerID int32 `json:"owner_id"` + PlaylistID int32 `json:"playlist_id"` + IsCurrent bool `json:"is_current"` + Blockhash string `json:"blockhash"` + Blocknumber int32 `json:"blocknumber"` + Txhash string `json:"txhash"` +} + +type PlaylistRow struct { Blockhash pgtype.Text `json:"blockhash"` Blocknumber pgtype.Int4 `json:"blocknumber"` PlaylistID int32 `json:"playlist_id"` @@ -1428,18 +1440,6 @@ type Playlist struct { ReleaseDate *time.Time `json:"release_date"` } -type PlaylistRoute struct { - Slug string `json:"slug"` - TitleSlug string `json:"title_slug"` - CollisionID int32 `json:"collision_id"` - OwnerID int32 `json:"owner_id"` - PlaylistID int32 `json:"playlist_id"` - IsCurrent bool `json:"is_current"` - Blockhash string `json:"blockhash"` - Blocknumber int32 `json:"blocknumber"` - Txhash string `json:"txhash"` -} - type PlaylistSeen struct { UserID int32 `json:"user_id"` PlaylistID int32 `json:"playlist_id"` diff --git a/api/dbv1/parallel.go b/api/dbv1/parallel.go index cfdece3f..5b1b400b 100644 --- a/api/dbv1/parallel.go +++ b/api/dbv1/parallel.go @@ -16,7 +16,7 @@ type ParallelParams struct { type ParallelResult struct { UserMap map[int32]User TrackMap map[int32]Track - PlaylistMap map[int32]FullPlaylist + PlaylistMap map[int32]Playlist } func (q *Queries) Parallel(ctx context.Context, arg ParallelParams) (*ParallelResult, error) { @@ -24,7 +24,7 @@ func (q *Queries) Parallel(ctx context.Context, arg ParallelParams) (*ParallelRe var userMap map[int32]User var trackMap map[int32]Track - var playlistMap map[int32]FullPlaylist + var playlistMap map[int32]Playlist if len(arg.UserIds) > 0 { g.Go(func() error { @@ -53,7 +53,7 @@ func (q *Queries) Parallel(ctx context.Context, arg ParallelParams) (*ParallelRe if len(arg.PlaylistIds) > 0 { g.Go(func() error { var err error - playlistMap, err = q.FullPlaylistsKeyed(ctx, FullPlaylistsParams{ + playlistMap, err = q.PlaylistsKeyed(ctx, PlaylistsParams{ GetPlaylistsParams: GetPlaylistsParams{ Ids: arg.PlaylistIds, MyID: arg.MyID, @@ -93,8 +93,8 @@ func (r *ParallelResult) TrackList() []Track { return trackList } -func (r *ParallelResult) PlaylistList() []FullPlaylist { - playlistList := make([]FullPlaylist, 0, len(r.PlaylistMap)) +func (r *ParallelResult) PlaylistList() []Playlist { + playlistList := make([]Playlist, 0, len(r.PlaylistMap)) for _, p := range r.PlaylistMap { playlistList = append(playlistList, p) } diff --git a/api/dbv1/full_playlists.go b/api/dbv1/playlists.go similarity index 53% rename from api/dbv1/full_playlists.go rename to api/dbv1/playlists.go index 047797d6..9394a25f 100644 --- a/api/dbv1/full_playlists.go +++ b/api/dbv1/playlists.go @@ -5,16 +5,15 @@ import ( "fmt" "api.audius.co/trashid" - "github.com/jackc/pgx/v5/pgtype" ) -type FullPlaylistsParams struct { +type PlaylistsParams struct { GetPlaylistsParams OmitTracks bool TrackLimit int // 0 means use default (200), positive values set the limit } -type FullPlaylist struct { +type Playlist struct { GetPlaylistsRow ID string `json:"id"` @@ -28,17 +27,17 @@ type FullPlaylist struct { FolloweeReposts []*FolloweeRepost `json:"followee_reposts"` FolloweeFavorites []*FolloweeFavorite `json:"followee_favorites"` - PlaylistContents []FullPlaylistContentsItem `json:"playlist_contents"` - AddedTimestamps []FullPlaylistContentsItem `json:"added_timestamps"` + PlaylistContents []PlaylistContentsItem `json:"playlist_contents"` + AddedTimestamps []PlaylistContentsItem `json:"added_timestamps"` } -type FullPlaylistContentsItem struct { +type PlaylistContentsItem struct { Time float64 `json:"timestamp"` TrackId string `json:"track_id"` MetadataTime float64 `json:"metadata_timestamp"` } -func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParams) (map[int32]FullPlaylist, error) { +func (q *Queries) PlaylistsKeyed(ctx context.Context, arg PlaylistsParams) (map[int32]Playlist, error) { rawPlaylists, err := q.GetPlaylists(ctx, arg.GetPlaylistsParams) if err != nil { return nil, err @@ -77,7 +76,7 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam return nil, err } - playlistMap := map[int32]FullPlaylist{} + playlistMap := map[int32]Playlist{} for _, playlist := range rawPlaylists { id, _ := trashid.EncodeHashId(int(playlist.PlaylistID)) user, ok := loaded.UserMap[playlist.PlaylistOwnerID] @@ -97,10 +96,10 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam } // slightly change playlist_contents - fullPlaylistContents := []FullPlaylistContentsItem{} + playlistContents := []PlaylistContentsItem{} for _, item := range playlist.PlaylistContents.TrackIDs { trackId, _ := trashid.EncodeHashId(int(item.Track)) - fullPlaylistContents = append(fullPlaylistContents, FullPlaylistContentsItem{ + playlistContents = append(playlistContents, PlaylistContentsItem{ Time: item.Time, MetadataTime: item.MetadataTime, TrackId: trackId, @@ -123,7 +122,7 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam playlistType = "playlist" } - playlistMap[playlist.PlaylistID] = FullPlaylist{ + playlistMap[playlist.PlaylistID] = Playlist{ GetPlaylistsRow: playlist, ID: id, Artwork: squareImageStruct(playlist.Artwork), @@ -133,9 +132,9 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam TrackCount: int32(len(playlist.PlaylistContents.TrackIDs)), FolloweeFavorites: fullFolloweeFavorites(playlist.FolloweeFavorites), FolloweeReposts: fullFolloweeReposts(playlist.FolloweeReposts), - PlaylistContents: fullPlaylistContents, + PlaylistContents: playlistContents, Permalink: fmt.Sprintf("/%s/%s/%s", user.Handle.String, playlistType, playlist.Slug.String), - AddedTimestamps: fullPlaylistContents, + AddedTimestamps: playlistContents, Access: Access{ Stream: streamAccess, Download: downloadAccess, @@ -146,79 +145,20 @@ func (q *Queries) FullPlaylistsKeyed(ctx context.Context, arg FullPlaylistsParam return playlistMap, nil } -func (q *Queries) FullPlaylists(ctx context.Context, arg FullPlaylistsParams) ([]FullPlaylist, error) { - playlistMap, err := q.FullPlaylistsKeyed(ctx, arg) +func (q *Queries) Playlists(ctx context.Context, arg PlaylistsParams) ([]Playlist, error) { + playlistMap, err := q.PlaylistsKeyed(ctx, arg) if err != nil { return nil, err } // return in same order as input list of ids // some ids may be not found... - fullPlaylists := []FullPlaylist{} + list := make([]Playlist, 0, len(arg.Ids)) for _, id := range arg.Ids { - if t, found := playlistMap[id]; found { - fullPlaylists = append(fullPlaylists, t) + if p, found := playlistMap[id]; found { + list = append(list, p) } } - return fullPlaylists, nil -} - -type MinPlaylist struct { - ID string `json:"id"` - PlaylistName pgtype.Text `json:"playlist_name"` - Artwork *SquareImage `json:"artwork"` - Access Access `json:"access"` - Description string `json:"description"` - IsImageAutogenerated bool `json:"is_image_autogenerated"` - Upc string `json:"upc"` - DdexApp string `json:"ddex_app"` - PlaylistContents interface{} `json:"playlist_contents"` - TrackCount int32 `json:"track_count"` - TotalPlayCount int64 `json:"total_play_count"` - IsAlbum bool `json:"is_album"` - FavoriteCount int32 `json:"favorite_count"` - RepostCount int32 `json:"repost_count"` - User User `json:"user"` - Permalink string `json:"permalink"` -} - -func ToMinPlaylist(fullPlaylist FullPlaylist) MinPlaylist { - minTracks := make([]Track, len(fullPlaylist.Tracks)) - for i, track := range fullPlaylist.Tracks { - minTracks[i] = track - } - - return MinPlaylist{ - ID: fullPlaylist.ID, - PlaylistName: fullPlaylist.PlaylistName, - Artwork: fullPlaylist.Artwork, - Access: fullPlaylist.Access, - Upc: fullPlaylist.Upc.String, - DdexApp: fullPlaylist.DdexApp.String, - PlaylistContents: fullPlaylist.PlaylistContents, - Description: fullPlaylist.Description.String, - IsImageAutogenerated: fullPlaylist.IsImageAutogenerated, - IsAlbum: fullPlaylist.IsAlbum, - TrackCount: int32(len(fullPlaylist.Tracks)), - TotalPlayCount: func() int64 { - var total int64 - for _, track := range fullPlaylist.Tracks { - total += track.PlayCount - } - return total - }(), - FavoriteCount: int32(fullPlaylist.FavoriteCount.Int32), - RepostCount: int32(fullPlaylist.RepostCount.Int32), - User: fullPlaylist.User, - Permalink: fullPlaylist.Permalink, - } -} - -func ToMinPlaylists(fullPlaylists []FullPlaylist) []MinPlaylist { - result := make([]MinPlaylist, len(fullPlaylists)) - for i, playlist := range fullPlaylists { - result[i] = ToMinPlaylist(playlist) - } - return result + return list, nil } diff --git a/api/dbv1/queries/get_playlists.sql b/api/dbv1/queries/get_playlists.sql index c8f9c7de..a0bf2db5 100644 --- a/api/dbv1/queries/get_playlists.sql +++ b/api/dbv1/queries/get_playlists.sql @@ -43,6 +43,12 @@ SELECT updated_at, release_date, + ( + SELECT COALESCE(SUM(ap.count), 0)::bigint + FROM jsonb_array_elements(COALESCE(p.playlist_contents->'track_ids', '[]'::jsonb)) AS e(item) + LEFT JOIN aggregate_plays ap ON ap.play_item_id = (e.item->>'track')::int + ) AS total_play_count, + ( SELECT count(*) > 0 FROM reposts diff --git a/api/response_helpers.go b/api/response_helpers.go index 349279ac..d93843c4 100644 --- a/api/response_helpers.go +++ b/api/response_helpers.go @@ -29,25 +29,16 @@ func v1UsersResponse(c *fiber.Ctx, users []dbv1.User) error { // Note: playlist response returned an array even though it's a single playlist // Done for backwards compatibility. Would be nice to get rid of this. -func v1PlaylistResponse(c *fiber.Ctx, playlist dbv1.FullPlaylist) error { - if c.Locals("isFull").(bool) { - return c.JSON(fiber.Map{ - "data": []dbv1.FullPlaylist{playlist}, - }) - } +// Default and full both return full playlist shape (min-collection parity with full). +func v1PlaylistResponse(c *fiber.Ctx, playlist dbv1.Playlist) error { return c.JSON(fiber.Map{ - "data": []dbv1.MinPlaylist{dbv1.ToMinPlaylist(playlist)}, + "data": []dbv1.Playlist{playlist}, }) } -func v1PlaylistsResponse(c *fiber.Ctx, playlists []dbv1.FullPlaylist) error { - if c.Locals("isFull").(bool) { - return c.JSON(fiber.Map{ - "data": playlists, - }) - } +func v1PlaylistsResponse(c *fiber.Ctx, playlists []dbv1.Playlist) error { return c.JSON(fiber.Map{ - "data": dbv1.ToMinPlaylists(playlists), + "data": playlists, }) } diff --git a/api/swagger/swagger-v1.yaml b/api/swagger/swagger-v1.yaml index 5e4e6342..4e7114f8 100644 --- a/api/swagger/swagger-v1.yaml +++ b/api/swagger/swagger-v1.yaml @@ -661,6 +661,11 @@ paths: description: The number of items to fetch schema: type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string - name: time in: query description: Calculate trending over a specified time range @@ -5433,17 +5438,30 @@ components: playlist: required: - access + - added_timestamps + - blocknumber + - created_at - favorite_count + - followee_favorites + - followee_reposts + - has_current_user_reposted + - has_current_user_saved - id - is_album + - is_delete - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated - permalink - playlist_contents - playlist_name - repost_count - total_play_count - track_count + - updated_at - user + - user_id type: object properties: artwork: @@ -5480,6 +5498,72 @@ components: type: string track_count: type: integer + blocknumber: + type: integer + created_at: + type: string + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: + type: string + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: + type: string + tracks: + type: array + items: + $ref: '#/components/schemas/Track' + cover_art: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: + type: string + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string playlist_artwork: type: object properties: @@ -5526,6 +5610,19 @@ components: type: array items: $ref: '#/components/schemas/favorite' + repost: + required: + - repost_item_id + - repost_type + - user_id + type: object + properties: + repost_item_id: + type: string + repost_type: + type: string + user_id: + type: string favorite: required: - created_at diff --git a/api/v1_explore_best_selling_test.go b/api/v1_explore_best_selling_test.go index 4bc49f22..7baa634b 100644 --- a/api/v1_explore_best_selling_test.go +++ b/api/v1_explore_best_selling_test.go @@ -285,7 +285,7 @@ func TestExploreBestSelling(t *testing.T) { Data []BestSellingItem Related struct { Tracks []dbv1.Track - Playlists []dbv1.FullPlaylist + Playlists []dbv1.Playlist } } diff --git a/api/v1_playlist.go b/api/v1_playlist.go index 4d759169..414915f3 100644 --- a/api/v1_playlist.go +++ b/api/v1_playlist.go @@ -9,7 +9,7 @@ func (app *ApiServer) v1Playlist(c *fiber.Ctx) error { myId := app.getMyId(c) playlistId := c.Locals("playlistId").(int) - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ MyID: myId, Ids: []int32{int32(playlistId)}, diff --git a/api/v1_playlist_by_permalink.go b/api/v1_playlist_by_permalink.go index 0adaffee..3d75f77b 100644 --- a/api/v1_playlist_by_permalink.go +++ b/api/v1_playlist_by_permalink.go @@ -28,7 +28,7 @@ func (app *ApiServer) v1PlaylistByPermalink(c *fiber.Ctx) error { return err } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ MyID: myId, Ids: ids, diff --git a/api/v1_playlist_stream.go b/api/v1_playlist_stream.go index b1fdc844..63d6a8e3 100644 --- a/api/v1_playlist_stream.go +++ b/api/v1_playlist_stream.go @@ -13,7 +13,7 @@ func (app *ApiServer) v1PlaylistStream(c *fiber.Ctx) error { myId := app.getMyId(c) playlistId := c.Locals("playlistId").(int) - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ MyID: myId, Ids: []int32{int32(playlistId)}, diff --git a/api/v1_playlist_test.go b/api/v1_playlist_test.go index 724f9ebd..223b0baa 100644 --- a/api/v1_playlist_test.go +++ b/api/v1_playlist_test.go @@ -11,7 +11,7 @@ import ( func TestGetPlaylist(t *testing.T) { app := testAppWithFixtures(t) var playlistResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/playlists/7eP5n", &playlistResponse) @@ -27,7 +27,7 @@ func TestGetPlaylist(t *testing.T) { func TestGetPlaylistFollowDownloadAccess(t *testing.T) { app := testAppWithFixtures(t) var playlistResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } // No access _, body1 := testGet(t, app, "/v1/full/playlists/ML51L", &playlistResponse) @@ -52,7 +52,7 @@ func TestGetPlaylistFollowDownloadAccess(t *testing.T) { func TestGetPlaylistUsdcPurchaseStreamAccess(t *testing.T) { app := testAppWithFixtures(t) var playlistResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } // No access _, body1 := testGet(t, app, "/v1/full/playlists/ELKzn", &playlistResponse) @@ -77,7 +77,7 @@ func TestGetPlaylistUsdcPurchaseStreamAccess(t *testing.T) { func TestGetPlaylistUsdcPurchaseSelfAccess(t *testing.T) { app := testAppWithFixtures(t) var playlistResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } // No access. User 3 is the owner, but has not signed authorization status, _ := testGet( diff --git a/api/v1_playlists.go b/api/v1_playlists.go index 4afdd6c7..a43e4a81 100644 --- a/api/v1_playlists.go +++ b/api/v1_playlists.go @@ -49,7 +49,7 @@ func (app *ApiServer) v1Playlists(c *fiber.Ctx) error { ids = append(ids, newIds...) } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ MyID: myId, Ids: ids, diff --git a/api/v1_playlists_test.go b/api/v1_playlists_test.go index 1508857b..da2ec1b4 100644 --- a/api/v1_playlists_test.go +++ b/api/v1_playlists_test.go @@ -34,7 +34,7 @@ func TestPlaylistsEndpointWithTracks(t *testing.T) { func TestPlaylistsEndpointWithPlaylistPermalink(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/playlists?permalink=/PlaylistsByPermalink/playlist/playlist-by-permalink", &resp) @@ -49,7 +49,7 @@ func TestPlaylistsEndpointWithPlaylistPermalink(t *testing.T) { func TestPlaylistsEndpointWithAlbumPermalink(t *testing.T) { app := testAppWithFixtures(t) var resp struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/playlists?permalink=/AlbumsByPermalink/album/album-by-permalink", &resp) diff --git a/api/v1_playlists_top.go b/api/v1_playlists_top.go index dfb1f983..5555c5d5 100644 --- a/api/v1_playlists_top.go +++ b/api/v1_playlists_top.go @@ -42,7 +42,7 @@ func (app *ApiServer) v1PlaylistsTop(c *fiber.Ctx) error { myId = app.getMyId(c) } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: playlistsIds, MyID: myId, diff --git a/api/v1_playlists_trending.go b/api/v1_playlists_trending.go index 575f7333..ee9ebf2a 100644 --- a/api/v1_playlists_trending.go +++ b/api/v1_playlists_trending.go @@ -106,7 +106,7 @@ func (app *ApiServer) v1PlaylistsTrending(c *fiber.Ctx) error { return err } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: ids, MyID: myId, diff --git a/api/v1_search.go b/api/v1_search.go index f15cb09e..fb82a9bc 100644 --- a/api/v1_search.go +++ b/api/v1_search.go @@ -16,8 +16,8 @@ func (app *ApiServer) v1SearchFull(c *fiber.Ctx) error { g := errgroup.Group{} var users = []dbv1.User{} var tracks = []dbv1.Track{} - var playlists = []dbv1.FullPlaylist{} - var albums = []dbv1.FullPlaylist{} + var playlists = []dbv1.Playlist{} + var albums = []dbv1.Playlist{} // users g.Go(func() (err error) { @@ -178,7 +178,7 @@ func (app *ApiServer) searchTracks(c *fiber.Ctx) ([]dbv1.Track, error) { return tracks, err } -func (app *ApiServer) searchPlaylists(c *fiber.Ctx) ([]dbv1.FullPlaylist, error) { +func (app *ApiServer) searchPlaylists(c *fiber.Ctx) ([]dbv1.Playlist, error) { isTagSearch := strings.Contains(c.Route().Path, "search/tags") isFullSearch := strings.Contains(c.Route().Path, "search/full") limit := c.QueryInt("limit", 10) @@ -209,7 +209,7 @@ func (app *ApiServer) searchPlaylists(c *fiber.Ctx) ([]dbv1.FullPlaylist, error) myId = 0 } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: playlistsIds, MyID: myId, @@ -219,7 +219,7 @@ func (app *ApiServer) searchPlaylists(c *fiber.Ctx) ([]dbv1.FullPlaylist, error) return playlists, err } -func (app *ApiServer) searchAlbums(c *fiber.Ctx) ([]dbv1.FullPlaylist, error) { +func (app *ApiServer) searchAlbums(c *fiber.Ctx) ([]dbv1.Playlist, error) { isTagSearch := strings.Contains(c.Route().Path, "search/tags") isFullSearch := strings.Contains(c.Route().Path, "search/full") limit := c.QueryInt("limit", 10) @@ -251,7 +251,7 @@ func (app *ApiServer) searchAlbums(c *fiber.Ctx) ([]dbv1.FullPlaylist, error) { myId = 0 } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: playlistsIds, MyID: myId, diff --git a/api/v1_users_albums.go b/api/v1_users_albums.go index f293e7ca..5b9ea558 100644 --- a/api/v1_users_albums.go +++ b/api/v1_users_albums.go @@ -82,7 +82,7 @@ func (app *ApiServer) v1UserAlbums(c *fiber.Ctx) error { return err } - albums, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + albums, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: ids, MyID: myId, diff --git a/api/v1_users_albums_test.go b/api/v1_users_albums_test.go index e4cfd349..77e7e809 100644 --- a/api/v1_users_albums_test.go +++ b/api/v1_users_albums_test.go @@ -45,7 +45,7 @@ func TestGetUserAlbums(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userAlbumsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } { @@ -101,7 +101,7 @@ func TestGetUserAlbums_SortRecentDesc(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userAlbumsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/albums?sort_method=recent&sort_direction=desc", &userAlbumsResponse) @@ -147,7 +147,7 @@ func TestGetUserAlbums_SortPopularAsc(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userAlbumsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/albums?sort_method=popular&sort_direction=asc", &userAlbumsResponse) @@ -196,7 +196,7 @@ func TestGetUserAlbums_FilterAlbumsPublic(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userAlbumsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/albums?filter_albums=public", &userAlbumsResponse) @@ -245,7 +245,7 @@ func TestGetUserAlbums_FilterAlbumsPrivate(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userAlbumsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/albums?filter_albums=private", &userAlbumsResponse) diff --git a/api/v1_users_library_playlists.go b/api/v1_users_library_playlists.go index 8e55cd9c..49e737d5 100644 --- a/api/v1_users_library_playlists.go +++ b/api/v1_users_library_playlists.go @@ -159,7 +159,7 @@ func (app *ApiServer) v1UsersLibraryPlaylists(c *fiber.Ctx) error { } // get playlists - playlists, err := app.queries.FullPlaylistsKeyed(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.PlaylistsKeyed(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: ids, MyID: myId, diff --git a/api/v1_users_playlists.go b/api/v1_users_playlists.go index 31b02d1a..cc607b4a 100644 --- a/api/v1_users_playlists.go +++ b/api/v1_users_playlists.go @@ -82,7 +82,7 @@ func (app *ApiServer) v1UserPlaylists(c *fiber.Ctx) error { return err } - playlists, err := app.queries.FullPlaylists(c.Context(), dbv1.FullPlaylistsParams{ + playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{ GetPlaylistsParams: dbv1.GetPlaylistsParams{ Ids: ids, MyID: myId, diff --git a/api/v1_users_playlists_test.go b/api/v1_users_playlists_test.go index 5402d58d..74926bc3 100644 --- a/api/v1_users_playlists_test.go +++ b/api/v1_users_playlists_test.go @@ -45,7 +45,7 @@ func TestGetUserPlaylists(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userPlaylistsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } { @@ -101,7 +101,7 @@ func TestGetUserPlaylists_SortRecentDesc(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userPlaylistsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?sort_method=recent&sort_direction=desc", &userPlaylistsResponse) @@ -147,7 +147,7 @@ func TestGetUserPlaylists_SortPopularAsc(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userPlaylistsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?sort_method=popular&sort_direction=asc", &userPlaylistsResponse) @@ -196,7 +196,7 @@ func TestGetUserPlaylists_FilterPlaylistsPublic(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userPlaylistsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?filter_playlists=public", &userPlaylistsResponse) @@ -245,7 +245,7 @@ func TestGetUserPlaylists_FilterPlaylistsPrivate(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) var userPlaylistsResponse struct { - Data []dbv1.FullPlaylist + Data []dbv1.Playlist } status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?filter_playlists=private", &userPlaylistsResponse) diff --git a/sqlc.yaml b/sqlc.yaml index 81b0c887..eda778b8 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -13,6 +13,7 @@ sql: rename: user: UserRow track: TrackRow + playlist: PlaylistRow overrides: - db_type: "pg_catalog.timestamp" engine: "postgresql" From 57a3bee157b223f66f70512e02ab93846421ce8e Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 17 Feb 2026 09:25:05 -0800 Subject: [PATCH 3/3] v1 to v1/full migration --- api/resolve_middleware.go | 5 +- api/server.go | 29 +- api/swagger/swagger-v1.yaml | 14650 ++++++++++++++++---- api/v1_explore_best_selling_test.go | 2 +- api/v1_playlist_test.go | 14 +- api/v1_playlists_test.go | 8 +- api/v1_resolve.go | 6 +- api/v1_track_remixing_test.go | 8 +- api/v1_track_test.go | 18 +- api/v1_tracks_test.go | 4 +- api/v1_users_albums_test.go | 12 +- api/v1_users_library_tracks.go | 4 +- api/v1_users_library_tracks_test.go | 22 +- api/v1_users_playlists_test.go | 12 +- api/v1_users_recommended_tracks_test.go | 6 +- api/v1_users_tracks_ai_attributed_test.go | 4 +- api/v1_users_tracks_count_test.go | 6 +- api/v1_users_tracks_test.go | 8 +- 18 files changed, 11691 insertions(+), 3127 deletions(-) diff --git a/api/resolve_middleware.go b/api/resolve_middleware.go index a2f478f5..7db4a0e6 100644 --- a/api/resolve_middleware.go +++ b/api/resolve_middleware.go @@ -10,9 +10,8 @@ import ( ) func (app *ApiServer) isFullMiddleware(c *fiber.Ctx) error { - u := c.OriginalURL() - isFull := strings.Contains(u, "/full/") - c.Locals("isFull", isFull) + // v1 returns single user/object in data; v1/full returns array + c.Locals("isFull", strings.HasPrefix(c.Path(), "/v1/full")) return c.Next() } diff --git a/api/server.go b/api/server.go index af091b6f..a115cee0 100644 --- a/api/server.go +++ b/api/server.go @@ -1,6 +1,7 @@ package api import ( + "bytes" "context" "crypto/tls" _ "embed" @@ -51,9 +52,14 @@ import ( //go:embed swagger/swagger-v1.yaml var swaggerV1 []byte -//go:embed swagger/swagger-v1-full.yaml +// swaggerV1Full is the same merged spec with server /v1/full for the v1/full Swagger UI var swaggerV1Full []byte +func init() { + swaggerV1Full = append([]byte(nil), swaggerV1...) + swaggerV1Full = bytes.Replace(swaggerV1Full, []byte("- url: /v1\n"), []byte("- url: /v1/full\n"), 1) +} + func RequestTimer() fiber.Handler { return func(c *fiber.Ctx) error { c.Locals("start", time.Now()) @@ -623,26 +629,21 @@ func NewApiServer(config config.Config) *ApiServer { // Disable swagger in test environments, because it will slow things down a lot if config.Env != "test" { - // Create Swagger middleware for v1 - // - // Swagger will be available at: /v1 + // Swagger for v1 (merged spec - includes all former v1/full endpoints) app.Use(swagger.New(swagger.Config{ - BasePath: "/", - Path: "v1", - // Only controls where the swagger.json is server from + BasePath: "/", + Path: "v1", FilePath: "v1/swagger.yaml", FileContent: swaggerV1, + Title: "Audius API v1", })) - - // Create Swagger middleware for v1/full - // - // Swagger will be available at: /v1/full + // Swagger for v1/full (same merged spec, server base /v1/full) app.Use(swagger.New(swagger.Config{ - BasePath: "/", - Path: "v1/full", - // Only controls where the swagger.json is server from + BasePath: "/", + Path: "v1/full", FilePath: "v1/full/swagger.yaml", FileContent: swaggerV1Full, + Title: "Audius API v1/full", })) } diff --git a/api/swagger/swagger-v1.yaml b/api/swagger/swagger-v1.yaml index 4e7114f8..5fa4be05 100644 --- a/api/swagger/swagger-v1.yaml +++ b/api/swagger/swagger-v1.yaml @@ -2,7 +2,7 @@ openapi: 3.0.1 info: title: API description: Audius V1 API - version: "1.0" + version: '1.0' servers: - url: /v1 tags: @@ -69,16 +69,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/undisbursed_challenges' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /challenges/undisbursed/{user_id}: @@ -118,16 +118,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/undisbursed_challenges' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /comments/unclaimed_id: @@ -137,13 +137,13 @@ paths: description: Gets an unclaimed blockchain comment ID operationId: Get unclaimed comment ID responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/unclaimed_id_response' - "500": + '500': description: Server error content: {} /comments/{comment_id}: @@ -160,13 +160,13 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/comment_response' - "500": + '500': description: Server error content: {} /comments/{comment_id}/replies: @@ -199,16 +199,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/comment_replies_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /dashboard_wallet_users: @@ -229,19 +229,19 @@ paths: items: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/dashboard_wallet_users_response' - "400": + '400': description: Bad request content: {} - "404": + '404': description: No such dashboard wallets content: {} - "500": + '500': description: Server error content: {} /developer_apps/{address}: @@ -258,19 +258,19 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/developer_app_response' - "400": + '400': description: Bad request content: {} - "404": + '404': description: Not found content: {} - "500": + '500': description: Server error content: {} /events: @@ -304,16 +304,16 @@ paths: - live_event - new_release responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/events_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /events/all: @@ -359,16 +359,16 @@ paths: - live_event - new_release responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/events_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /events/entity: @@ -421,16 +421,16 @@ paths: type: boolean default: true responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/events_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /events/unclaimed_id: @@ -440,21 +440,21 @@ paths: description: Gets an unclaimed blockchain event ID operationId: Get unclaimed event ID responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/unclaimed_id_response' - "500": + '500': description: Server error content: {} /explore/best-selling: get: tags: - explore - summary: Get best selling tracks and/or albums - description: Get best selling tracks and playlists + summary: Get best selling tracks and playlists with related entities + description: Get best selling tracks and/or albums with related entities operationId: Get Best Selling parameters: - name: offset @@ -484,16 +484,16 @@ paths: - track - album responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/best_selling_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /playlists: @@ -527,16 +527,16 @@ paths: items: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/playlist_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /playlists/by_permalink/{handle}/{slug}: @@ -564,7 +564,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -631,23 +631,23 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/playlist_search_result' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /playlists/trending: get: tags: - playlists - description: Gets trending playlists for a time period + description: Returns trending playlists for a time period operationId: Get Trending Playlists parameters: - name: offset @@ -692,16 +692,16 @@ paths: type: boolean default: false responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/trending_playlists_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /playlists/{playlist_id}: @@ -723,18 +723,12 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/playlist_response' - "400": - description: Bad request - content: {} - "500": - description: Server error - content: {} /playlists/{playlist_id}/access-info: get: tags: @@ -755,7 +749,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -775,16 +769,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/playlist_tracks_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /resolve: @@ -793,10 +787,12 @@ paths: - resolve summary: Resolves and redirects a provided Audius app URL to the API resource URL it represents - description: |- - This endpoint allows you to lookup and access API resources when you only know the + description: 'This endpoint allows you to lookup and access API resources when + you only know the + audius.co URL. - Tracks, Playlists, and Users are supported. + + Tracks, Playlists, and Users are supported.' operationId: Resolve parameters: - name: url @@ -807,7 +803,7 @@ paths: schema: type: string responses: - "302": + '302': description: Internal redirect content: {} /tips: @@ -888,7 +884,7 @@ paths: items: type: string responses: - "200": + '200': description: Success content: application/json: @@ -901,6 +897,11 @@ paths: description: Gets a list of tracks using their IDs or permalinks operationId: Get Bulk Tracks parameters: + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string - name: permalink in: query description: The permalink of the track(s) @@ -921,31 +922,20 @@ paths: type: string - name: isrc in: query - description: The ISRC of the track(s) + description: The ISRC code of the track(s) style: form explode: true schema: type: array items: type: string - - name: user_id - in: query - description: The user ID of the user making the request - schema: - type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": - description: Bad request - content: {} - "500": - description: Server error - content: {} /tracks/inspect: get: tags: @@ -971,16 +961,16 @@ paths: type: boolean default: false responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_inspect_list' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/recent-premium: @@ -1007,7 +997,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1055,7 +1045,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1109,7 +1099,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1149,16 +1139,15 @@ paths: type: integer minimum: 1 responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": + '400': description: Bad request content: {} - /tracks/recent-comments: get: tags: @@ -1188,13 +1177,13 @@ paths: default: 0 minimum: 0 responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": + '400': description: Bad request content: {} /tracks/most-shared: @@ -1236,16 +1225,16 @@ paths: - month - allTime responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/usdc-purchase: @@ -1287,23 +1276,24 @@ paths: - year - allTime responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/usdc-purchase/{version}: get: tags: - tracks - description: Gets the top trending (most popular) USDC purchase tracks on Audius using a given trending strategy version + description: Gets the top trending (most popular) USDC purchase tracks on Audius + using a given trending strategy version operationId: Get Trending USDC Purchase Tracks With Version parameters: - name: version @@ -1314,7 +1304,8 @@ paths: type: string - name: offset in: query - description: The number of items to skip. Useful for pagination (page number * limit) + description: The number of items to skip. Useful for pagination (page number + * limit) schema: type: integer - name: limit @@ -1343,7 +1334,7 @@ paths: - year - allTime responses: - "200": + '200': description: Success content: application/json: @@ -1404,7 +1395,7 @@ paths: description: Return only downloadable tracks schema: type: string - default: "false" + default: 'false' - name: includePurchaseable in: query description: Whether or not to include purchaseable content @@ -1441,16 +1432,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_search' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/trending: @@ -1471,6 +1462,11 @@ paths: description: The number of items to fetch schema: type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string - name: genre in: query description: Filter trending to a specified genre @@ -1486,29 +1482,25 @@ paths: - month - year - allTime - - name: user_id - in: query - description: The user ID of the user making the request - schema: - type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/trending/{version}: get: tags: - tracks - description: Gets the top 100 trending (most popular) tracks on Audius using a given trending strategy version + description: Gets the top 100 trending (most popular) tracks on Audius using + a given trending strategy version operationId: Get Trending Tracks With Version parameters: - name: version @@ -1519,7 +1511,8 @@ paths: type: string - name: offset in: query - description: The number of items to skip. Useful for pagination (page number * limit) + description: The number of items to skip. Useful for pagination (page number + * limit) schema: type: integer - name: limit @@ -1548,7 +1541,7 @@ paths: - year - allTime responses: - "200": + '200': description: Success content: application/json: @@ -1578,23 +1571,24 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/trending_ids_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/trending/ids/{version}: get: tags: - tracks - description: Gets the track IDs of the top trending tracks on Audius based on the given trending strategy version + description: Gets the track IDs of the top trending tracks on Audius based on + the given trending strategy version operationId: Get Trending Tracks IDs With Version parameters: - name: version @@ -1605,7 +1599,8 @@ paths: type: string - name: offset in: query - description: The number of items to skip. Useful for pagination (page number * limit) + description: The number of items to skip. Useful for pagination (page number + * limit) schema: type: integer - name: limit @@ -1619,16 +1614,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/trending_ids_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/trending/underground: @@ -1655,7 +1650,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1665,18 +1660,20 @@ paths: get: tags: - tracks - description: Gets the top 100 trending underground tracks on Audius using a given trending strategy version + description: Gets the top 100 trending underground tracks on Audius using a + given trending strategy version operationId: Get Underground Trending Tracks With Version parameters: - name: version in: path - description: The strategy version of trending to use + description: The strategy version of trending to user required: true schema: type: string - name: offset in: query - description: The number of items to skip. Useful for pagination (page number * limit) + description: The number of items to skip. Useful for pagination (page number + * limit) schema: type: integer - name: limit @@ -1690,7 +1687,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1700,7 +1697,7 @@ paths: get: tags: - tracks - description: Gets a track by ID + description: Gets a track by ID. operationId: Get Track parameters: - name: track_id @@ -1715,18 +1712,12 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_response' - "400": - description: Bad request - content: {} - "500": - description: Server error - content: {} /tracks/{track_id}/access-info: get: tags: @@ -1747,7 +1738,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -1772,16 +1763,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_comment_count_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/comment_notification_setting: @@ -1803,16 +1794,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_comment_notification_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/comments: @@ -1855,16 +1846,16 @@ paths: - newest - timestamp responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_comments_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/favorites: @@ -1897,16 +1888,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_favorites_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/remixes: @@ -1959,7 +1950,7 @@ paths: schema: type: boolean responses: - "200": + '200': description: Success content: application/json: @@ -1995,7 +1986,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -2031,16 +2022,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_reposts_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/download: @@ -2064,9 +2055,9 @@ paths: type: string - name: user_signature in: query - description: |- - Optional - signature from the requesting user's wallet. - This is needed to authenticate the user and verify access in case the track is gated. + description: "Optional - signature from the requesting user's wallet.\n \ + \ This is needed to authenticate the user and verify access in case\ + \ the track is gated." schema: type: string - name: user_data @@ -2077,31 +2068,31 @@ paths: type: string - name: nft_access_signature in: query - description: |- - Optional - nft access signature for this track which was previously generated by a registered DN. - We perform checks on it and pass it through to CN. + description: "Optional - nft access signature for this track which was previously\ + \ generated by a registered DN.\n We perform checks on it and pass\ + \ it through to CN." schema: type: string - name: filename in: query - description: "Optional - name of file to download. If not provided, defaults\ - \ to track original filename or title." + description: Optional - name of file to download. If not provided, defaults + to track original filename or title. schema: type: string responses: - "200": + '200': description: Success content: {} - "216": + '216': description: Partial content content: {} - "400": + '400': description: Bad request content: {} - "416": + '416': description: Content range invalid content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/inspect: @@ -2125,16 +2116,16 @@ paths: type: boolean default: false responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/track_inspect' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/stems: @@ -2151,7 +2142,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -2162,10 +2153,11 @@ paths: tags: - tracks summary: Get the streamable MP3 file of a track - description: |- - Stream an mp3 track + description: 'Stream an mp3 track + This endpoint accepts the Range header for streaming. - https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests + + https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests' operationId: Stream Track parameters: - name: track_id @@ -2187,9 +2179,9 @@ paths: default: false - name: user_signature in: query - description: |- - Optional - signature from the requesting user's wallet. - This is needed to authenticate the user and verify access in case the track is gated. + description: "Optional - signature from the requesting user's wallet.\n \ + \ This is needed to authenticate the user and verify access in case\ + \ the track is gated." schema: type: string - name: user_data @@ -2200,9 +2192,9 @@ paths: type: string - name: nft_access_signature in: query - description: |- - Optional - gated content signature for this track which was previously generated by a registered DN. - We perform checks on it and pass it through to CN. + description: "Optional - gated content signature for this track which was\ + \ previously generated by a registered DN.\n We perform checks on\ + \ it and pass it through to CN." schema: type: string - name: skip_play_count @@ -2229,22 +2221,22 @@ paths: schema: type: boolean responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/stream_url_response' - "216": + '216': description: Partial content content: {} - "400": + '400': description: Bad request content: {} - "416": + '416': description: Content range invalid content: {} - "500": + '500': description: Server error content: {} /tracks/{track_id}/top_listeners: @@ -2277,16 +2269,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/top_listener' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users: @@ -2311,23 +2303,24 @@ paths: items: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/users_response' - "400": + $ref: '#/components/schemas/user_response' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/address: get: tags: - users - description: Gets User IDs from any Ethereum wallet address or Solana account address associated with their Audius account. + description: Gets User IDs from any Ethereum wallet address or Solana account + address associated with their Audius account. operationId: Get User IDs by Addresses parameters: - name: address @@ -2337,14 +2330,14 @@ paths: style: form explode: true example: - - "0x1234567890abcdef1234567890abcdef12345678" - - "E2LCbKdo2L3ikt1gK6pwp1pDLuhAfHBNf6fEQXpAqrf9" + - '0x1234567890abcdef1234567890abcdef12345678' + - E2LCbKdo2L3ikt1gK6pwp1pDLuhAfHBNf6fEQXpAqrf9 schema: type: array items: type: string responses: - "200": + '200': description: Success content: application/json: @@ -2369,16 +2362,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/user_response' - "400": + $ref: '#/components/schemas/user_response_single' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/handle/{handle}/tracks: @@ -2412,7 +2405,7 @@ paths: type: string - name: sort in: query - description: "[Deprecated] Field to sort by" + description: '[Deprecated] Field to sort by' schema: type: string default: date @@ -2463,20 +2456,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/tracks_response' - "400": + $ref: '#/components/schemas/tracks' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/handle/{handle}/tracks/ai_attributed: @@ -2511,7 +2504,7 @@ paths: type: string - name: sort in: query - description: "[Deprecated] Field to sort by" + description: '[Deprecated] Field to sort by' schema: type: string default: date @@ -2562,20 +2555,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/tracks_response' - "400": + $ref: '#/components/schemas/tracks' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/search: @@ -2625,16 +2618,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/user_search' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/verify_token: @@ -2652,19 +2645,19 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/verify_token' - "400": + '400': description: Bad input content: {} - "404": + '404': description: ID token not valid content: {} - "500": + '500': description: Server error content: {} /users/{id}: @@ -2686,16 +2679,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/user_response' - "400": + $ref: '#/components/schemas/user_response_single' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/albums: @@ -2748,20 +2741,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/albums_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/authorized_apps: @@ -2778,16 +2771,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/authorized_apps' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/balance/history: @@ -2805,21 +2798,24 @@ paths: type: string - name: start_time in: query - description: Start time for the balance history query (ISO 8601 format). Defaults to 7 days ago. + description: Start time for the balance history query (ISO 8601 format). Defaults + to 7 days ago. required: false schema: type: string format: date-time - name: end_time in: query - description: End time for the balance history query (ISO 8601 format). Defaults to now. + description: End time for the balance history query (ISO 8601 format). Defaults + to now. required: false schema: type: string format: date-time - name: granularity in: query - description: Data granularity. 'hourly' returns hourly data points, 'daily' returns daily aggregated data. Defaults to 'hourly'. + description: Data granularity. 'hourly' returns hourly data points, 'daily' + returns daily aggregated data. Defaults to 'hourly'. required: false schema: type: string @@ -2839,26 +2835,26 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/balance_history_response' - "400": + '400': description: Bad request content: {} - "401": + '401': description: Unauthorized content: {} - "403": + '403': description: Forbidden content: {} - "500": + '500': description: Server error content: {} /users/{id}/challenges: @@ -2881,24 +2877,23 @@ paths: type: boolean default: false responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/get_challenges' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} - /users/{id}/coins: get: tags: - users - description: 'Gets a list of the coins owned by the user and their balances' + description: Gets a list of the coins owned by the user and their balances parameters: - name: id in: path @@ -2934,7 +2929,8 @@ paths: get: tags: - users - description: 'Gets information about a specific coin owned by the user and their wallets' + description: Gets information about a specific coin owned by the user and their + wallets parameters: - name: id in: path @@ -2948,7 +2944,7 @@ paths: required: true schema: type: string - example: "Dez1g5f3h4j5k6l7m8n9o0p1q2r3s4t5u6v7w8x9y0z" + example: Dez1g5f3h4j5k6l7m8n9o0p1q2r3s4t5u6v7w8x9y0z operationId: Get User Coin responses: '200': @@ -2961,7 +2957,7 @@ paths: get: tags: - wallet - description: 'Gets a list of the coins held by a wallet address and their balances' + description: Gets a list of the coins held by a wallet address and their balances parameters: - name: walletId in: path @@ -2969,7 +2965,7 @@ paths: required: true schema: type: string - example: "Dez1g5f3h4j5k6l7m8n9o0p1q2r3s4t5u6v7w8x9y0z" + example: Dez1g5f3h4j5k6l7m8n9o0p1q2r3s4t5u6v7w8x9y0z - name: offset in: query description: The number of items to skip. Useful for pagination (page number @@ -3008,16 +3004,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/collectibles_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/comments: @@ -3050,16 +3046,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/user_comments_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/connected_wallets: @@ -3076,16 +3072,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/connected_wallets_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/developer_apps: @@ -3102,16 +3098,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/developer_apps' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/favorites: @@ -3119,7 +3115,7 @@ paths: tags: - users description: Gets a user's favorite tracks - operationId: Get Favorites + operationId: Get User Favorites parameters: - name: id in: path @@ -3128,16 +3124,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/favorites_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/followers: @@ -3170,16 +3166,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/followers_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/following: @@ -3212,16 +3208,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/following_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/history/tracks: @@ -3288,26 +3284,26 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/history_response' - "400": + '400': description: Bad request content: {} - "401": + '401': description: Unauthorized content: {} - "403": + '403': description: Forbidden content: {} - "500": + '500': description: Server error content: {} /users/{id}/listen_counts_monthly: @@ -3339,16 +3335,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/user_track_listen_counts_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/muted: @@ -3371,16 +3367,22 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/users_response' + $ref: '#/components/schemas/user_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} /users/{id}/mutuals: get: tags: @@ -3412,16 +3414,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/mutual_followers_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/playlists: @@ -3474,20 +3476,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/playlists_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/purchasers: @@ -3532,16 +3534,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/purchasers_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/purchases/download: @@ -3569,11 +3571,11 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: {} /users/{id}/recommended-tracks: @@ -3616,16 +3618,16 @@ paths: - month - allTime responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/tracks_response' - "400": + $ref: '#/components/schemas/tracks' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/related: @@ -3660,29 +3662,29 @@ paths: type: string - name: filter_followed in: query - description: "If true, filters out artists that the current user already follows" + description: If true, filters out artists that the current user already follows schema: type: boolean default: false responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/related_artist_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/remixers: get: tags: - users - description: "Gets the list of unique users who have remixed tracks by the given\ - \ user, or a specific track by that user if provided" + description: Gets the list of unique users who have remixed tracks by the given + user, or a specific track by that user if provided operationId: Get remixers parameters: - name: id @@ -3713,16 +3715,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/remixers_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/reposts: @@ -3755,16 +3757,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/reposts' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/sales/aggregate: @@ -3803,11 +3805,11 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -3838,11 +3840,11 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: {} /users/{id}/sales/download/json: @@ -3875,11 +3877,11 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -3915,16 +3917,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/subscribers_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/supporters: @@ -3957,7 +3959,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -3993,7 +3995,7 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: @@ -4024,16 +4026,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tags_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/tracks: @@ -4067,7 +4069,7 @@ paths: type: string - name: sort in: query - description: "[Deprecated] Field to sort by" + description: '[Deprecated] Field to sort by' schema: type: string default: date @@ -4113,7 +4115,7 @@ paths: - public - name: gate_condition in: query - description: Filter tracks by gate condition type. Multiple values can be provided to filter by multiple gate types (OR logic) + description: Filter by gate conditions (can be repeated) style: form explode: true schema: @@ -4134,20 +4136,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/tracks_response' - "400": + $ref: '#/components/schemas/tracks' + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/tracks/count: @@ -4200,20 +4202,20 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/tracks_count_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/tracks/remixed: @@ -4252,16 +4254,16 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/user_tracks_remixed_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /users/{id}/withdrawals/download: @@ -4289,11 +4291,11 @@ paths: type: string - name: Encoded-Data-Signature in: header - description: "The signature of data, used for signature recovery" + description: The signature of data, used for signature recovery schema: type: string responses: - "200": + '200': description: Success content: {} /users/{receiving_user_id}/emails/{grantor_user_id}/key: @@ -4317,94 +4319,93 @@ paths: schema: type: string responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/email_access_response' - "400": + '400': description: Bad request content: {} - "500": + '500': description: Server error content: {} /coins: get: tags: - - coins + - coins operationId: Get Coins - description: 'Gets information about coins' + description: Gets a list of coins with optional filtering parameters: - - name: mint + - name: ticker in: query - description: The mint addresses of the coins - style: form - explode: true + description: Filter by coin ticker(s) schema: type: array items: type: string - - name: owner_id - in: query - description: The user IDs of artists with minted coins style: form explode: true + - name: mint + in: query + description: Filter by coin mint address(es) schema: type: array items: type: string - - name: ticker - in: query - description: The tickers of the coins style: form explode: true + - name: owner_id + in: query + description: Filter by owner user ID(s) schema: type: array items: type: string - - name: query + style: form + explode: true + - name: limit in: query - description: Search by the coin name or ticker + description: Maximum number of results to return schema: - type: string + type: integer + minimum: 1 + maximum: 100 + default: 50 - name: offset in: query - description: The number of items to skip. Useful for pagination (page number - * limit) + description: Number of results to skip schema: type: integer - default: 0 minimum: 0 - - name: limit + default: 0 + - name: query in: query - description: The number of items to fetch + description: Search query for ticker, name, or handle schema: - type: integer - default: 50 - minimum: 1 - maximum: 100 + type: string - name: sort_method in: query - description: Field to use for sorting the results + description: Sort method schema: type: string - default: market_cap enum: - market_cap - - volume - price + - volume - created_at - holder + default: market_cap - name: sort_direction in: query - description: The sort direction + description: Sort direction schema: type: string - default: desc enum: - asc - desc + default: desc responses: '200': description: Success @@ -4414,9 +4415,9 @@ paths: $ref: '#/components/schemas/coins_response' post: tags: - - coins + - coins operationId: Create Coin - description: 'Creates a new artist coin' + description: Creates a new artist coin parameters: - name: user_id in: query @@ -4424,7 +4425,7 @@ paths: required: true schema: type: string - example: "7eP5n" + example: 7eP5n requestBody: required: true content: @@ -4441,18 +4442,15 @@ paths: '400': description: Bad request - Invalid parameters content: {} - '401': - description: Unauthorized - User not authenticated - content: {} '500': description: Server error content: {} /coins/{mint}: get: tags: - - coins + - coins operationId: Get Coin - description: 'Gets information about a specific coin by its mint address' + description: Gets information about a specific coin by its mint address parameters: - name: mint in: path @@ -4470,9 +4468,9 @@ paths: $ref: '#/components/schemas/coin_response' post: tags: - - coins + - coins operationId: Update Coin - description: 'Updates information about a specific coin by its mint address' + description: Updates information about a specific coin by its mint address parameters: - name: mint in: path @@ -4487,7 +4485,7 @@ paths: required: true schema: type: string - example: "7eP5n" + example: 7eP5n requestBody: required: true content: @@ -4516,13 +4514,13 @@ paths: /coins/ticker/{ticker}: get: tags: - - coins + - coins operationId: Get Coin By Ticker - description: 'Gets information about a specific coin by its ticker' + description: Gets information about a specific coin by its ticker parameters: - name: ticker in: path - description: The ticker of the coin + description: The ticker symbol of the coin required: true schema: type: string @@ -4534,18 +4532,12 @@ paths: application/json: schema: $ref: '#/components/schemas/coin_response' - '400': - description: Bad request - content: {} - '500': - description: Server error - content: {} /coins/{mint}/insights: get: tags: - - coins + - coins operationId: Get Coin Insights - description: 'Gets insights about a specific coin by its mint address' + description: Gets insights about a specific coin by its mint address parameters: - name: mint in: path @@ -4564,9 +4556,10 @@ paths: /coins/{mint}/members: get: tags: - - coins + - coins operationId: Get Coin Members - description: 'Gets a list of Audius users with a non-zero balance of a specific coin' + description: Gets a list of Audius users with a non-zero balance of a specific + coin parameters: - name: mint in: path @@ -4610,9 +4603,10 @@ paths: /coins/{mint}/members/count: get: tags: - - coins + - coins operationId: Get Coin Members Count - description: 'Gets the total number of Audius users with a non-zero balance of a specific coin' + description: Gets the total number of Audius users with a non-zero balance of + a specific coin parameters: - name: mint in: path @@ -4631,9 +4625,9 @@ paths: /coins/volume-leaders: get: tags: - - coins + - coins operationId: Get Volume Leaders - description: 'Gets top coin<>AUDIO trading addresses by volume' + description: Gets top coin<>AUDIO trading addresses by volume parameters: - name: from in: query @@ -4641,14 +4635,14 @@ paths: schema: type: string example: '2006-01-02T15:04:05Z' - default: '(most recent midnight UTC)' + default: (most recent midnight UTC) - name: to in: query description: End of time range to query over (RFC3339 format) schema: type: string example: '2006-01-02T15:04:05Z' - default: '(most recent midnight UTC + 24hrs)' + default: (most recent midnight UTC + 24hrs) - name: offset in: query description: The number of items to skip. Useful for pagination (page number @@ -4678,9 +4672,10 @@ paths: /coins/{mint}/redeem: get: tags: - - coins + - coins operationId: Get Coin Redeem Amount - description: 'Gets the availability indicator for reward codes for a specific coin' + description: Gets the availability indicator for reward codes for a specific + coin parameters: - name: mint in: path @@ -4698,9 +4693,9 @@ paths: $ref: '#/components/schemas/redeem_amount_response' post: tags: - - coins + - coins operationId: Claim Coin Reward - description: 'Claims a coin reward for a given mint' + description: Claims a coin reward for a given mint parameters: - name: mint in: path @@ -4734,9 +4729,9 @@ paths: /coins/{mint}/redeem/{code}: get: tags: - - coins + - coins operationId: Get Reward Code - description: 'Gets information about a specific reward code for a coin' + description: Gets information about a specific reward code for a coin parameters: - name: mint in: path @@ -4767,9 +4762,9 @@ paths: $ref: '#/components/schemas/reward_code_error_response' post: tags: - - coins + - coins operationId: Claim Coin Reward Code - description: 'Claims a coin reward using a given code' + description: Claims a coin reward using a given code parameters: - name: mint in: path @@ -4797,7 +4792,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/claim_rewards_response' + $ref: '#/components/schemas/claim_rewards_response' '400': description: Bad request - Code is invalid or already used content: @@ -4820,16 +4815,16 @@ paths: schema: $ref: '#/components/schemas/claim_rewards_request' responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/claim_rewards_response' - "400": + '400': description: Bad request - No rewards to claim or invalid parameters content: {} - "500": + '500': description: Server error content: {} /rewards/code: @@ -4845,43 +4840,47 @@ paths: schema: $ref: '#/components/schemas/create_reward_code_request' responses: - "201": + '201': description: Created - Reward code successfully created content: application/json: schema: $ref: '#/components/schemas/create_reward_code_response' - "400": - description: Bad request - Invalid signature format or missing required fields + '400': + description: Bad request - Invalid signature format or missing required + fields content: {} - "403": + '403': description: Forbidden - Signature verification failed content: {} - "500": + '500': description: Server error content: {} /prizes: get: tags: - - prizes + - prizes operationId: Get Prizes - description: 'Gets a list of active prizes available for claiming. Excludes sensitive information like download URLs.' + description: Gets a list of active prizes available for claiming. Excludes sensitive + information like download URLs. responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/prizes_response' - "500": + '500': description: Server error content: {} /prizes/claim: post: tags: - - prizes + - prizes operationId: Claim Prize - description: 'Claims a prize by verifying a Solana transaction. User must send exactly 2 YAK to the prize receiver address. Returns the prize won and any redeem codes/URLs.' + description: Claims a prize by verifying a Solana transaction. User must send + exactly 2 YAK to the prize receiver address. Returns the prize won and any + redeem codes/URLs. requestBody: required: true content: @@ -4889,3269 +4888,11834 @@ paths: schema: $ref: '#/components/schemas/prize_claim_request' responses: - "200": + '200': description: Success - Prize claimed content: application/json: schema: $ref: '#/components/schemas/prize_claim_response' - "400": - description: Bad request - Transaction not found, invalid, or signature already used + '400': + description: Bad request - Transaction not found, invalid, or signature + already used content: {} - "500": + '500': description: Server error content: {} /wallet/{wallet}/prizes: get: tags: - - prizes + - prizes operationId: Get Wallet Prizes - description: 'Gets all claimed prizes for a wallet. Public endpoint - no authentication required. Excludes sensitive action_data for security.' + description: Gets all claimed prizes for a wallet. Public endpoint - no authentication + required. Excludes sensitive action_data for security. parameters: - - name: wallet - in: path - description: The wallet address to get prizes for - required: true - schema: - type: string - example: "HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC" + - name: wallet + in: path + description: The wallet address to get prizes for + required: true + schema: + type: string + example: HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC responses: - "200": + '200': description: Success content: application/json: schema: $ref: '#/components/schemas/claimed_prizes_response' - "400": + '400': description: Bad request - Missing wallet parameter content: {} - "500": + '500': description: Server error content: {} - -components: - schemas: - user_response: - type: object - properties: - data: - $ref: '#/components/schemas/user' - user: - required: - - album_count - - erc_wallet - - followee_count - - follower_count - - handle - - id - - is_available - - is_deactivated - - is_verified - - name - - playlist_count - - repost_count - - spl_wallet - - spl_usdc_wallet - - supporter_count - - supporting_count - - total_audio_balance - - track_count - - verified_with_instagram - - verified_with_tiktok - - verified_with_twitter - - wallet - type: object - properties: - album_count: - type: integer - artist_pick_track_id: - type: string - bio: - type: string - cover_photo: - $ref: '#/components/schemas/cover_photo' - followee_count: - type: integer - follower_count: - type: integer - handle: - type: string - id: - type: string - is_verified: - type: boolean - twitter_handle: - type: string - instagram_handle: + /challenges/{challenge_id}/attest: + get: + tags: + - challenges + description: Produces an attestation that a given user has completed a challenge, + or errors. + operationId: Get Challenge Attestation + parameters: + - name: challenge_id + in: path + description: The challenge ID of the user challenge requiring the attestation + required: true + schema: type: string - tiktok_handle: + - name: oracle + in: query + description: The address of a valid, registered Anti-Abuse Oracle + required: true + schema: type: string - verified_with_twitter: - type: boolean - verified_with_instagram: - type: boolean - verified_with_tiktok: - type: boolean - website: + - name: specifier + in: query + description: The specifier of the user challenge requiring the attestation + required: true + schema: type: string - donation: + - name: user_id + in: query + description: The user ID of the user challenge requiring the attestation + required: true + schema: type: string - location: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/attestation_reponse' + '400': + description: The attestation request was invalid (eg. The user didn't complete + that challenge yet) + content: {} + '500': + description: Server error + content: {} + /cid_data/{metadata_id}: + get: + tags: + - cid_data + description: Get a metadata by CID + operationId: Get Metadata + parameters: + - name: metadata_id + in: path + description: A Metdata CID + required: true + schema: type: string - name: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/cid_data_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /notifications/{user_id}: + get: + tags: + - notifications + description: Get notifications for user ID + operationId: Get Notifications + parameters: + - name: user_id + in: path + description: A User ID + required: true + schema: type: string - playlist_count: - type: integer - profile_picture: - $ref: '#/components/schemas/profile_picture' - repost_count: - type: integer - track_count: + - name: timestamp + in: query + description: The timestamp from which to paginate + schema: type: integer - is_deactivated: - type: boolean - is_available: - type: boolean - erc_wallet: - type: string - spl_wallet: - type: string - spl_usdc_wallet: - type: string - spl_usdc_payout_wallet: + - name: group_id + in: query + description: The group_id form which to paginate + schema: type: string - supporter_count: - type: integer - supporting_count: - type: integer - total_audio_balance: + - name: limit + in: query + description: The number of notifications to return + schema: type: integer - wallet: - type: string - description: The user's Ethereum wallet address for their account - balance: - type: string - associated_wallets_balance: - type: string - total_balance: - type: string - payout_wallet: - type: string - waudio_balance: + - name: types + in: query + description: Additional valid notification types to return + style: form + explode: true + schema: + type: array + items: + type: string + enum: + - announcement + - follow + - repost + - save + - remix + - cosign + - create + - tip_receive + - tip_send + - challenge_reward + - repost_of_repost + - save_of_repost + - tastemaker + - reaction + - supporter_dethroned + - supporter_rank_up + - supporting_rank_up + - milestone + - track_milestone + - track_added_to_playlist + - playlist_milestone + - tier_change + - trending + - trending_playlist + - trending_underground + - usdc_purchase_buyer + - usdc_purchase_seller + - track_added_to_purchased_album + - request_manager + - approve_manager_request + - claimable_reward + - comment + - comment_thread + - comment_mention + - comment_reaction + - listen_streak_reminder + - fan_remix_contest_started + - fan_remix_contest_ended + - fan_remix_contest_ending_soon + - fan_remix_contest_winners_selected + - artist_remix_contest_ended + - artist_remix_contest_ending_soon + - artist_remix_contest_submissions + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/notifications_response' + /notifications/{user_id}/playlist_updates: + get: + tags: + - notifications + description: Get playlists the user has saved that have been updated for user + ID + operationId: Get Playlist Updates + parameters: + - name: user_id + in: path + description: A User ID + required: true + schema: type: string - associated_sol_wallets_balance: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/playlist_updates_response' + /playlists/trending/{version}: + get: + tags: + - playlists + description: Returns trending playlists for a time period based on the given + trending version + operationId: Get Trending Playlists With Version + parameters: + - name: version + in: path + description: The strategy version of trending to use + required: true + schema: type: string - blocknumber: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: type: integer - created_at: + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - format: date-time - updated_at: + - name: time + in: query + description: Calculate trending over a specified time range + schema: type: string - format: date-time - is_storage_v2: - type: boolean - creator_node_endpoint: + enum: + - week + - month + - year + - allTime + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/trending_playlists_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /playlists/{playlist_id}/favorites: + get: + tags: + - playlists + description: Get users that favorited a playlist + operationId: Get Users From Playlist Favorites + parameters: + - name: playlist_id + in: path + description: A Playlist ID + required: true + schema: type: string - current_user_followee_follow_count: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: type: integer - does_current_user_follow: - type: boolean - does_current_user_subscribe: - type: boolean - does_follow_current_user: - type: boolean - handle_lc: - type: string - profile_type: - type: string - user_id: + - name: limit + in: query + description: The number of items to fetch + schema: type: integer - has_collectibles: - type: boolean - allow_ai_attribution: - type: boolean - coin_flair_mint: - type: string - artist_coin_badge: - type: object - cover_photo_sizes: + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - cover_photo_cids: - type: object - cover_photo_legacy: - type: object - profile_picture_sizes: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/following_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /playlists/{playlist_id}/reposts: + get: + tags: + - playlists + description: Get users that reposted a playlist + operationId: Get Users From Playlist Reposts + parameters: + - name: playlist_id + in: path + description: A Playlist ID + required: true + schema: type: string - profile_picture_cids: - type: object - profile_picture_legacy: - type: object - cover_photo: - type: object - properties: - "640x": + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - "2000x": + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/following_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /reactions: + get: + tags: + - reactions + description: Gets reactions by reacted_to_id and type + operationId: Bulk get Reactions + parameters: + - name: type + in: query + description: The type of reactions for which to query. + schema: type: string - profile_picture: - type: object - properties: - "150x150": + - name: reacted_to_ids + in: query + description: The `reacted_to` transaction id(s) of the reactions in question. + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/reactions' + '400': + description: Bad request + content: {} + '404': + description: No such reaction + content: {} + '500': + description: Server error + content: {} + /search/autocomplete: + get: + tags: + - search + summary: Get Users/Tracks/Playlists/Albums that best match the search query + description: Same as search but optimized for quicker response at the cost of + some entity information. + operationId: Search Autocomplete + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - "480x480": + - name: query + in: query + description: The search query + schema: type: string - "1000x1000": + - name: kind + in: query + description: 'The type of response, one of: all, users, tracks, playlists, + or albums' + schema: type: string - users_response: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/user' - user_track_listen_counts_response: - type: object - properties: - data: - $ref: '#/components/schemas/wild_month_model' - wild_month_model: - type: object - additionalProperties: - $ref: '#/components/schemas/monthly_aggregate_play' - monthly_aggregate_play: - type: object - properties: - totalListens: - type: integer - trackIds: + default: all + enum: + - all + - users + - tracks + - playlists + - albums + - name: includePurchaseable + in: query + description: Whether or not to include purchaseable content + schema: + type: boolean + - name: genre + in: query + description: The genres to filter by + style: form + explode: true + schema: type: array items: - type: integer - listenCounts: + type: string + - name: mood + in: query + description: The moods to filter by + style: form + explode: true + schema: type: array items: - $ref: '#/components/schemas/listen_count' - listen_count: - type: object - properties: - trackId: - type: integer - date: - type: string - listens: - type: integer - tracks_response: - type: object - properties: - data: + type: string + - name: is_verified + in: query + description: Only include verified users in the user results + schema: + type: boolean + - name: has_downloads + in: query + description: Only include tracks that have downloads in the track results + schema: + type: boolean + - name: is_purchaseable + in: query + description: Only include purchaseable tracks and albums in the track and + album results + schema: + type: boolean + - name: key + in: query + description: Only include tracks that match the musical key + style: form + explode: true + schema: type: array items: - $ref: '#/components/schemas/Track' - Track: - required: - - artwork - - comment_count - - duration - - favorite_count - - genre - - id - - is_downloadable - - is_original_available - - permalink - - play_count - - repost_count - - title - - user - type: object - properties: - artwork: - $ref: '#/components/schemas/track_artwork' - description: - type: string - genre: - type: string - id: - type: string - track_cid: - type: string - preview_cid: - type: string - orig_file_cid: - type: string - orig_filename: - type: string - is_original_available: - type: boolean - mood: - type: string - release_date: - type: string - isrc: + type: string + - name: bpm_min + in: query + description: Only include tracks that have a bpm greater than or equal to + schema: + type: number + - name: bpm_max + in: query + description: Only include tracks that have a bpm less than or equal to + schema: + type: number + - name: sort_method + in: query + description: The sort method + schema: type: string - remix_of: - $ref: '#/components/schemas/remix_parent' - repost_count: - type: integer - favorite_count: + enum: + - relevant + - popular + - recent + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/search_autocomplete_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /search/full: + get: + tags: + - search + description: Get Users/Tracks/Playlists/Albums that best match the search query + operationId: Search + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: type: integer - comment_count: + - name: limit + in: query + description: The number of items to fetch + schema: type: integer - tags: + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - title: + - name: query + in: query + description: The search query + schema: type: string - user: - $ref: '#/components/schemas/user' - duration: - type: integer - is_downloadable: - type: boolean - play_count: - type: integer - permalink: + - name: kind + in: query + description: 'The type of response, one of: all, users, tracks, playlists, + or albums' + schema: type: string - is_streamable: + default: all + enum: + - all + - users + - tracks + - playlists + - albums + - name: includePurchaseable + in: query + description: Whether or not to include purchaseable content + schema: type: boolean - ddex_app: - type: string - playlists_containing_track: + - name: genre + in: query + description: The genres to filter by + style: form + explode: true + schema: type: array items: - type: integer - pinned_comment_id: - type: integer - album_backlink: - $ref: '#/components/schemas/album_backlink' - access: - $ref: '#/components/schemas/track_access_info' - ai_attribution_user_id: - type: integer - allowed_api_keys: + type: string + - name: mood + in: query + description: The moods to filter by + style: form + explode: true + schema: type: array items: type: string - artists: - type: object - audio_analysis_error_count: - type: integer - audio_upload_id: - type: string - blocknumber: - type: integer - bpm: - type: number - comments_disabled: + - name: is_verified + in: query + description: Only include verified users in the user results + schema: type: boolean - copyright_line: - type: object - cover_art: - type: string - cover_art_sizes: - type: string - cover_original_artist: - type: string - cover_original_song_title: - type: string - create_date: - type: string - created_at: - type: string - format: date-time - credits_splits: - type: string - ddex_release_ids: - type: object - download: - $ref: '#/components/schemas/media_link' - download_conditions: - $ref: '#/components/schemas/access_gate' - field_visibility: - type: object - followee_favorites: - type: array - items: - type: object - followee_reposts: - type: array - items: - type: object - has_current_user_reposted: - type: boolean - has_current_user_saved: - type: boolean - indirect_resource_contributors: - type: object - is_available: - type: boolean - is_custom_bpm: - type: boolean - is_custom_musical_key: - type: boolean - is_delete: - type: boolean - is_download_gated: - type: boolean - is_owned_by_user: - type: boolean - is_scheduled_release: - type: boolean - is_stream_gated: + - name: has_downloads + in: query + description: Only include tracks that have downloads in the track results + schema: type: boolean - is_unlisted: + - name: is_purchaseable + in: query + description: Only include purchaseable tracks and albums in the track and + album results + schema: type: boolean - iswc: - type: string - license: - type: string - musical_key: - type: string - parental_warning_type: - type: string - playlists_previously_containing_track: - type: object - preview: - $ref: '#/components/schemas/media_link' - preview_start_seconds: - type: number - producer_copyright_line: - type: object - resource_contributors: - type: object - rights_controller: - type: object - slug: - type: string - stem_of: + - name: key + in: query + description: Only include tracks that match the musical key + style: form + explode: true + schema: type: array items: - type: object - stream: - $ref: '#/components/schemas/media_link' - stream_conditions: - $ref: '#/components/schemas/access_gate' - track_id: - type: integer - track_segments: - type: object - updated_at: + type: string + - name: bpm_min + in: query + description: Only include tracks that have a bpm greater than or equal to + schema: + type: number + - name: bpm_max + in: query + description: Only include tracks that have a bpm less than or equal to + schema: + type: number + - name: sort_method + in: query + description: The sort method + schema: type: string - format: date-time - track_artwork: - type: object - properties: - "150x150": + enum: + - relevant + - popular + - recent + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/search_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /search/tags: + get: + tags: + - search + description: Get Users/Tracks/Playlists/Albums that best match the provided + tag + operationId: SearchTags + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - "480x480": + - name: query + in: query + description: The search query + schema: type: string - "1000x1000": + - name: kind + in: query + description: 'The type of response, one of: all, users, tracks, playlists, + or albums' + schema: type: string - remix_parent: - type: object - properties: - tracks: + default: all + enum: + - all + - users + - tracks + - playlists + - albums + - name: includePurchaseable + in: query + description: Whether or not to include purchaseable content + schema: + type: boolean + - name: genre + in: query + description: The genres to filter by + style: form + explode: true + schema: type: array items: - $ref: '#/components/schemas/track_element' - track_element: - required: - - parent_track_id - type: object - properties: - parent_track_id: - type: string - album_backlink: - required: - - permalink - - playlist_id - - playlist_name - type: object - properties: - playlist_id: - type: integer - playlist_name: - type: string - permalink: - type: string - reposts: - type: object - properties: - data: + type: string + - name: mood + in: query + description: The moods to filter by + style: form + explode: true + schema: type: array items: - $ref: '#/components/schemas/activity' - track_activity: - allOf: - - $ref: '#/components/schemas/activity' - - required: - - item - - item_type - type: object - properties: - item_type: - type: string - example: track - enum: - - track - item: - $ref: '#/components/schemas/Track' - activity: - required: - - class - - item - - item_type - - timestamp - type: object - properties: - timestamp: - type: string - item_type: - type: string - example: track - enum: - - track - - playlist - item: - type: object - properties: {} - class: - type: string - discriminator: - propertyName: class - collection_activity: - allOf: - - $ref: '#/components/schemas/activity' - - required: - - item - - item_type - type: object - properties: - timestamp: type: string - item_type: - type: string - example: playlist - enum: - - playlist - item: - $ref: '#/components/schemas/playlist' - playlist: - required: - - access - - added_timestamps - - blocknumber - - created_at - - favorite_count - - followee_favorites - - followee_reposts - - has_current_user_reposted - - has_current_user_saved - - id - - is_album - - is_delete - - is_image_autogenerated - - is_private - - is_scheduled_release - - is_stream_gated - - permalink - - playlist_contents - - playlist_name - - repost_count - - total_play_count - - track_count - - updated_at - - user - - user_id - type: object - properties: - artwork: - $ref: '#/components/schemas/playlist_artwork' - description: - type: string - permalink: - type: string - id: - type: string - is_album: - type: boolean - is_image_autogenerated: - type: boolean - playlist_name: - type: string - playlist_contents: - type: array - items: - $ref: '#/components/schemas/playlist_added_timestamp' - repost_count: - type: integer - favorite_count: - type: integer - total_play_count: - type: integer - user: - $ref: '#/components/schemas/user' - ddex_app: - type: string - access: - $ref: '#/components/schemas/access' - upc: - type: string - track_count: - type: integer - blocknumber: - type: integer - created_at: - type: string - followee_reposts: - type: array - items: - $ref: '#/components/schemas/repost' - followee_favorites: - type: array - items: - $ref: '#/components/schemas/favorite' - has_current_user_reposted: - type: boolean - has_current_user_saved: + - name: is_verified + in: query + description: Only include verified users in the user results + schema: type: boolean - is_delete: + - name: has_downloads + in: query + description: Only include tracks that have downloads in the track results + schema: type: boolean - is_private: + - name: is_purchaseable + in: query + description: Only include purchaseable tracks and albums in the track and + album results + schema: type: boolean - updated_at: - type: string - added_timestamps: + - name: key + in: query + description: Only include tracks that match the musical key + style: form + explode: true + schema: type: array - description: DEPRECATED. Use playlist_contents instead. items: - $ref: '#/components/schemas/playlist_added_timestamp' - user_id: + type: string + - name: bpm_min + in: query + description: Only include tracks that have a bpm greater than or equal to + schema: + type: number + - name: bpm_max + in: query + description: Only include tracks that have a bpm less than or equal to + schema: + type: number + - name: sort_method + in: query + description: The sort method + schema: type: string - tracks: - type: array - items: - $ref: '#/components/schemas/Track' - cover_art: + enum: + - relevant + - popular + - recent + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/search_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /tracks/best_new_releases: + get: + tags: + - tracks + description: Gets the tracks found on the "Best New Releases" smart playlist + operationId: Get Best New Releases + parameters: + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - cover_art_sizes: + - name: window + in: query + description: The window from now() to look back over + required: true + schema: type: string - cover_art_cids: - $ref: '#/components/schemas/playlist_artwork' - is_stream_gated: - type: boolean - stream_conditions: - type: object - description: How to unlock stream access to the track - allOf: - - $ref: '#/components/schemas/access_gate' - is_scheduled_release: + enum: + - week + - month + - year + - name: limit + in: query + description: The number of tracks to get + schema: + type: integer + default: 25 + - name: with_users + in: query + description: Boolean to include user info with tracks + schema: type: boolean - release_date: - type: string - ddex_release_ids: - type: object - properties: {} - artists: - type: array - items: - type: object - properties: {} - copyright_line: - type: object - properties: {} - producer_copyright_line: - type: object - properties: {} - parental_warning_type: - type: string - playlist_artwork: - type: object - properties: - "150x150": - type: string - "480x480": - type: string - "1000x1000": + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /tracks/most_loved: + get: + tags: + - tracks + description: Gets the tracks found on the "Most Loved" smart playlist + operationId: Get Most Loved Tracks + parameters: + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - playlist_added_timestamp: - required: - - metadata_timestamp - - timestamp - - track_id - type: object - properties: - metadata_timestamp: + - name: limit + in: query + description: Number of tracks to fetch + schema: type: integer - timestamp: + default: 25 + - name: with_users + in: query + description: Boolean to include user info with tracks + schema: + type: boolean + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /tracks/remixables: + get: + tags: + - tracks + description: Gets a list of tracks that have stems available for remixing + operationId: Get Remixable Tracks + parameters: + - name: limit + in: query + description: The number of items to fetch + schema: type: integer - track_id: + - name: user_id + in: query + description: The user ID of the user making the request + schema: type: string - access: - required: - - download - - stream - type: object - properties: - stream: - type: boolean - download: - type: boolean + - name: with_users + in: query + description: Boolean to include user info with tracks + schema: + type: boolean + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/remixables_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /tracks/under_the_radar: + get: + tags: + - tracks + description: Gets the tracks found on the "Under the Radar" smart playlist + operationId: Get Under the Radar Tracks + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: filter + in: query + description: Filters for activity that is original vs reposts + schema: + type: string + default: all + enum: + - all + - repost + - original + - name: tracks_only + in: query + description: Whether to only include tracks + schema: + type: boolean + - name: with_users + in: query + description: Boolean to include user info with tracks + schema: + type: boolean + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/tracks_response' + /transactions: + get: + tags: + - transactions + summary: Gets the user's $AUDIO transaction history within the App + description: 'Deprecated: Use `/users/{id}/transactions/audio` or `sdk.full.users.getAudioTransactions()` + instead.' + operationId: Get Audio Transaction History + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: sort_method + in: query + description: The sort method + schema: + type: string + default: date + enum: + - date + - transaction_type + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + default: desc + enum: + - asc + - desc + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_response' + deprecated: true + /transactions/count: + get: + tags: + - transactions + summary: Gets the count of the user's $AUDIO transaction history within the + App + description: 'Deprecated: Use `/users/{id}/transactions/audio/count` or `sdk.full.users.getAudioTransactionCount()` + instead.' + operationId: Get Audio Transaction History Count + parameters: + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_count_response' + deprecated: true + /users/account/{wallet}: + get: + tags: + - users + description: Gets the account for a given user + operationId: Get User Account + parameters: + - name: wallet + in: path + description: Wallet address for the account + required: true + schema: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/user_account_response' + '401': + description: Unauthorized + content: {} + '403': + description: Forbidden + content: {} + '404': + description: Not Found + content: {} + '500': + description: Server error + content: {} + /users/genre/top: + get: + tags: + - users + description: Get the Top Users for a Given Genre + operationId: Get Top Users In Genre + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: genre + in: query + description: List of Genres + style: form + explode: true + schema: + type: array + items: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/top_genre_users_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/handle/{handle}/reposts: + get: + tags: + - users + description: Gets the user's reposts by the user handle + operationId: Get Reposts by Handle + parameters: + - name: handle + in: path + description: A User handle + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/reposts' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/subscribers: + get: + tags: + - users + description: All users that subscribe to the provided users + operationId: Bulk Get Subscribers + parameters: + - name: ids + in: query + description: User IDs to fetch subscribers for + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/bulk_subscribers_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + post: + tags: + - users + description: Get all users that subscribe to the users listed in the JSON request + operationId: Bulk Get Subscribers via JSON request + parameters: + - name: ids + in: query + description: User IDs to fetch subscribers for + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/bulk_subscribers_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/top: + get: + tags: + - users + description: Get the Top Users having at least one track by follower count + operationId: Get Top Users + parameters: + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/top_users_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/favorites/tracks: + get: + tags: + - users + description: Gets a user's favorite tracks + operationId: Get User Favorite Tracks + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: query + in: query + description: The filter query + schema: + type: string + - name: sort_method + in: query + description: The sort method + schema: + type: string + enum: + - title + - artist_name + - release_date + - last_listen_date + - added_date + - plays + - reposts + - saves + - most_listens_by_user + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/track_library_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/feed: + get: + tags: + - users + description: Gets the feed for the user + operationId: Get User Feed + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: filter + in: query + description: Controls whether the feed is limited to reposts, original content, + or all items + schema: + type: string + default: all + enum: + - all + - repost + - original + - name: tracks_only + in: query + description: Limit feed to only tracks + schema: + type: boolean + - name: with_users + in: query + description: Include user data with feed items + schema: + type: boolean + - name: followee_user_id + in: query + description: A list of followed users to prioritize in feed generation + style: form + explode: true + schema: + type: array + items: + type: integer + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/user_feed_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/library/albums: + get: + tags: + - users + summary: Fetch a user's full library playlists + description: Gets a user's saved/reposted/purchased/all albums + operationId: Get User Library Albums + parameters: + - name: id + in: path + description: A user ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: query + in: query + description: The filter query + schema: + type: string + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: type + in: query + description: 'The type of entity to return: favorited, reposted, purchased, + or all. Defaults to favorite' + schema: + type: string + default: favorite + enum: + - all + - repost + - favorite + - purchase + - name: sort_method + in: query + description: The sort method + schema: + type: string + enum: + - added_date + - reposts + - saves + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/collection_library_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/library/playlists: + get: + tags: + - users + summary: Fetch a user's full library playlists + description: Gets a user's saved/reposted/purchased/all playlists + operationId: Get User Library Playlists + parameters: + - name: id + in: path + description: A user ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: query + in: query + description: The filter query + schema: + type: string + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: type + in: query + description: 'The type of entity to return: favorited, reposted, purchased, + or all. Defaults to favorite' + schema: + type: string + default: favorite + enum: + - all + - repost + - favorite + - purchase + - name: sort_method + in: query + description: The sort method + schema: + type: string + enum: + - added_date + - reposts + - saves + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/collection_library_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/library/tracks: + get: + tags: + - users + summary: Fetch a user's full library tracks + description: Gets a user's saved/reposted/purchased/all tracks + operationId: Get User Library Tracks + parameters: + - name: id + in: path + description: A user ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: query + in: query + description: The filter query + schema: + type: string + - name: sort_method + in: query + description: The sort method + schema: + type: string + enum: + - title + - artist_name + - release_date + - last_listen_date + - added_date + - plays + - reposts + - saves + - most_listens_by_user + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: type + in: query + description: 'The type of entity to return: favorited, reposted, purchased, + or all. Defaults to favorite' + schema: + type: string + default: favorite + enum: + - all + - repost + - favorite + - purchase + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/track_library_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/managed_users: + get: + tags: + - users + description: Gets a list of users managed by the given user + operationId: Get Managed Users + parameters: + - name: id + in: path + description: A user id for the manager + required: true + schema: + type: string + - name: is_approved + in: query + description: If true, only show users where the management request has been + accepted. If false, only show those where the request was rejected. If omitted, + shows all users regardless of approval status. + schema: + type: boolean + - name: is_revoked + in: query + description: If true, only show users where the management request has been + revoked. If false, only show those with a pending or accepted request. Defaults + to false. + schema: + type: boolean + default: false + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/managed_users_response' + '400': + description: Bad request + content: {} + '401': + description: Unauthorized + content: {} + '403': + description: Forbidden + content: {} + '500': + description: Server error + content: {} + /users/{id}/managers: + get: + tags: + - users + description: Gets a list of users managing the given user + operationId: Get Managers + parameters: + - name: id + in: path + description: An id for the managed user + required: true + schema: + type: string + - name: is_approved + in: query + description: If true, only show users where the management request has been + accepted. If false, only show those where the request was rejected. If omitted, + shows all users regardless of approval status. + schema: + type: boolean + - name: is_revoked + in: query + description: If true, only show users where the management request has been + revoked. If false, only show those with a pending or accepted request. Defaults + to false. + schema: + type: boolean + default: false + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/managers_response' + '400': + description: Bad request + content: {} + '401': + description: Unauthorized + content: {} + '403': + description: Forbidden + content: {} + '500': + description: Server error + content: {} + /users/{id}/purchasers/count: + get: + tags: + - users + description: Gets the list of users who have purchased content by the given + user + operationId: Get purchasers count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: content_type + in: query + description: Type of content to filter by (track or album) + schema: + type: string + - name: content_id + in: query + description: Filters for users who have purchased the given track or album + ID + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/purchasers_count_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/purchases: + get: + tags: + - users + description: Gets the purchases the user has made + operationId: Get Purchases + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: sort_method + in: query + description: The sort direction + schema: + type: string + enum: + - content_title + - artist_name + - buyer_name + - date + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: content_ids + in: query + description: Filters purchases by track or album IDs + style: form + explode: true + schema: + type: array + items: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/purchases_response' + /users/{id}/purchases/count: + get: + tags: + - users + description: Gets the count of purchases the user has made + operationId: Get Purchases Count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: content_ids + in: query + description: Filters purchases by track or album IDs + style: form + explode: true + schema: + type: array + items: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/purchases_count_response' + /users/{id}/remixers/count: + get: + tags: + - users + description: Gets the count of unique users who have remixed tracks by the given + user, or a specific track by that user if provided + operationId: Get remixers count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: track_id + in: query + description: Filters for remixers who have remixed the given track ID + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/remixers_count_response' + '400': + description: Bad request + content: {} + '500': + description: Server error + content: {} + /users/{id}/sales: + get: + tags: + - users + description: Gets the sales the user has made + operationId: Get Sales + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: sort_method + in: query + description: The sort direction + schema: + type: string + enum: + - content_title + - artist_name + - buyer_name + - date + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + enum: + - asc + - desc + - name: content_ids + in: query + description: Filters purchases by track or album IDs + style: form + explode: true + schema: + type: array + items: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/purchases_response' + /users/{id}/sales/count: + get: + tags: + - users + description: Gets the count of sales the user has made + operationId: Get Sales Count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + - name: content_ids + in: query + description: Filters purchases by track or album IDs + style: form + explode: true + schema: + type: array + items: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/purchases_count_response' + /users/{id}/supporters/{supporter_user_id}: + get: + tags: + - users + description: Gets the specified supporter of the given user + operationId: Get Supporter + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: supporter_user_id + in: path + description: A User ID of a supporter + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/get_supporter' + /users/{id}/supporting/{supported_user_id}: + get: + tags: + - users + description: Gets the support from the given user to the supported user + operationId: Get Supporting + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: supported_user_id + in: path + description: A User ID of a supported user + required: true + schema: + type: string + - name: user_id + in: query + description: The user ID of the user making the request + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/get_supporting' + /users/{id}/transactions/audio: + get: + tags: + - users + description: Gets the user's $AUDIO transaction history within the App + operationId: Get Audio Transactions + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: sort_method + in: query + description: The sort method + schema: + type: string + default: date + enum: + - date + - transaction_type + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + default: desc + enum: + - asc + - desc + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_response' + /users/{id}/transactions/audio/count: + get: + tags: + - users + description: Gets the count of the user's $AUDIO transaction history within + the App + operationId: Get Audio Transaction Count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_count_response' + /users/{id}/transactions/usdc: + get: + tags: + - users + description: Gets the user's $USDC transaction history within the App + operationId: Get USDC Transactions + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: offset + in: query + description: The number of items to skip. Useful for pagination (page number + * limit) + schema: + type: integer + - name: limit + in: query + description: The number of items to fetch + schema: + type: integer + - name: sort_method + in: query + description: The sort method + schema: + type: string + default: date + enum: + - date + - transaction_type + - name: sort_direction + in: query + description: The sort direction + schema: + type: string + default: desc + enum: + - asc + - desc + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + - name: type + in: query + description: Filters the type of transactions to show + style: form + explode: true + schema: + type: array + items: + type: string + enum: + - purchase_content + - transfer + - internal_transfer + - prepare_withdrawal + - recover_withdrawal + - withdrawal + - purchase_stripe + - name: include_system_transactions + in: query + description: Include intermediate system transactions in the results + schema: + type: boolean + default: false + - name: method + in: query + description: Filters the method (sent/received) of transactions to show + schema: + type: string + enum: + - send + - receive + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_response' + /users/{id}/transactions/usdc/count: + get: + tags: + - users + description: Gets the count of the user's $USDC transaction history within the + App + operationId: Get USDC Transaction Count + parameters: + - name: id + in: path + description: A User ID + required: true + schema: + type: string + - name: type + in: query + description: Filters the type of transactions to show + style: form + explode: true + schema: + type: array + items: + type: string + enum: + - purchase_content + - transfer + - internal_transfer + - prepare_withdrawal + - recover_withdrawal + - withdrawal + - purchase_stripe + - name: include_system_transactions + in: query + description: Include intermediate system transactions in the results + schema: + type: boolean + default: false + - name: method + in: query + description: Filters the method (sent/received) of transactions to show + schema: + type: string + enum: + - send + - receive + - name: Encoded-Data-Message + in: header + description: The data that was signed by the user for signature recovery + schema: + type: string + - name: Encoded-Data-Signature + in: header + description: The signature of data, used for signature recovery + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/transaction_history_count_response' +components: + schemas: + user_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + user: + required: + - album_count + - allow_ai_attribution + - artist_coin_badge + - associated_sol_wallets_balance + - associated_wallets_balance + - balance + - blocknumber + - created_at + - current_user_followee_follow_count + - does_current_user_follow + - does_current_user_subscribe + - does_follow_current_user + - erc_wallet + - followee_count + - follower_count + - handle + - handle_lc + - has_collectibles + - id + - is_available + - is_deactivated + - is_storage_v2 + - is_verified + - name + - playlist_count + - repost_count + - spl_wallet + - spl_usdc_wallet + - supporter_count + - supporting_count + - total_audio_balance + - total_balance + - track_count + - updated_at + - verified_with_instagram + - verified_with_tiktok + - verified_with_twitter + - wallet + - waudio_balance + type: object + properties: + album_count: + type: integer + artist_pick_track_id: + type: string + artist_coin_badge: + type: object + properties: + mint: + type: string + logo_uri: + type: string + banner_image_url: + type: string + ticker: + type: string + description: The coin symbol/ticker + coin_flair_mint: + type: string + bio: + type: string + cover_photo: + $ref: '#/components/schemas/cover_photo' + followee_count: + type: integer + follower_count: + type: integer + handle: + type: string + id: + type: string + is_verified: + type: boolean + twitter_handle: + type: string + instagram_handle: + type: string + tiktok_handle: + type: string + verified_with_twitter: + type: boolean + verified_with_instagram: + type: boolean + verified_with_tiktok: + type: boolean + website: + type: string + donation: + type: string + location: + type: string + name: + type: string + playlist_count: + type: integer + profile_picture: + $ref: '#/components/schemas/profile_picture' + repost_count: + type: integer + track_count: + type: integer + is_deactivated: + type: boolean + is_available: + type: boolean + erc_wallet: + type: string + spl_wallet: + type: string + spl_usdc_wallet: + type: string + spl_usdc_payout_wallet: + type: string + supporter_count: + type: integer + supporting_count: + type: integer + total_audio_balance: + type: integer + wallet: + type: string + description: The user's Ethereum wallet address for their account + balance: + type: string + associated_wallets_balance: + type: string + total_balance: + type: string + waudio_balance: + type: string + associated_sol_wallets_balance: + type: string + blocknumber: + type: integer + created_at: + type: string + is_storage_v2: + type: boolean + creator_node_endpoint: + type: string + current_user_followee_follow_count: + type: integer + does_current_user_follow: + type: boolean + does_current_user_subscribe: + type: boolean + does_follow_current_user: + type: boolean + handle_lc: + type: string + updated_at: + type: string + cover_photo_sizes: + type: string + cover_photo_cids: + $ref: '#/components/schemas/cover_photo' + cover_photo_legacy: + type: string + profile_picture_sizes: + type: string + profile_picture_cids: + $ref: '#/components/schemas/profile_picture' + profile_picture_legacy: + type: string + has_collectibles: + type: boolean + playlist_library: + $ref: '#/components/schemas/playlist_library' + allow_ai_attribution: + type: boolean + profile_type: + type: string + cover_photo: + type: object + properties: + 640x: + type: string + 2000x: + type: string + mirrors: + type: array + items: + type: string + profile_picture: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + mirrors: + type: array + items: + type: string + users_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user' + user_track_listen_counts_response: + type: object + properties: + data: + $ref: '#/components/schemas/wild_month_model' + wild_month_model: + type: object + additionalProperties: + $ref: '#/components/schemas/monthly_aggregate_play' + monthly_aggregate_play: + type: object + properties: + totalListens: + type: integer + trackIds: + type: array + items: + type: integer + listenCounts: + type: array + items: + $ref: '#/components/schemas/listen_count' + listen_count: + type: object + properties: + trackId: + type: integer + date: + type: string + listens: + type: integer + tracks_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track' + Track: + required: + - artwork + - comment_count + - duration + - favorite_count + - genre + - id + - is_downloadable + - is_original_available + - permalink + - play_count + - repost_count + - title + - user + type: object + properties: + artwork: + $ref: '#/components/schemas/track_artwork' + description: + type: string + genre: + type: string + id: + type: string + track_cid: + type: string + preview_cid: + type: string + orig_file_cid: + type: string + orig_filename: + type: string + is_original_available: + type: boolean + mood: + type: string + release_date: + type: string + isrc: + type: string + remix_of: + $ref: '#/components/schemas/remix_parent' + repost_count: + type: integer + favorite_count: + type: integer + comment_count: + type: integer + tags: + type: string + title: + type: string + user: + $ref: '#/components/schemas/user' + duration: + type: integer + is_downloadable: + type: boolean + play_count: + type: integer + permalink: + type: string + is_streamable: + type: boolean + ddex_app: + type: string + playlists_containing_track: + type: array + items: + type: integer + pinned_comment_id: + type: integer + album_backlink: + $ref: '#/components/schemas/album_backlink' + access: + $ref: '#/components/schemas/track_access_info' + ai_attribution_user_id: + type: integer + allowed_api_keys: + type: array + items: + type: string + artists: + type: object + audio_analysis_error_count: + type: integer + audio_upload_id: + type: string + blocknumber: + type: integer + bpm: + type: number + comments_disabled: + type: boolean + copyright_line: + type: object + cover_art: + type: string + cover_art_sizes: + type: string + cover_original_artist: + type: string + cover_original_song_title: + type: string + create_date: + type: string + created_at: + type: string + format: date-time + credits_splits: + type: string + ddex_release_ids: + type: object + download: + $ref: '#/components/schemas/media_link' + download_conditions: + $ref: '#/components/schemas/access_gate' + field_visibility: + type: object + followee_favorites: + type: array + items: + type: object + followee_reposts: + type: array + items: + type: object + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + indirect_resource_contributors: + type: object + is_available: + type: boolean + is_custom_bpm: + type: boolean + is_custom_musical_key: + type: boolean + is_delete: + type: boolean + is_download_gated: + type: boolean + is_owned_by_user: + type: boolean + is_scheduled_release: + type: boolean + is_stream_gated: + type: boolean + is_unlisted: + type: boolean + iswc: + type: string + license: + type: string + musical_key: + type: string + parental_warning_type: + type: string + playlists_previously_containing_track: + type: object + preview: + $ref: '#/components/schemas/media_link' + preview_start_seconds: + type: number + producer_copyright_line: + type: object + resource_contributors: + type: object + rights_controller: + type: object + slug: + type: string + stem_of: + type: array + items: + type: object + stream: + $ref: '#/components/schemas/media_link' + stream_conditions: + $ref: '#/components/schemas/access_gate' + track_id: + type: integer + track_segments: + type: object + updated_at: + type: string + format: date-time + track_artwork: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + mirrors: + type: array + items: + type: string + remix_parent: + type: object + properties: + tracks: + type: array + items: + $ref: '#/components/schemas/remix' + track_element: + required: + - parent_track_id + type: object + properties: + parent_track_id: + type: string + album_backlink: + required: + - permalink + - playlist_id + - playlist_name + type: object + properties: + playlist_id: + type: integer + playlist_name: + type: string + permalink: + type: string + reposts: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/activity' + track_activity: + allOf: + - $ref: '#/components/schemas/activity' + - required: + - item + - item_type + type: object + properties: + item_type: + type: string + example: track + enum: + - track + item: + $ref: '#/components/schemas/track' + activity: + required: + - class + - item + - item_type + - timestamp + type: object + properties: + timestamp: + type: string + item_type: + type: string + example: track + enum: + - track + - playlist + item: + type: object + properties: {} + class: + type: string + discriminator: + propertyName: class + collection_activity: + allOf: + - $ref: '#/components/schemas/activity' + - required: + - item + - item_type + type: object + properties: + timestamp: + type: string + item_type: + type: string + example: playlist + enum: + - playlist + item: + $ref: '#/components/schemas/playlist' + playlist: + required: + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - followee_favorites + - followee_reposts + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: + type: string + permalink: + type: string + id: + type: string + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: + type: string + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: + type: integer + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: + type: string + access: + $ref: '#/components/schemas/access' + upc: + type: string + track_count: + type: integer + blocknumber: + type: integer + created_at: + type: string + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: + type: string + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: + type: string + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: + type: string + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + playlist_artwork: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + playlist_added_timestamp: + required: + - metadata_timestamp + - timestamp + - track_id + type: object + properties: + metadata_timestamp: + type: integer + timestamp: + type: integer + track_id: + type: string + access: + required: + - download + - stream + type: object + properties: + stream: + type: boolean + download: + type: boolean tags_response: type: object properties: - data: + data: + type: array + items: + type: string + favorites_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/favorite' + repost: + required: + - repost_item_id + - repost_type + - user_id + type: object + properties: + repost_item_id: + type: string + repost_type: + type: string + user_id: + type: string + favorite: + required: + - created_at + - favorite_item_id + - favorite_type + - user_id + type: object + properties: + favorite_item_id: + type: string + favorite_type: + type: string + user_id: + type: string + created_at: + type: string + playlists_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist_without_tracks' + albums_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist_without_tracks' + user_search: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user' + history_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track_activity' + subscribers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + followers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + following_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + mutual_followers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + related_artist_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + user_ids_addresses_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user_id_address' + user_id_address: + type: object + required: + - user_id + - address + properties: + user_id: + type: string + address: + type: string + connected_wallets_response: + type: object + properties: + data: + $ref: '#/components/schemas/connected_wallets' + connected_wallets: + required: + - erc_wallets + - spl_wallets + type: object + properties: + erc_wallets: + type: array + items: + type: string + spl_wallets: + type: array + items: + type: string + collectibles_response: + type: object + properties: + data: + $ref: '#/components/schemas/collectibles' + collectibles: + type: object + properties: + data: + type: object + properties: {} + description: Raw collectibles JSON structure generated by client + get_challenges: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/challenge_response' + challenge_response: + required: + - amount + - challenge_id + - challenge_type + - disbursed_amount + - is_active + - is_complete + - is_disbursed + - metadata + - user_id + type: object + properties: + challenge_id: + type: string + user_id: + type: string + specifier: + type: string + is_complete: + type: boolean + is_active: + type: boolean + is_disbursed: + type: boolean + current_step_count: + type: integer + max_steps: + type: integer + challenge_type: + type: string + amount: + type: string + disbursed_amount: + type: integer + cooldown_days: + type: integer + metadata: + type: object + properties: {} + get_supporters: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/supporter' + supporter: + required: + - amount + - rank + - sender + type: object + properties: + rank: + type: integer + amount: + type: string + sender: + $ref: '#/components/schemas/user' + get_supported_users: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/supporting' + supporting: + required: + - amount + - rank + - receiver + type: object + properties: + rank: + type: integer + amount: + type: string + receiver: + $ref: '#/components/schemas/user' + verify_token: + type: object + properties: + data: + $ref: '#/components/schemas/decoded_user_token' + decoded_user_token: + required: + - apiKey + - email + - handle + - iat + - name + - sub + - userId + - verified + type: object + properties: + apiKey: + type: string + userId: + type: string + email: + type: string + name: + type: string + handle: + type: string + verified: + type: boolean + profilePicture: + $ref: '#/components/schemas/profilePicture' + sub: + type: string + iat: + type: string + profilePicture: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + developer_apps: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/developer_app' + developer_app: + required: + - address + - name + - user_id + type: object + properties: + address: + type: string + user_id: + type: string + name: + type: string + description: + type: string + image_url: + type: string + authorized_apps: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/authorized_app' + authorized_app: + required: + - address + - grant_created_at + - grant_updated_at + - grantor_user_id + - name + type: object + properties: + address: + type: string + name: + type: string + description: + type: string + image_url: + type: string + grantor_user_id: + type: string + grant_created_at: + type: string + grant_updated_at: + type: string + sales_aggregate_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/sales_aggregate' + sales_aggregate: + required: + - content_id + - content_type + - purchase_count + type: object + properties: + content_type: + type: string + content_id: + type: string + purchase_count: + type: integer + sales_json_response: + type: object + properties: + data: + $ref: '#/components/schemas/sales_json_content' + sales_json_content: + type: object + properties: + sales: + type: array + items: + $ref: '#/components/schemas/sale_json' + sale_json: + type: object + properties: + title: + type: string + description: Title of the content (track/album/playlist) + link: + type: string + description: URL link to the content + purchased_by: + type: string + description: Name of the buyer + buyer_user_id: + type: integer + description: User ID of the buyer + date: + type: string + description: ISO format date string of when the sale occurred + sale_price: + type: number + description: Base sale price in USDC + network_fee: + type: number + description: Network fee deducted from sale in USDC + pay_extra: + type: number + description: Extra amount paid by buyer in USDC + total: + type: number + description: Total amount received by seller in USDC + country: + type: string + description: Country code where purchase was made + encrypted_email: + type: string + description: Encrypted email of buyer if available + encrypted_key: + type: string + description: Encrypted key for decrypting the buyer's email + is_initial: + type: boolean + description: Whether this is an initial encryption from the backfill + pubkey_base64: + type: string + description: Base64 encoded public key of the buyer + remixers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + purchasers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + user_tracks_remixed_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/remixed_track_aggregate' + remixed_track_aggregate: + required: + - remix_count + - title + - track_id + type: object + properties: + track_id: + type: string + title: + type: string + remix_count: + type: integer + email_access_response: + type: object + properties: + data: + $ref: '#/components/schemas/email_access' + email_access: + required: + - created_at + - email_owner_user_id + - encrypted_key + - grantor_user_id + - id + - is_initial + - receiving_user_id + - updated_at + type: object + properties: + id: + type: integer + email_owner_user_id: + type: integer + receiving_user_id: + type: integer + grantor_user_id: + type: integer + encrypted_key: + type: string + is_initial: + type: boolean + created_at: + type: string + updated_at: + type: string + user_comments_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/comment' + related: + $ref: '#/components/schemas/related' + comment: + required: + - created_at + - entity_id + - entity_type + - id + - is_edited + - message + - react_count + - reply_count + type: object + properties: + id: + type: string + entity_id: + type: string + entity_type: + type: string + user_id: + type: string + message: + type: string + mentions: + type: array + items: + $ref: '#/components/schemas/comment_mention' + track_timestamp_s: + type: integer + react_count: + type: integer + reply_count: + type: integer + is_edited: + type: boolean + is_current_user_reacted: + type: boolean + is_artist_reacted: + type: boolean + is_tombstone: + type: boolean + is_muted: + type: boolean + created_at: + type: string + updated_at: + type: string + replies: + type: array + items: + $ref: '#/components/schemas/reply_comment' + parent_comment_id: + type: integer + comment_mention: + required: + - handle + - user_id + type: object + properties: + user_id: + type: integer + handle: + type: string + reply_comment: + required: + - created_at + - entity_id + - entity_type + - id + - is_edited + - message + - react_count + - user_id + type: object + properties: + id: + type: string + entity_id: + type: string + entity_type: + type: string + user_id: + type: string + message: + type: string + mentions: + type: array + items: + $ref: '#/components/schemas/comment_mention' + track_timestamp_s: + type: integer + react_count: + type: integer + is_edited: + type: boolean + is_current_user_reacted: + type: boolean + is_artist_reacted: + type: boolean + created_at: + type: string + updated_at: + type: string + parent_comment_id: + type: integer + playlist_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist' + playlist_tracks_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track' + playlist_search_result: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/playlist' + trending_playlists_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist' + access_info_response: + type: object + properties: + data: + $ref: '#/components/schemas/track_access_info' + track_access_info: + required: + - blocknumber + - user_id + type: object + properties: + access: + type: object + description: Describes what access the given user has + allOf: + - $ref: '#/components/schemas/access' + user_id: + type: string + description: The user ID of the owner of this track + blocknumber: + type: integer + description: The blocknumber this track was last updated + is_stream_gated: + type: boolean + description: Whether or not the owner has restricted streaming behind an + access gate + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/extended_access_gate' + is_download_gated: + type: boolean + description: Whether or not the owner has restricted downloading behind + an access gate + download_conditions: + type: object + description: How to unlock the track download + allOf: + - $ref: '#/components/schemas/extended_access_gate' + extended_access_gate: + oneOf: + - $ref: '#/components/schemas/tip_gate' + - $ref: '#/components/schemas/follow_gate' + - $ref: '#/components/schemas/extended_purchase_gate' + - $ref: '#/components/schemas/nft_gate' + - $ref: '#/components/schemas/token_gate' + tip_gate: + required: + - tip_user_id + type: object + properties: + tip_user_id: + type: integer + description: Must tip the given user ID to unlock + follow_gate: + required: + - follow_user_id + type: object + properties: + follow_user_id: + type: integer + description: Must follow the given user ID to unlock + token_gate: + required: + - token_gate + type: object + properties: + token_gate: + type: object + description: Must hold an NFT of the given collection to unlock + allOf: + - $ref: '#/components/schemas/extended_token_gate' + extended_token_gate: + required: + - token_mint + - token_amount + type: object + properties: + token_mint: + type: string + description: The mint of the token needed to unlock + token_amount: + type: integer + description: The amount of the token needed to unlock + extended_purchase_gate: + required: + - usdc_purchase + type: object + properties: + usdc_purchase: + type: object + description: Must pay the total price and split to the given addresses to + unlock + allOf: + - $ref: '#/components/schemas/extended_usdc_gate' + extended_usdc_gate: + required: + - price + - splits + type: object + properties: + price: + type: integer + splits: + type: array + items: + $ref: '#/components/schemas/extended_payment_split' + extended_payment_split: + required: + - amount + - payout_wallet + - percentage + type: object + properties: + user_id: + type: integer + percentage: + type: number + eth_wallet: + type: string + payout_wallet: + type: string + amount: + type: integer + nft_gate: + required: + - nft_collection + type: object + properties: + nft_collection: + type: object + description: Must hold an NFT of the given collection to unlock + allOf: + - $ref: '#/components/schemas/nft_collection' + media_link: + type: object + properties: + url: + type: string + mirrors: + type: array + items: + type: string + access_gate: + oneOf: + - $ref: '#/components/schemas/tip_gate' + - $ref: '#/components/schemas/follow_gate' + - $ref: '#/components/schemas/purchase_gate' + - $ref: '#/components/schemas/nft_gate' + - $ref: '#/components/schemas/token_gate' + nft_collection: + required: + - address + - chain + - name + type: object + properties: + chain: + type: string + example: eth + enum: + - eth + - sol + standard: + type: string + example: ERC721 + enum: + - ERC721 + - ERC1155 + address: + type: string + name: + type: string + imageUrl: + type: string + externalLink: + type: string + track_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/track' + track_inspect: + type: object + properties: + data: + $ref: '#/components/schemas/blob_info' + blob_info: + required: + - content_type + - size + type: object + properties: + size: + type: integer + content_type: + type: string + track_inspect_list: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/blob_info' + track_comments_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/comment' + related: + $ref: '#/components/schemas/related' + trending_ids_response: + type: object + properties: + data: + $ref: '#/components/schemas/trending_times_ids' + trending_times_ids: + type: object + properties: + week: + type: array + items: + $ref: '#/components/schemas/track_id' + month: + type: array + items: + $ref: '#/components/schemas/track_id' + year: + type: array + items: + $ref: '#/components/schemas/track_id' + track_id: + required: + - id + type: object + properties: + id: + type: string + track_favorites_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + track_reposts_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + remixes_response: + required: + - count + type: object + properties: + count: + type: integer + tracks: + type: array + items: + $ref: '#/components/schemas/track' + remixing_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track' + tracks_count_response: + type: object + required: + - data + properties: + data: + type: integer + description: The total number of tracks matching the filter criteria + example: 15 + track_comment_count_response: + type: object + properties: + data: + type: integer + track_comment_notification_response: + type: object + properties: + data: + $ref: '#/components/schemas/comment_notification_setting' + comment_notification_setting: + required: + - is_muted + type: object + properties: + is_muted: + type: boolean + stream_url_response: + required: + - data + type: object + properties: + data: + type: string + track_search: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Track' + top_listener: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/top_listener' + stems_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/stem' + stem: + required: + - blocknumber + - category + - cid + - id + - orig_filename + - parent_id + - user_id + type: object + properties: + id: + type: string + parent_id: + type: string + category: + type: string + cid: + type: string + user_id: + type: string + blocknumber: + type: integer + orig_filename: + type: string + undisbursed_challenges: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/undisbursed_challenge' + undisbursed_challenge: + required: + - amount + - challenge_id + - completed_at + - completed_blocknumber + - created_at + - handle + - specifier + - user_id + - wallet + type: object + properties: + challenge_id: + type: string + user_id: + type: string + specifier: + type: string + amount: + type: string + completed_blocknumber: + type: integer + handle: + type: string + wallet: + type: string + created_at: + type: string + completed_at: + type: string + cooldown_days: + type: integer + get_tips_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/tip' + tip: + required: + - amount + - created_at + - followee_supporters + - receiver + - sender + - slot + - tx_signature + type: object + properties: + amount: + type: string + sender: + $ref: '#/components/schemas/user' + receiver: + $ref: '#/components/schemas/user' + created_at: + type: string + slot: + type: integer + followee_supporters: + type: array + items: + $ref: '#/components/schemas/supporter_reference' + tx_signature: + type: string + developer_app_response: + type: object + properties: + data: + $ref: '#/components/schemas/developer_app' + dashboard_wallet_users_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/dashboard_wallet_user' + dashboard_wallet_user: + required: + - user + - wallet + type: object + properties: + wallet: + type: string + user: + $ref: '#/components/schemas/user' + comment_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/comment' + comment_replies_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/reply_comment' + related: + $ref: '#/components/schemas/related' + unclaimed_id_response: + type: object + properties: + data: + type: string + events_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/event' + event: + required: + - created_at + - event_data + - event_id + - event_type + - updated_at + - user_id + type: object + properties: + event_id: + type: string + event_type: + type: string + example: remix_contest + enum: + - remix_contest + - live_event + - new_release + user_id: + type: string + entity_type: + type: string + example: track + enum: + - track + - collection + - user + entity_id: + type: string + end_date: + type: string + is_deleted: + type: boolean + created_at: + type: string + updated_at: + type: string + event_data: + type: object + properties: {} + best_selling_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/best_selling_item' + related: + $ref: '#/components/schemas/related' + best_selling_item: + required: + - content_id + - owner_id + - title + type: object + properties: + content_id: + type: string + content_type: + type: string + example: track + enum: + - track + - album + title: + type: string + owner_id: + type: string + coin: + type: object + description: A coin object + required: + - mint + - ticker + - decimals + - name + - created_at + properties: + mint: + type: string + description: The mint address of the coin + example: bearR26zyyB3fNQm5wWv1ZfN8MPQDUMwaAuoG79b1Yj + ticker: + type: string + description: The coin symbol/ticker + example: BEAR + decimals: + type: integer + description: The number of decimals for the coin + example: 9 + name: + type: string + description: The coin name + example: BEAR + logo_uri: + type: string + description: The URI for the coin's logo image + example: https://example.com/logo.png + banner_image_url: + type: string + description: The URI for the coin's banner image + example: https://example.com/banner.png + description: + type: string + description: The description of the coin + example: A majestic bear token for wildlife conservation + x_handle: + type: string + description: X (Twitter) handle for the coin + example: bear_token + instagram_handle: + type: string + description: Instagram handle for the coin + example: bear_token + tiktok_handle: + type: string + description: TikTok handle for the coin + example: bear_token + website: + type: string + description: Website URL for the coin + example: https://bear-token.com + link_1: + type: string + description: Generic link URL for the coin + example: https://x.com/bear_token + link_2: + type: string + description: Generic link URL for the coin + example: https://instagram.com/bear_token + link_3: + type: string + description: Generic link URL for the coin + example: https://tiktok.com/@bear_token + link_4: + type: string + description: Generic link URL for the coin + example: https://bear-token.com + has_discord: + type: boolean + description: Whether the coin has a Discord server + example: false + created_at: + type: string + format: date-time + description: The date and time when the coin was created + example: '2024-01-15T10:30:00Z' + updated_at: + type: string + format: date-time + description: The date and time when the coin was last updated + example: '2024-01-15T10:30:00Z' + owner_id: + type: string + description: The user ID of the coin owner + example: 7eP5n + escrow_recipient: + type: string + description: The escrow recipient address for custom-created coins without + DBCs + example: DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 + price: + type: number + description: Current price in USD (from Birdeye or bonding curve) + priceChange24hPercent: + type: number + description: 24h price change in percent + liquidity: + type: number + description: Current liquidity in USD + marketCap: + type: number + description: Market capitalization in USD + totalSupply: + type: number + description: Total supply of the token + circulatingSupply: + type: number + description: Circulating supply of the token + holder: + type: integer + description: Number of holders + totalVolumeUSD: + type: number + description: Total volume of coin traded in USD (all time) + dynamicBondingCurve: + type: object + description: Information about the dynamic bonding curve if one exists + properties: + address: + type: string + description: Address of the bonding curve pool + price: + type: number + description: Current price in the pool's quote token + priceUSD: + type: number + description: Current price in USD + curveProgress: + type: number + description: Progress along the bonding curve (0.0 - 1.0) + isMigrated: + type: boolean + description: Whether the bonding curve has been migrated + creatorQuoteFee: + type: number + description: Creator quote fee for the bonding curve + totalTradingQuoteFee: + type: number + description: Total trading quote fee accumulated + creatorWalletAddress: + type: string + description: Address of the pool creator's wallet + artist_fees: + type: object + description: Artist fee information + properties: + unclaimed_fees: + type: number + total_fees: + type: number + artist_locker: + type: object + description: Artist locker vesting information + properties: + address: + type: string + locked: + type: number + unlocked: + type: number + claimable: + type: number + reward_pool: + type: object + description: Reward pool information + properties: + address: + type: string + balance: + type: number + coin_response: + type: object + properties: + data: + $ref: '#/components/schemas/coin' + coins_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/coin' + create_coin_request: + type: object + required: + - mint + - ticker + - decimals + - name + properties: + mint: + type: string + description: The mint address of the coin + example: bearR26zyyB3fNQm5wWv1ZfN8MPQDUMwaAuoG79b1Yj + ticker: + type: string + description: The coin symbol/ticker + example: BEAR + decimals: + type: integer + description: The number of decimals for the coin (0-18) + minimum: 0 + maximum: 18 + example: 9 + name: + type: string + description: The coin name + example: BEAR + logo_uri: + type: string + description: The URI for the coin's logo image + example: https://example.com/logo.png + banner_image_url: + type: string + description: The URI for the coin's banner image + example: https://example.com/banner.png + format: uri + description: + type: string + description: The description of the coin + example: A majestic bear token for wildlife conservation + link_1: + type: string + description: Generic link URL for the coin + example: https://x.com/bear_token + format: uri + link_2: + type: string + description: Generic link URL for the coin + example: https://instagram.com/bear_token + format: uri + link_3: + type: string + description: Generic link URL for the coin + example: https://tiktok.com/@bear_token + format: uri + link_4: + type: string + description: Generic link URL for the coin + example: https://bear-token.com + format: uri + create_coin_response: + type: object + properties: + data: + type: object + required: + - mint + - ticker + - user_id + - decimals + - name + - created_at + properties: + mint: + type: string + description: The mint address of the coin + example: bearR26zyyB3fNQm5wWv1ZfN8MPQDUMwaAuoG79b1Yj + ticker: + type: string + description: The coin symbol/ticker + example: BEAR + user_id: + type: integer + description: The user ID who created the coin + example: 1 + decimals: + type: integer + description: The number of decimals for the coin + example: 9 + name: + type: string + description: The coin name + example: BEAR + logo_uri: + type: string + description: The URI for the coin's logo image + example: https://example.com/logo.png + banner_image_url: + type: string + description: The URI for the coin's banner image + example: https://example.com/banner.png + description: + type: string + description: The description of the coin + example: A majestic bear token for wildlife conservation + link_1: + type: string + description: Generic link URL for the coin + example: https://x.com/bear_token + link_2: + type: string + description: Generic link URL for the coin + example: https://instagram.com/bear_token + link_3: + type: string + description: Generic link URL for the coin + example: https://tiktok.com/@bear_token + link_4: + type: string + description: Generic link URL for the coin + example: https://bear-token.com + created_at: + type: string + format: date-time + description: The date and time when the coin was created + example: '2024-01-15T10:30:00Z' + update_coin_request: + type: object + description: Request body for updating coin information + properties: + description: + type: string + description: The description of the coin (max 2500 characters) + example: Updated description for the bear token + maxLength: 2500 + banner_image_url: + type: string + description: URL for the coin's banner image + example: https://example.com/banner.png + format: uri + link_1: + type: string + description: Generic link URL for the coin + example: https://x.com/bear_token + format: uri + link_2: + type: string + description: Generic link URL for the coin + example: https://instagram.com/bear_token + format: uri + link_3: + type: string + description: Generic link URL for the coin + example: https://tiktok.com/@bear_token + format: uri + link_4: + type: string + description: Generic link URL for the coin + example: https://bear-token.com + format: uri + update_coin_response: + type: object + properties: + success: + type: boolean + description: Indicates if the update was successful + example: true + coin_insights: + type: object + description: 'Additional token information from Birdeye''s defi token overview + API. + + Includes price, volume, supply, market cap, and other on-chain and market + data. + + ' + required: + - mint + - marketCap + - fdv + - liquidity + - lastTradeUnixTime + - lastTradeHumanTime + - price + - history24hPrice + - priceChange24hPercent + - uniqueWallet24h + - uniqueWalletHistory24h + - uniqueWallet24hChangePercent + - totalSupply + - circulatingSupply + - holder + - trade24h + - tradeHistory24h + - trade24hChangePercent + - sell24h + - sellHistory24h + - sell24hChangePercent + - buy24h + - buyHistory24h + - buy24hChangePercent + - v24h + - v24hUSD + - vHistory24h + - totalVolume + - totalVolumeUSD + - volumeBuy + - volumeBuyUSD + - volumeSell + - volumeSellUSD + - totalTrade + - buy + - sell + - dynamicBondingCurve + properties: + address: + type: string + description: The SPL token mint address + example: DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 + decimals: + type: integer + description: Number of decimals for the token + example: 5 + symbol: + type: string + description: The token symbol + example: BONK + name: + type: string + description: The token name + example: Bonk + marketCap: + type: number + description: Market capitalization in USD + example: 2625779824.994664 + fdv: + type: number + description: Fully diluted valuation in USD + example: 2625779824.994664 + extensions: + type: object + description: Token metadata and links + properties: + coingeckoId: + type: string + description: CoinGecko ID + example: bonk + description: + type: string + description: Token description + example: The Official Bonk Inu token + twitter: + type: string + description: Twitter URL + example: https://twitter.com/bonk_inu + website: + type: string + description: Website URL + example: https://www.bonkcoin.com/ + discord: + type: string + description: Discord invite URL + example: https://discord.gg/ubqvDDFUhf + liquidity: + type: number + description: Current liquidity in USD + example: 18977326.389274083 + lastTradeUnixTime: + type: integer + description: Unix timestamp of the last trade + example: 1752620592 + lastTradeHumanTime: + type: string + description: ISO8601 time of the last trade + example: '2025-07-15T23:03:12' + price: + type: number + description: Current price in USD + example: 2.9571022098881748e-05 + history24hPrice: + type: number + description: Price 24 hours ago in USD + example: 2.7195701160436288e-05 + priceChange24hPercent: + type: number + description: 24h price change in percent + example: 8.73417796596848 + uniqueWallet24h: + type: integer + description: Unique wallets traded in last 24h + example: 20242 + uniqueWalletHistory24h: + type: integer + description: Unique wallets traded in previous 24h + example: 21155 + uniqueWallet24hChangePercent: + type: number + description: 24h change in unique wallets (percent) + example: -4.315764594658473 + totalSupply: + type: number + description: Total supply of the token + example: 88795707372386.03 + circulatingSupply: + type: number + description: Circulating supply of the token + example: 88795707372386.03 + holder: + type: integer + description: Number of holders + example: 957291 + trade24h: + type: integer + description: Number of trades in last 24h + example: 449987 + tradeHistory24h: + type: integer + description: Number of trades in previous 24h + example: 390400 + trade24hChangePercent: + type: number + description: 24h change in trade count (percent) + example: 15.263063524590164 + sell24h: + type: integer + description: Number of sell trades in last 24h + example: 223845 + sellHistory24h: + type: integer + description: Number of sell trades in previous 24h + example: 191979 + sell24hChangePercent: + type: number + description: 24h change in sell trades (percent) + example: 16.598690481771445 + buy24h: + type: integer + description: Number of buy trades in last 24h + example: 226142 + buyHistory24h: + type: integer + description: Number of buy trades in previous 24h + example: 198421 + buy24hChangePercent: + type: number + description: 24h change in buy trades (percent) + example: 13.970799461750522 + v24h: + type: number + description: 24h trading volume (token units) + example: 2456470915352.043 + v24hUSD: + type: number + description: 24h trading volume in USD + example: 69961943.60091284 + vHistory24h: + type: number + description: Previous 24h trading volume (token units) + example: 1849367819551.6223 + vHistory24hUSD: + type: number + description: Previous 24h trading volume in USD + example: 49529721.91224754 + v24hChangePercent: + type: number + description: 24h change in volume (percent) + example: 32.82760137718911 + vBuy24h: + type: number + description: 24h buy volume (token units) + example: 1267704208631.2197 + vBuy24hUSD: + type: number + description: 24h buy volume in USD + example: 35985775.23314727 + vBuyHistory24h: + type: number + description: Previous 24h buy volume (token units) + example: 926415751610.5529 + vBuyHistory24hUSD: + type: number + description: Previous 24h buy volume in USD + example: 24916558.31987226 + vBuy24hChangePercent: + type: number + description: 24h change in buy volume (percent) + example: 36.83966474310746 + vSell24h: + type: number + description: 24h sell volume (token units) + example: 1188766706720.8232 + vSell24hUSD: + type: number + description: 24h sell volume in USD + example: 33976168.367765576 + vSellHistory24h: + type: number + description: Previous 24h sell volume (token units) + example: 922952067941.0695 + vSellHistory24hUSD: + type: number + description: Previous 24h sell volume in USD + example: 24613163.592375275 + vSell24hChangePercent: + type: number + description: 24h change in sell volume (percent) + example: 28.800481413161105 + numberMarkets: + type: integer + description: Number of markets the token is traded on + example: 317 + totalVolume: + type: number + description: Total volume of coin traded (all time) + example: 158766463.26959822 + totalVolumeUSD: + type: number + description: Total volume of coin traded in USD (all time) + example: 20188521260.405678 + volumeBuy: + type: number + description: Total volume bought (all time) + example: 78227859.16098201 + volumeBuyUSD: + type: number + description: Total volume bought in USD (all time) + example: 20188521260.405678 + volumeSell: + type: number + description: Total volume sold (all time) + example: 80538604.1086162 + volumeSellUSD: + type: number + description: Total volume sold in USD (all time) + example: 20188521260.405678 + totalTrade: + type: integer + description: Total number of trades (all time) + example: 258522892 + buy: + type: integer + description: Total number of buys (all time) + example: 87829497 + sell: + type: integer + description: Total number of sells (all time) + example: 170693395 + dynamicBondingCurve: + type: object + description: Information about the dynamic bonding curve if one exists for + the Coin + required: + - address + - price + - priceUSD + - curveProgress + - creatorQuoteFee + - totalTradingQuoteFee + - creatorWalletAddress + properties: + address: + type: string + description: Address of the bonding curve pool + example: 2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN + price: + type: number + description: Current price in the pool's quote token (e.g., AUDIO) + example: 0.0028402095736478586 + priceUSD: + type: number + description: Current price in USD + example: 2.9571022098881748e-05 + curveProgress: + type: number + description: Progress along the bonding curve (0.0 - 1.0) + example: 0.75 + isMigrated: + type: boolean + description: Whether the bonding curve has been migrated + example: false + creatorQuoteFee: + type: number + description: Creator quote fee for the bonding curve + example: 0.05 + totalTradingQuoteFee: + type: number + description: Total trading quote fee accumulated + example: 0.001 + creatorWalletAddress: + type: string + description: Address of the pool creator's wallet + example: 2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN + coin_insights_response: + type: object + properties: + data: + $ref: '#/components/schemas/coin_insights' + coin_member: + type: object + required: + - balance + - user_id + properties: + balance: + type: integer + description: The user's balance of the specific coin (in wei) + example: 100000 + user_id: + type: string + description: The ID of the user with a non-zero balance + example: 7eP5n + coin_members_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/coin_member' + coin_members_count_response: + type: object + required: + - data + properties: + data: + type: integer + description: The total number of users with a non-zero balance of the specific + coin + example: 42 + coins_volume_leaders_response: + type: object + required: + - data + properties: + data: + type: array + items: + type: object + required: + - address + - volume + properties: + address: + type: string + volume: + type: number + user: + $ref: '#/components/schemas/user' + user_coin: + required: + - mint + - ticker + - decimals + - owner_id + - balance + - has_discord + - balance_usd + type: object + properties: + mint: + type: string + description: The coin mint address + example: 9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM + ticker: + type: string + description: The coin symbol + example: $AUDIO + decimals: + type: integer + description: The number of decimals for the coin + example: 8 + owner_id: + type: string + description: The ID of the user associated with the coin + example: 7eP5n + logo_uri: + type: string + nullable: true + description: URL to the coin's logo image + example: https://example.com/logo.png + banner_image_url: + type: string + nullable: true + description: URL to the coin's banner image + example: https://example.com/banner.png + has_discord: + type: boolean + description: Whether the coin has a Discord server + example: true + balance: + type: integer + description: The balance of the coin in the user's account (in wei) + example: 1000000000 + balance_usd: + type: number + description: The balance of the coin in the user's account in USD + example: 1.23 + user_coins_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/user_coin' + user_coin_with_accounts: + type: object + required: + - mint + - ticker + - decimals + - balance + - balance_usd + - accounts + properties: + mint: + type: string + description: The coin mint address + example: 9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM + ticker: + type: string + description: The coin symbol + example: $AUDIO + decimals: + type: integer + description: The number of decimals for the coin + example: 8 + logo_uri: + type: string + nullable: true + description: URL to the coin's logo image + example: https://example.com/logo.png + balance: + type: integer + description: The total balance of the coin in the user's account (in wei) + example: 1000000000 + balance_usd: + type: number + description: The total balance of the coin in the user's account in USD + example: 1.23 + accounts: + type: array + items: + $ref: '#/components/schemas/user_coin_account' + user_coin_account: + type: object + required: + - account + - owner + - balance + - balance_usd + - is_in_app_wallet + properties: + account: + type: string + description: The token account address + example: CTyFguG69kwYrzk24P3UuBvY1rR5atu9kf2S6XEwAU8X + owner: + type: string + description: The owner wallet of the token account + example: HzZ3EKACbH6XEHs59Rt1adVzUKv5cTDE9o9YWFaMhwpF + balance: + type: integer + description: The balance of the coin in the user's account (in wei) + example: 1000000000 + balance_usd: + type: number + description: The balance of the coin in the user's account in USD + example: 1.23 + is_in_app_wallet: + type: boolean + description: Whether the account is in the user's in-app wallet + example: true + user_coin_response: + type: object + properties: + data: + $ref: '#/components/schemas/user_coin_with_accounts' + balance_history_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/balance_history_data_point' + balance_history_data_point: + type: object + required: + - timestamp + - balance_usd + properties: + timestamp: + type: integer + format: int64 + description: Unix timestamp in seconds + example: 1704067200 + balance_usd: + type: number + format: double + description: Total portfolio balance in USD at this timestamp + example: 1234.56 + claim_rewards_response: + type: object + required: + - data + properties: + data: + type: array + items: + type: object + required: + - challengeId + - specifier + properties: + challengeId: + type: string + description: The challenge ID + example: u + specifier: + type: string + description: The challenge specifier + example: 7eP5n + amount: + type: string + description: The reward amount + example: '1000000000' + signatures: + type: array + items: + type: string + description: Transaction signatures + example: + - 5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA + error: + type: string + description: Error message if claim failed + example: Insufficient balance + claim_rewards_request: + type: object + required: + - userId + properties: + challengeId: + type: string + description: The challenge ID to filter rewards (optional) + example: u + specifier: + type: string + description: The specifier to filter rewards (optional) + example: 7eP5n + userId: + type: string + description: The user ID to claim rewards for + example: 7eP5n + redeem_amount_response: + type: object + required: + - amount + properties: + amount: + type: integer + description: Static amount indicator (always 1) + example: 1 + reward_code_response: + type: object + required: + - code + - amount + properties: + code: + type: string + description: The reward code + example: XYZ123 + amount: + type: integer + description: The amount of coins rewarded by this code + example: 100 + reward_code_error_response: + type: object + properties: + error: + type: string + description: Error message indicating why the code cannot be redeemed + example: used + enum: + - used + - invalid + create_reward_code_request: + type: object + required: + - signature + - mint + - amount + properties: + signature: + type: string + description: Base64-encoded Solana Ed25519 signature of the string "code" + example: 3fG7xQh2L8vK9pN4mR5sT6uW7vX8yZ1aB2cD3eF4gH5iJ6kL7mN8oP9qR0sT1uV2wX3yZ4aB5cD6eF7gH8iJ9k= + mint: + type: string + description: The coin mint address + example: 9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM + amount: + type: integer + description: The reward amount (must be greater than 0) + minimum: 1 + example: 100 + create_reward_code_response: + type: object + required: + - code + - mint + - reward_address + - amount + properties: + code: + type: string + description: The generated 6-character alphanumeric reward code + example: aB3d5F + mint: + type: string + description: The coin mint address + example: 9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM + reward_address: + type: string + description: The reward address (authorized public key) + example: 9XeZbswbSSUU4AHVArQbTQjAEjAPhVweGU5cogBVkvh4 + amount: + type: integer + description: The reward amount + example: 100 + prize_claim_request: + type: object + required: + - signature + - wallet + properties: + signature: + type: string + description: The Solana transaction signature for the 2 YAK payment + example: 5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA + wallet: + type: string + description: The wallet address that sent the transaction + example: HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC + prize_claim_response: + type: object + required: + - prize_id + - prize_name + - wallet + properties: + prize_id: + type: string + description: The unique identifier of the prize won + example: prize_1_yak_airdrop + prize_name: + type: string + description: The name of the prize won + example: 1 YAK Airdrop + wallet: + type: string + description: The wallet address that claimed the prize + example: HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC + prize_type: + type: string + description: The type of prize (e.g., "coin_airdrop", "download") + example: coin_airdrop + action_data: + type: object + description: Prize-specific action data (e.g., redeem code/URL for coin + airdrops, download URL for downloads) + additionalProperties: true + example: + code: aB3d5F + url: /coins/YAK/redeem/aB3d5F + prize_public: + type: object + required: + - prize_id + - name + - weight + properties: + prize_id: + type: string + description: The unique identifier of the prize + example: prize_1_yak_airdrop + name: + type: string + description: The name of the prize + example: 1 YAK Airdrop + description: + type: string + description: Description of the prize + example: Win 1 YAK coin airdrop + weight: + type: integer + description: Weight for random selection (higher = more likely) + example: 1 + metadata: + type: object + description: Sanitized metadata (excludes sensitive URLs) + additionalProperties: true + example: + type: coin_airdrop + amount: 1000000000 + prizes_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/prize_public' + description: List of active prizes available for claiming + claimed_prize: + type: object + required: + - id + - wallet + - signature + - mint + - amount + - prize_id + - prize_name + - created_at + properties: + id: + type: integer + description: The unique identifier of the claimed prize record + example: 1 + wallet: + type: string + description: The wallet address that claimed the prize + example: HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC + signature: + type: string + description: The transaction signature used to claim the prize + example: 5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA + mint: + type: string + description: The coin mint address used for the claim + example: ZDaUDL4XFdEct7UgeztrFQAptsvh4ZdhyZDZ1RpxYAK + amount: + type: integer + description: The amount paid to claim the prize (in smallest unit, e.g., + lamports) + example: 2000000000 + prize_id: + type: string + description: The unique identifier of the prize won + example: prize_1_yak_airdrop + prize_name: + type: string + description: The name of the prize won + example: 1 YAK Airdrop + prize_type: + type: string + description: The type of prize (e.g., "coin_airdrop", "download") + example: coin_airdrop + created_at: + type: string + format: date-time + description: When the prize was claimed + example: '2024-01-15T10:30:00Z' + claimed_prizes_response: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/claimed_prize' + description: List of claimed prizes for the wallet (action_data excluded + for security) + full_get_supporters: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/supporter' + albums_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist_without_tracks' + transaction_history_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/transaction_details' + remixers_count_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: integer + user_account_response: + type: object + properties: + data: + $ref: '#/components/schemas/account' + history_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track_activity' + full_purchasers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + bulk_subscribers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user_subscribers' + full_mutual_followers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + full_get_supporting: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/supporting' + collection_library_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/collection_activity_without_tracks' + transaction_history_count_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: integer + related_artist_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + track_library_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track_activity' + full_get_supporter: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/supporter' + remixables_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track' + top_users_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + top_genre_users_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + account_full: + required: + - playlists + - track_save_count + - user + type: object + properties: + user: + $ref: '#/components/schemas/user' + playlists: + type: array + items: + $ref: '#/components/schemas/account_collection' + playlist_library: + $ref: '#/components/schemas/playlist_library' + track_save_count: + type: integer + full_supporting: + required: + - amount + - rank + - receiver + type: object + properties: + rank: + type: integer + amount: + type: string + receiver: + $ref: '#/components/schemas/user' + search_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/search_model' + managed_users_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/managed_user' + version_metadata: + required: + - service + - version + type: object + properties: + service: + type: string + version: + type: string + purchasers_count_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: integer + attestation_reponse: + type: object + properties: + data: + $ref: '#/components/schemas/attestation' + full_supporter: + required: + - amount + - rank + - sender + type: object + properties: + rank: + type: integer + amount: + type: string + sender: + $ref: '#/components/schemas/user' + track_activity_full: + allOf: + - $ref: '#/components/schemas/activity' + - required: + - item + - item_type + type: object + properties: + item_type: + type: string + example: track + enum: + - track + item: + $ref: '#/components/schemas/track' + playlist_updates_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/playlist_updates' + full_remixers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + user_subscribers: + required: + - user_id + type: object + properties: + user_id: + type: string + subscriber_ids: + type: array + items: + type: string + playlist_updates: + type: object + properties: + playlist_updates: + type: array + items: + $ref: '#/components/schemas/playlist_update' + collection_activity_full_without_tracks: + allOf: + - $ref: '#/components/schemas/activity' + - required: + - item + - item_type + type: object + properties: + item_type: + type: string + example: playlist + enum: + - playlist + item: + $ref: '#/components/schemas/playlist_without_tracks' + managers_response: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/user_manager' + cid_data_response: + type: object + properties: + data: + $ref: '#/components/schemas/data_and_type' + search_autocomplete_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/search_model' + purchases_count_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: integer + related: + type: object + properties: + users: + type: array + items: + $ref: '#/components/schemas/user' + tracks: + type: array + items: + $ref: '#/components/schemas/track' + playlists: + type: array + items: + $ref: '#/components/schemas/playlist' + purchases_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/purchase' + stem_full: + required: + - blocknumber + - category + - cid + - id + - orig_filename + - parent_id + - user_id + type: object + properties: + id: + type: string + parent_id: + type: string + category: + type: string + cid: + type: string + user_id: + type: string + blocknumber: + type: integer + orig_filename: + type: string + track_full: + required: + - access + - artwork + - blocknumber + - comment_count + - cover_art_sizes + - created_at + - download + - duration + - favorite_count + - field_visibility + - followee_favorites + - followee_reposts + - genre + - has_current_user_reposted + - has_current_user_saved + - id + - is_available + - is_delete + - is_download_gated + - is_downloadable + - is_original_available + - is_owned_by_user + - is_scheduled_release + - is_stream_gated + - is_unlisted + - permalink + - play_count + - preview + - remix_of + - repost_count + - route_id + - stream + - title + - track_segments + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/track_artwork' + description: + type: string + genre: + type: string + id: + type: string + track_cid: + type: string + preview_cid: + type: string + orig_file_cid: + type: string + orig_filename: + type: string + is_original_available: + type: boolean + mood: + type: string + release_date: + type: string + remix_of: + $ref: '#/components/schemas/remix_parent' + repost_count: + type: integer + favorite_count: + type: integer + comment_count: + type: integer + tags: + type: string + title: + type: string + user: + $ref: '#/components/schemas/user' + duration: + type: integer + is_downloadable: + type: boolean + play_count: + type: integer + permalink: + type: string + is_streamable: + type: boolean + ddex_app: + type: string + playlists_containing_track: + type: array + items: + type: integer + pinned_comment_id: + type: integer + album_backlink: + $ref: '#/components/schemas/album_backlink' + access: + type: object + description: Describes what access the given user has + allOf: + - $ref: '#/components/schemas/access' + blocknumber: + type: integer + description: The blocknumber this track was last updated + create_date: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/cover_art' + created_at: + type: string + credits_splits: + type: string + isrc: + type: string + license: + type: string + iswc: + type: string + field_visibility: + $ref: '#/components/schemas/field_visibility' + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + has_current_user_reposted: + type: boolean + is_scheduled_release: + type: boolean + is_unlisted: + type: boolean + has_current_user_saved: + type: boolean + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + route_id: + type: string + stem_of: + $ref: '#/components/schemas/stem_parent' + track_segments: + type: array + items: + $ref: '#/components/schemas/track_segment' + updated_at: + type: string + user_id: + type: string + is_delete: + type: boolean + cover_art: + type: string + is_available: + type: boolean + ai_attribution_user_id: + type: integer + allowed_api_keys: + type: array + items: + type: string + audio_upload_id: + type: string + preview_start_seconds: + type: number + bpm: + type: number + is_custom_bpm: + type: boolean + musical_key: + type: string + is_custom_musical_key: + type: boolean + audio_analysis_error_count: + type: integer + comments_disabled: + type: boolean + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + resource_contributors: + type: array + items: + type: object + properties: {} + indirect_resource_contributors: + type: array + items: + type: object + properties: {} + rights_controller: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + is_stream_gated: + type: boolean + description: Whether or not the owner has restricted streaming behind an + access gate + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_download_gated: + type: boolean + description: Whether or not the owner has restricted downloading behind + an access gate + download_conditions: + type: object + description: How to unlock the track download + allOf: + - $ref: '#/components/schemas/access_gate' + cover_original_song_title: + type: string + cover_original_artist: + type: string + is_owned_by_user: + type: boolean + description: Indicates whether the track is owned by the user for MRI sake + stream: + $ref: '#/components/schemas/url_with_mirrors' + download: + $ref: '#/components/schemas/url_with_mirrors' + preview: + $ref: '#/components/schemas/url_with_mirrors' + reactions: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/reaction' + playlist_library: + type: object + properties: + contents: + type: array + items: + type: object + properties: {} + data_and_type: + type: object + properties: + type: + type: string + data: + $ref: '#/components/schemas/cid_data' + full_get_supported_users: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/supporting' + playlist_full: + required: + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - followee_favorites + - followee_reposts + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: + type: string + permalink: + type: string + id: + type: string + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: + type: string + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: + type: integer + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: + type: string + access: + $ref: '#/components/schemas/access' + upc: + type: string + track_count: + type: integer + blocknumber: + type: integer + created_at: + type: string + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: + type: string + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: + type: string + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: + type: string + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + user_full: + required: + - album_count + - allow_ai_attribution + - artist_coin_badge + - associated_sol_wallets_balance + - associated_wallets_balance + - balance + - blocknumber + - created_at + - current_user_followee_follow_count + - does_current_user_follow + - does_current_user_subscribe + - does_follow_current_user + - erc_wallet + - followee_count + - follower_count + - handle + - handle_lc + - has_collectibles + - id + - is_available + - is_deactivated + - is_storage_v2 + - is_verified + - name + - playlist_count + - repost_count + - spl_wallet + - spl_usdc_wallet + - supporter_count + - supporting_count + - total_audio_balance + - total_balance + - track_count + - updated_at + - verified_with_instagram + - verified_with_tiktok + - verified_with_twitter + - wallet + - waudio_balance + type: object + properties: + album_count: + type: integer + artist_pick_track_id: + type: string + artist_coin_badge: + type: object + properties: + mint: + type: string + logo_uri: + type: string + banner_image_url: + type: string + ticker: + type: string + description: The coin symbol/ticker + coin_flair_mint: + type: string + bio: + type: string + cover_photo: + $ref: '#/components/schemas/cover_photo' + followee_count: + type: integer + follower_count: + type: integer + handle: + type: string + id: + type: string + is_verified: + type: boolean + twitter_handle: + type: string + instagram_handle: + type: string + tiktok_handle: + type: string + verified_with_twitter: + type: boolean + verified_with_instagram: + type: boolean + verified_with_tiktok: + type: boolean + website: + type: string + donation: + type: string + location: + type: string + name: + type: string + playlist_count: + type: integer + profile_picture: + $ref: '#/components/schemas/profile_picture' + repost_count: + type: integer + track_count: + type: integer + is_deactivated: + type: boolean + is_available: + type: boolean + erc_wallet: + type: string + spl_wallet: + type: string + spl_usdc_wallet: + type: string + spl_usdc_payout_wallet: + type: string + supporter_count: + type: integer + supporting_count: + type: integer + total_audio_balance: + type: integer + wallet: + type: string + description: The user's Ethereum wallet address for their account + balance: + type: string + associated_wallets_balance: + type: string + total_balance: + type: string + waudio_balance: + type: string + associated_sol_wallets_balance: + type: string + blocknumber: + type: integer + created_at: + type: string + is_storage_v2: + type: boolean + creator_node_endpoint: + type: string + current_user_followee_follow_count: + type: integer + does_current_user_follow: + type: boolean + does_current_user_subscribe: + type: boolean + does_follow_current_user: + type: boolean + handle_lc: + type: string + updated_at: + type: string + cover_photo_sizes: + type: string + cover_photo_cids: + $ref: '#/components/schemas/cover_photo' + cover_photo_legacy: + type: string + profile_picture_sizes: + type: string + profile_picture_cids: + $ref: '#/components/schemas/profile_picture' + profile_picture_legacy: + type: string + has_collectibles: + type: boolean + playlist_library: + $ref: '#/components/schemas/playlist_library' + allow_ai_attribution: + type: boolean + profile_type: + type: string + url_with_mirrors: + required: + - mirrors + type: object + properties: + url: + type: string + mirrors: + type: array + items: + type: string + notifications_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/notifications' + cid_data: + type: object + properties: + collectibles: + type: object + properties: {} + associated_sol_wallets: + type: object + properties: {} + associated_wallets: + type: object + properties: {} + account_collection: + required: + - id + - is_album + - name + - permalink + - user + type: object + properties: + id: + type: string + is_album: + type: boolean + name: + type: string + permalink: + type: string + user: + $ref: '#/components/schemas/account_collection_user' + following_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + managed_user: + required: + - grant + - user + type: object + properties: + user: + $ref: '#/components/schemas/user' + grant: + $ref: '#/components/schemas/grant' + search_model: + required: + - albums + - playlists + - tracks + - users + type: object + properties: + users: + type: array + items: + $ref: '#/components/schemas/user' + followed_users: + type: array + items: + $ref: '#/components/schemas/user' + tracks: + type: array + items: + $ref: '#/components/schemas/search_track' + saved_tracks: + type: array + items: + $ref: '#/components/schemas/search_track' + playlists: + type: array + items: + $ref: '#/components/schemas/search_playlist' + saved_playlists: + type: array + items: + $ref: '#/components/schemas/search_playlist' + albums: + type: array + items: + $ref: '#/components/schemas/search_playlist' + saved_albums: + type: array + items: + $ref: '#/components/schemas/search_playlist' + tracks: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track' + cover_photo_full: + type: object + properties: + 640x: + type: string + 2000x: + type: string + mirrors: + type: array + items: + type: string + user_comments_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/comment' + related: + $ref: '#/components/schemas/related' + grant: + required: + - created_at + - grantee_address + - is_approved + - is_revoked + - updated_at + - user_id + type: object + properties: + grantee_address: + type: string + user_id: + type: string + is_revoked: + type: boolean + is_approved: + type: boolean + created_at: + type: string + updated_at: + type: string + search_playlist_full: + required: + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: + type: string + permalink: + type: string + id: + type: string + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: + type: string + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: + type: integer + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: + type: string + access: + $ref: '#/components/schemas/access' + upc: + type: string + track_count: + type: integer + blocknumber: + type: integer + created_at: + type: string + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: + type: string + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: + type: string + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: + type: string + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + attestation: + required: + - attestation + - owner_wallet + type: object + properties: + owner_wallet: + type: string + attestation: + type: string + full_followers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + full_subscribers_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user' + transaction_details: + required: + - balance + - change + - metadata + - method + - signature + - transaction_date + - transaction_type + - user_bank + type: object + properties: + transaction_date: + type: string + transaction_type: + type: string + method: + type: string + signature: + type: string + user_bank: + type: string + change: + type: string + balance: + type: string + metadata: + type: object + properties: {} + discriminator: + propertyName: transaction_type + user_feed_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/user_feed_item' + playlists_response_full: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/playlist_without_tracks' + playlist_artwork_full: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + mirrors: + type: array + items: + type: string + field_visibility: + required: + - genre + - mood + - play_count + - remixes + - share + - tags + type: object + properties: + mood: + type: boolean + tags: + type: boolean + genre: + type: boolean + share: + type: boolean + play_count: + type: boolean + remixes: + type: boolean + playlist_update: + required: + - playlist_id + - updated_at + type: object + properties: + playlist_id: + type: string + updated_at: + type: integer + last_seen_at: + type: integer + purchase: + required: + - access + - amount + - buyer_user_id + - content_id + - content_type + - created_at + - extra_amount + - seller_user_id + - signature + - slot + - splits + - updated_at + type: object + properties: + slot: + type: integer + signature: + type: string + seller_user_id: + type: string + buyer_user_id: + type: string + amount: + type: string + extra_amount: + type: string + content_type: + type: string + content_id: + type: string + created_at: + type: string + updated_at: + type: string + access: + type: string + splits: + type: array + items: + $ref: '#/components/schemas/purchase_split' + discriminator: + propertyName: content_type + search_track_full: + required: + - access + - artwork + - blocknumber + - comment_count + - cover_art_sizes + - created_at + - download + - duration + - favorite_count + - field_visibility + - genre + - has_current_user_reposted + - has_current_user_saved + - id + - is_available + - is_delete + - is_download_gated + - is_downloadable + - is_original_available + - is_owned_by_user + - is_scheduled_release + - is_stream_gated + - is_unlisted + - permalink + - play_count + - preview + - remix_of + - repost_count + - route_id + - stream + - title + - track_segments + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/track_artwork' + description: + type: string + genre: + type: string + id: + type: string + track_cid: + type: string + preview_cid: + type: string + orig_file_cid: + type: string + orig_filename: + type: string + is_original_available: + type: boolean + mood: + type: string + release_date: + type: string + remix_of: + $ref: '#/components/schemas/remix_parent' + repost_count: + type: integer + favorite_count: + type: integer + comment_count: + type: integer + tags: + type: string + title: + type: string + user: + $ref: '#/components/schemas/user' + duration: + type: integer + is_downloadable: + type: boolean + play_count: + type: integer + permalink: + type: string + is_streamable: + type: boolean + ddex_app: + type: string + playlists_containing_track: + type: array + items: + type: integer + pinned_comment_id: + type: integer + album_backlink: + $ref: '#/components/schemas/album_backlink' + access: + type: object + description: Describes what access the given user has + allOf: + - $ref: '#/components/schemas/access' + blocknumber: + type: integer + description: The blocknumber this track was last updated + create_date: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/cover_art' + created_at: + type: string + credits_splits: + type: string + isrc: + type: string + license: + type: string + iswc: + type: string + field_visibility: + $ref: '#/components/schemas/field_visibility' + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + has_current_user_reposted: + type: boolean + is_scheduled_release: + type: boolean + is_unlisted: + type: boolean + has_current_user_saved: + type: boolean + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + route_id: + type: string + stem_of: + $ref: '#/components/schemas/stem_parent' + track_segments: + type: array + items: + $ref: '#/components/schemas/track_segment' + updated_at: + type: string + user_id: + type: string + is_delete: + type: boolean + cover_art: + type: string + is_available: + type: boolean + ai_attribution_user_id: + type: integer + allowed_api_keys: + type: array + items: + type: string + audio_upload_id: + type: string + preview_start_seconds: + type: number + bpm: + type: number + is_custom_bpm: + type: boolean + musical_key: + type: string + is_custom_musical_key: + type: boolean + audio_analysis_error_count: + type: integer + comments_disabled: + type: boolean + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + resource_contributors: + type: array + items: + type: object + properties: {} + indirect_resource_contributors: + type: array + items: + type: object + properties: {} + rights_controller: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + is_stream_gated: + type: boolean + description: Whether or not the owner has restricted streaming behind an + access gate + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_download_gated: + type: boolean + description: Whether or not the owner has restricted downloading behind + an access gate + download_conditions: + type: object + description: How to unlock the track download + allOf: + - $ref: '#/components/schemas/access_gate' + cover_original_song_title: + type: string + cover_original_artist: + type: string + is_owned_by_user: + type: boolean + description: Indicates whether the track is owned by the user for MRI sake + stream: + $ref: '#/components/schemas/url_with_mirrors' + download: + $ref: '#/components/schemas/url_with_mirrors' + preview: + $ref: '#/components/schemas/url_with_mirrors' + cover_art: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + playlist_full_without_tracks: + required: + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - followee_favorites + - followee_reposts + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id + type: object + properties: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: + type: string + permalink: + type: string + id: + type: string + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: + type: string + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: + type: integer + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: + type: string + access: + $ref: '#/components/schemas/access' + upc: + type: string + track_count: + type: integer + blocknumber: + type: integer + created_at: + type: string + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: + type: string + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: + type: string + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: + type: string + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + account_collection_user: + required: + - handle + - id + type: object + properties: + id: + type: string + handle: + type: string + is_deactivated: + type: boolean + user_feed_item: + oneOf: + - $ref: '#/components/schemas/track_feed_item' + - $ref: '#/components/schemas/playlist_feed_item' + discriminator: + propertyName: type + mapping: + track: '#/components/schemas/track_feed_item' + playlist: '#/components/schemas/playlist_feed_item' + full_remix_parent: + type: object + properties: + tracks: + type: array + items: + $ref: '#/components/schemas/remix' + purchase_gate: + required: + - usdc_purchase + type: object + properties: + usdc_purchase: + type: object + description: Must pay the total price and split to the given addresses to + unlock + allOf: + - $ref: '#/components/schemas/usdc_gate' + user_manager: + required: + - grant + - manager + type: object + properties: + manager: + $ref: '#/components/schemas/user' + grant: + $ref: '#/components/schemas/grant' + full_remix: + required: + - has_remix_author_reposted + - has_remix_author_saved + - parent_track_id + - user + type: object + properties: + parent_track_id: + type: string + user: + $ref: '#/components/schemas/user' + has_remix_author_reposted: + type: boolean + has_remix_author_saved: + type: boolean + reaction: + required: + - reacted_to + - reaction_type + - reaction_value + - sender_user_id + type: object + properties: + reaction_value: + type: string + reaction_type: + type: string + sender_user_id: + type: string + reacted_to: + type: string + activity_full: + required: + - class + - item + - item_type + - timestamp + type: object + properties: + timestamp: + type: string + item_type: + type: string + example: track + enum: + - track + - playlist + item: + type: object + properties: {} + class: + type: string + discriminator: + propertyName: class + notifications: + required: + - unread_count + type: object + properties: + notifications: type: array items: - type: string - favorites_response: + $ref: '#/components/schemas/notification' + unread_count: + type: integer + track_feed_item: + required: + - item + - type type: object properties: - data: + type: + type: string + item: + $ref: '#/components/schemas/track' + usdc_gate: + required: + - price + - splits + type: object + properties: + splits: + $ref: '#/components/schemas/wild_card_split' + price: + type: integer + track_artwork_full: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + mirrors: type: array items: - $ref: '#/components/schemas/favorite' - repost: + type: string + purchase_split: required: - - repost_item_id - - repost_type - - user_id + - amount + - payout_wallet type: object properties: - repost_item_id: + user_id: + type: integer + payout_wallet: type: string - repost_type: + amount: type: string - user_id: + track_segment: + required: + - duration + - multihash + type: object + properties: + duration: + type: number + multihash: type: string - favorite: + full_tip: required: + - amount - created_at - - favorite_item_id - - favorite_type - - user_id + - followee_supporters + - receiver + - sender + - slot + - tx_signature type: object properties: - favorite_item_id: + amount: type: string - favorite_type: + sender: + $ref: '#/components/schemas/user' + receiver: + $ref: '#/components/schemas/user' + created_at: type: string - user_id: + slot: + type: integer + followee_supporters: + type: array + items: + $ref: '#/components/schemas/supporter_reference' + tx_signature: type: string - created_at: + playlist_feed_item: + required: + - item + - type + type: object + properties: + type: type: string - playlists_response: + item: + $ref: '#/components/schemas/playlist' + wild_card_split: + type: object + additionalProperties: + type: integer + stem_parent: + required: + - category + - parent_track_id + type: object + properties: + category: + type: string + parent_track_id: + type: integer + profile_picture_full: + type: object + properties: + 150x150: + type: string + 480x480: + type: string + 1000x1000: + type: string + mirrors: + type: array + items: + type: string + notification: + oneOf: + - $ref: '#/components/schemas/follow_notification' + - $ref: '#/components/schemas/save_notification' + - $ref: '#/components/schemas/repost_notification' + - $ref: '#/components/schemas/repost_of_repost_notification' + - $ref: '#/components/schemas/save_of_repost_notification' + - $ref: '#/components/schemas/tastemaker_notification' + - $ref: '#/components/schemas/remix_notification' + - $ref: '#/components/schemas/cosign_notification' + - $ref: '#/components/schemas/create_notification' + - $ref: '#/components/schemas/send_tip_notification' + - $ref: '#/components/schemas/receive_tip_notification' + - $ref: '#/components/schemas/supporter_dethroned_notification' + - $ref: '#/components/schemas/supporter_rank_up_notification' + - $ref: '#/components/schemas/supporter_rank_up_notification' + - $ref: '#/components/schemas/challenge_reward_notification' + - $ref: '#/components/schemas/claimable_reward_notification' + - $ref: '#/components/schemas/reaction_notification' + - $ref: '#/components/schemas/milestone_notification' + - $ref: '#/components/schemas/tier_change_notification' + - $ref: '#/components/schemas/track_added_to_playlist_notification' + - $ref: '#/components/schemas/track_added_to_purchased_album_notification' + - $ref: '#/components/schemas/usdc_purchase_seller_notification' + - $ref: '#/components/schemas/usdc_purchase_buyer_notification' + - $ref: '#/components/schemas/request_manager_notification' + - $ref: '#/components/schemas/approve_manager_request_notification' + - $ref: '#/components/schemas/trending_notification' + - $ref: '#/components/schemas/trending_playlist_notification' + - $ref: '#/components/schemas/trending_underground_notification' + - $ref: '#/components/schemas/announcement_notification' + - $ref: '#/components/schemas/comment_notification' + - $ref: '#/components/schemas/comment_thread_notification' + - $ref: '#/components/schemas/comment_mention_notification' + - $ref: '#/components/schemas/comment_reaction_notification' + - $ref: '#/components/schemas/listen_streak_reminder_notification' + - $ref: '#/components/schemas/fan_remix_contest_started_notification' + - $ref: '#/components/schemas/fan_remix_contest_ended_notification' + - $ref: '#/components/schemas/fan_remix_contest_ending_soon_notification' + - $ref: '#/components/schemas/artist_remix_contest_ended_notification' + - $ref: '#/components/schemas/artist_remix_contest_ending_soon_notification' + - $ref: '#/components/schemas/artist_remix_contest_submissions_notification' + - $ref: '#/components/schemas/fan_remix_contest_winners_selected_notification' + discriminator: + propertyName: type + mapping: + follow: '#/components/schemas/follow_notification' + save: '#/components/schemas/save_notification' + repost: '#/components/schemas/repost_notification' + repost_of_repost: '#/components/schemas/repost_of_repost_notification' + save_of_repost: '#/components/schemas/save_of_repost_notification' + tastemaker: '#/components/schemas/tastemaker_notification' + remix: '#/components/schemas/remix_notification' + cosign: '#/components/schemas/cosign_notification' + create: '#/components/schemas/create_notification' + tip_send: '#/components/schemas/send_tip_notification' + tip_receive: '#/components/schemas/receive_tip_notification' + supporter_dethroned: '#/components/schemas/supporter_dethroned_notification' + supporter_rank_up: '#/components/schemas/supporter_rank_up_notification' + supporting_rank_up: '#/components/schemas/supporter_rank_up_notification' + challenge_reward: '#/components/schemas/challenge_reward_notification' + claimable_reward: '#/components/schemas/claimable_reward_notification' + reaction: '#/components/schemas/reaction_notification' + milestone: '#/components/schemas/milestone_notification' + tier_change: '#/components/schemas/tier_change_notification' + track_added_to_playlist: '#/components/schemas/track_added_to_playlist_notification' + track_added_to_purchased_album: '#/components/schemas/track_added_to_purchased_album_notification' + usdc_purchase_seller: '#/components/schemas/usdc_purchase_seller_notification' + usdc_purchase_buyer: '#/components/schemas/usdc_purchase_buyer_notification' + request_manager: '#/components/schemas/request_manager_notification' + approve_manager_request: '#/components/schemas/approve_manager_request_notification' + trending: '#/components/schemas/trending_notification' + trending_playlist: '#/components/schemas/trending_playlist_notification' + trending_underground: '#/components/schemas/trending_underground_notification' + announcement: '#/components/schemas/announcement_notification' + comment: '#/components/schemas/comment_notification' + comment_thread: '#/components/schemas/comment_thread_notification' + comment_mention: '#/components/schemas/comment_mention_notification' + comment_reaction: '#/components/schemas/comment_reaction_notification' + listen_streak_reminder: '#/components/schemas/listen_streak_reminder_notification' + fan_remix_contest_started: '#/components/schemas/fan_remix_contest_started_notification' + fan_remix_contest_ended: '#/components/schemas/fan_remix_contest_ended_notification' + fan_remix_contest_ending_soon: '#/components/schemas/fan_remix_contest_ending_soon_notification' + artist_remix_contest_ended: '#/components/schemas/artist_remix_contest_ended_notification' + artist_remix_contest_ending_soon: '#/components/schemas/artist_remix_contest_ending_soon_notification' + artist_remix_contest_submissions: '#/components/schemas/artist_remix_contest_submissions_notification' + fan_remix_contest_winners_selected: '#/components/schemas/fan_remix_contest_winners_selected_notification' + comment_reaction_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/comment_reaction_notification_action' + follow_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/follow_notification_action' + repost_of_repost_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/repost_of_repost_notification_action' + tastemaker_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/tastemaker_notification_action' + receive_tip_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/receive_tip_notification_action' + create_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/create_notification_action' + remix_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/remix_notification_action' + fan_remix_contest_started_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/fan_remix_contest_started_notification_action' + usdc_purchase_seller_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/usdc_purchase_seller_notification_action' + follow_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: + $ref: '#/components/schemas/follow_notification_action_data' + comment_thread_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/playlist' - albums_response: + $ref: '#/components/schemas/comment_thread_notification_action' + fan_remix_contest_ended_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/fan_remix_contest_ended_notification_action' + repost_of_repost_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/repost_of_repost_notification_action_data' + track_added_to_playlist_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/track_added_to_playlist_notification_action' + trending_playlist_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/trending_playlist_notification_action' + comment_reaction_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/playlist' - user_search: + $ref: '#/components/schemas/comment_reaction_notification_action_data' + tier_change_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - history_response: + $ref: '#/components/schemas/tier_change_notification_action' + challenge_reward_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/track_activity' - subscribers_response: + $ref: '#/components/schemas/challenge_reward_notification_action' + approve_manager_request_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - followers_response: + $ref: '#/components/schemas/approve_manager_request_notification_action' + save_of_repost_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - following_response: + $ref: '#/components/schemas/save_of_repost_notification_action' + trending_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - mutual_followers_response: + $ref: '#/components/schemas/trending_notification_action' + comment_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - related_artist_response: + $ref: '#/components/schemas/comment_notification_action' + repost_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - user_ids_addresses_response: + $ref: '#/components/schemas/repost_notification_action' + fan_remix_contest_ended_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: + $ref: '#/components/schemas/fan_remix_contest_ended_notification_action_data' + artist_remix_contest_ended_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user_id_address' - user_id_address: - type: object + $ref: '#/components/schemas/artist_remix_contest_ended_notification_action' + comment_reaction_notification_action_data: required: - - user_id - - address + - entity_id + - entity_user_id + - reacter_user_id + - type + type: object properties: - user_id: + type: type: string - address: + example: Track + enum: + - Track + - Playlist + - Album + entity_id: type: string - connected_wallets_response: + entity_user_id: + type: string + reacter_user_id: + type: string + comment_id: + type: string + tier_change_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/connected_wallets' - connected_wallets: + $ref: '#/components/schemas/tier_change_notification_action_data' + repost_notification_action: required: - - erc_wallets - - spl_wallets + - data + - specifier + - timestamp + - type type: object properties: - erc_wallets: - type: array - items: - type: string - spl_wallets: - type: array - items: - type: string - collectibles_response: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/repost_notification_action_data' + usdc_purchase_seller_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/collectibles' - collectibles: + $ref: '#/components/schemas/usdc_purchase_seller_notification_action_data' + artist_remix_contest_ended_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: object - properties: {} - description: Raw collectibles JSON structure generated by client - get_challenges: + $ref: '#/components/schemas/artist_remix_contest_ended_notification_action_data' + fan_remix_contest_started_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/challenge_response' - challenge_response: + $ref: '#/components/schemas/fan_remix_contest_started_notification_action_data' + announcement_notification: required: - - amount - - challenge_id - - challenge_type - - disbursed_amount - - is_active - - is_complete - - is_disbursed - - metadata - - user_id + - actions + - group_id + - is_seen + - type type: object properties: - challenge_id: + type: type: string - user_id: - type: string - specifier: + group_id: type: string - is_complete: - type: boolean - is_active: - type: boolean - is_disbursed: + is_seen: type: boolean - current_step_count: - type: integer - max_steps: + seen_at: type: integer - challenge_type: + actions: + type: array + items: + $ref: '#/components/schemas/announcement_notification_action' + comment_thread_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: type: string - amount: + type: type: string - disbursed_amount: - type: integer - cooldown_days: + timestamp: type: integer - metadata: - type: object - properties: {} - get_supporters: + data: + $ref: '#/components/schemas/comment_thread_notification_action_data' + follow_notification_action_data: + required: + - followee_user_id + - follower_user_id type: object properties: + follower_user_id: + type: string + followee_user_id: + type: string + challenge_reward_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/supporter' - supporter: + $ref: '#/components/schemas/challenge_reward_notification_action_data' + usdc_purchase_seller_notification_action_data: required: - amount - - rank - - sender + - buyer_user_id + - content_id + - content_type + - extra_amount + - seller_user_id type: object properties: - rank: - type: integer + content_type: + type: string + buyer_user_id: + type: string + seller_user_id: + type: string amount: type: string - sender: - $ref: '#/components/schemas/user' - get_supported_users: + extra_amount: + type: string + content_id: + type: string + fan_remix_contest_winners_selected_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/supporting' - supporting: + $ref: '#/components/schemas/fan_remix_contest_winners_selected_notification_action' + comment_notification_action: required: - - amount - - rank - - receiver + - data + - specifier + - timestamp + - type type: object properties: - rank: - type: integer - amount: + specifier: type: string - receiver: - $ref: '#/components/schemas/user' - verify_token: - type: object - properties: + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/decoded_user_token' - decoded_user_token: + $ref: '#/components/schemas/comment_notification_action_data' + challenge_reward_notification_action_data: required: - - apiKey - - email - - handle - - iat - - name - - sub - - userId - - verified + - amount + - challenge_id + - specifier type: object properties: - apiKey: + amount: type: string - userId: + specifier: type: string - email: + challenge_id: type: string - name: + listen_streak: + type: integer + comment_notification_action_data: + required: + - comment_user_id + - entity_id + - type + type: object + properties: + type: type: string - handle: + example: Track + enum: + - Track + - Playlist + - Album + entity_id: type: string - verified: - type: boolean - profilePicture: - $ref: '#/components/schemas/profilePicture' - sub: + comment_user_id: type: string - iat: + comment_id: type: string - profilePicture: + artist_remix_contest_ending_soon_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - "150x150": + type: type: string - "480x480": + group_id: type: string - "1000x1000": - type: string - developer_apps: + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/artist_remix_contest_ending_soon_notification_action' + claimable_reward_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/developer_app' - developer_app: + $ref: '#/components/schemas/claimable_reward_notification_action' + supporter_reference: required: - - address - - name - user_id type: object properties: - address: - type: string user_id: type: string - name: - type: string - description: + tastemaker_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: type: string - image_url: + type: type: string - authorized_apps: + timestamp: + type: integer + data: + $ref: '#/components/schemas/tastemaker_notification_action_data' + reaction_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/authorized_app' - authorized_app: + $ref: '#/components/schemas/reaction_notification_action' + trending_playlist_notification_action: required: - - address - - grant_created_at - - grant_updated_at - - grantor_user_id - - name + - data + - specifier + - timestamp + - type type: object properties: - address: - type: string - name: - type: string - description: - type: string - image_url: - type: string - grantor_user_id: - type: string - grant_created_at: + specifier: type: string - grant_updated_at: + type: type: string - sales_aggregate_response: + timestamp: + type: integer + data: + $ref: '#/components/schemas/trending_playlist_notification_action_data' + artist_remix_contest_submissions_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/sales_aggregate' - sales_aggregate: + $ref: '#/components/schemas/artist_remix_contest_submissions_notification_action' + cosign_notification: required: - - content_id - - content_type - - purchase_count + - actions + - group_id + - is_seen + - type type: object properties: - content_type: + type: type: string - content_id: + group_id: type: string - purchase_count: + is_seen: + type: boolean + seen_at: type: integer - sales_json_response: - type: object - properties: - data: - $ref: '#/components/schemas/sales_json_content' - sales_json_content: - type: object - properties: - sales: + actions: type: array items: - $ref: '#/components/schemas/sale_json' - sale_json: + $ref: '#/components/schemas/cosign_notification_action' + comment_mention_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - title: - type: string - description: Title of the content (track/album/playlist) - link: + type: type: string - description: Full URL link to the content - purchased_by: + group_id: type: string - description: Name of the buyer - buyer_user_id: + is_seen: + type: boolean + seen_at: type: integer - description: User ID of the buyer - date: + actions: + type: array + items: + $ref: '#/components/schemas/comment_mention_notification_action' + fan_remix_contest_started_notification_action_data: + required: + - entity_id + - entity_user_id + type: object + properties: + entity_user_id: type: string - description: ISO format date string of when the sale occurred - sale_price: - type: number - description: Base sale price in USDC - network_fee: - type: number - description: Network fee deducted from sale in USDC - pay_extra: - type: number - description: Extra amount paid by buyer in USDC - total: - type: number - description: Total amount received by seller in USDC - country: + entity_id: type: string - description: Country code where purchase was made - encrypted_email: + fan_remix_contest_ending_soon_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: type: string - description: Encrypted email of buyer if available - encrypted_key: + group_id: type: string - description: Encrypted key for decrypting the buyer's email - is_initial: + is_seen: type: boolean - description: Whether this is an initial encryption from the backfill - pubkey_base64: - type: string - description: Base64 encoded public key of the buyer - remixers_response: + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/fan_remix_contest_ending_soon_notification_action' + request_manager_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - purchasers_response: + $ref: '#/components/schemas/request_manager_notification_action' + send_tip_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/user' - user_tracks_remixed_response: + $ref: '#/components/schemas/send_tip_notification_action' + listen_streak_reminder_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/remixed_track_aggregate' - remixed_track_aggregate: + $ref: '#/components/schemas/listen_streak_reminder_notification_action' + save_notification: required: - - remix_count - - title - - track_id + - actions + - group_id + - is_seen + - type type: object properties: - track_id: + type: type: string - title: + group_id: type: string - remix_count: + is_seen: + type: boolean + seen_at: type: integer - email_access_response: + actions: + type: array + items: + $ref: '#/components/schemas/save_notification_action' + tier_change_notification_action_data: + required: + - current_value + - new_tier + - new_tier_value type: object properties: - data: - $ref: '#/components/schemas/email_access' - email_access: + new_tier: + type: string + current_value: + type: string + new_tier_value: + type: integer + trending_underground_notification: required: - - created_at - - email_owner_user_id - - encrypted_key - - grantor_user_id - - id - - is_initial - - receiving_user_id - - updated_at + - actions + - group_id + - is_seen + - type type: object properties: - id: - type: integer - email_owner_user_id: - type: integer - receiving_user_id: - type: integer - grantor_user_id: - type: integer - encrypted_key: + type: type: string - is_initial: + group_id: + type: string + is_seen: type: boolean - created_at: + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/trending_underground_notification_action' + create_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: type: string - updated_at: + type: type: string - user_comments_response: + timestamp: + type: integer + data: + $ref: '#/components/schemas/create_notification_action_data' + supporter_dethroned_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/comment' - comment: + $ref: '#/components/schemas/supporter_dethroned_notification_action' + repost_notification_action_data: required: - - created_at - - entity_id - - entity_type - - id - - is_edited - - message - - react_count - - reply_count + - repost_item_id + - type + - user_id type: object properties: - id: - type: string - entity_id: - type: string - entity_type: + type: type: string + example: track + enum: + - track + - playlist + - album user_id: type: string - message: + repost_item_id: type: string - mentions: - type: array - items: - $ref: '#/components/schemas/comment_mention' - track_timestamp_s: - type: integer - react_count: - type: integer - reply_count: - type: integer - is_edited: - type: boolean - is_current_user_reacted: - type: boolean - is_artist_reacted: - type: boolean - is_tombstone: - type: boolean - is_muted: - type: boolean - created_at: + milestone_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: type: string - updated_at: + group_id: type: string - replies: + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/reply_comment' - parent_comment_id: - type: integer - comment_mention: + $ref: '#/components/schemas/milestone_notification_action' + usdc_purchase_buyer_notification: required: - - handle - - user_id + - actions + - group_id + - is_seen + - type type: object properties: - user_id: - type: integer - handle: + type: type: string - reply_comment: + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: + type: array + items: + $ref: '#/components/schemas/usdc_purchase_buyer_notification_action' + fan_remix_contest_ended_notification_action_data: required: - - created_at - entity_id - - entity_type - - id - - is_edited - - message - - react_count - - user_id + - entity_user_id type: object properties: - id: + entity_user_id: type: string entity_id: type: string - entity_type: - type: string - user_id: + supporter_rank_up_notification: + required: + - actions + - group_id + - is_seen + - type + type: object + properties: + type: type: string - message: + group_id: type: string - mentions: + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/comment_mention' - track_timestamp_s: - type: integer - react_count: - type: integer - is_edited: - type: boolean - is_current_user_reacted: - type: boolean - is_artist_reacted: - type: boolean - created_at: + $ref: '#/components/schemas/supporter_rank_up_notification_action' + listen_streak_reminder_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: type: string - updated_at: + type: type: string - parent_comment_id: + timestamp: type: integer - playlist_response: - type: object - properties: data: - type: array - items: - $ref: '#/components/schemas/playlist' - playlist_tracks_response: + $ref: '#/components/schemas/listen_streak_reminder_notification_action_data' + save_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/Track' - playlist_search_result: + $ref: '#/components/schemas/save_notification_action_data' + track_added_to_purchased_album_notification: + required: + - actions + - group_id + - is_seen + - type type: object properties: - data: + type: + type: string + group_id: + type: string + is_seen: + type: boolean + seen_at: + type: integer + actions: type: array items: - $ref: '#/components/schemas/playlist' - trending_playlists_response: + $ref: '#/components/schemas/track_added_to_purchased_album_notification_action' + artist_remix_contest_ending_soon_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/playlist' - access_info_response: + $ref: '#/components/schemas/artist_remix_contest_ending_soon_notification_action_data' + receive_tip_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/track_access_info' - track_access_info: + $ref: '#/components/schemas/receive_tip_notification_action_data' + track_added_to_purchased_album_notification_action: required: - - blocknumber - - user_id + - data + - specifier + - timestamp + - type type: object properties: - access: - type: object - description: Describes what access the given user has - allOf: - - $ref: '#/components/schemas/access' - user_id: + specifier: type: string - description: The user ID of the owner of this track - blocknumber: + type: + type: string + timestamp: type: integer - description: The blocknumber this track was last updated - is_stream_gated: - type: boolean - description: Whether or not the owner has restricted streaming behind an - access gate - stream_conditions: - type: object - description: How to unlock stream access to the track - allOf: - - $ref: '#/components/schemas/extended_access_gate' - is_download_gated: - type: boolean - description: Whether or not the owner has restricted downloading behind - an access gate - download_conditions: - type: object - description: How to unlock the track download - allOf: - - $ref: '#/components/schemas/extended_access_gate' - extended_access_gate: - oneOf: - - $ref: '#/components/schemas/tip_gate' - - $ref: '#/components/schemas/follow_gate' - - $ref: '#/components/schemas/extended_purchase_gate' - - $ref: '#/components/schemas/nft_gate' - - $ref: '#/components/schemas/token_gate' - tip_gate: + data: + $ref: '#/components/schemas/track_added_to_purchased_album_notification_action_data' + fan_remix_contest_winners_selected_notification_action: required: - - tip_user_id + - data + - specifier + - timestamp + - type type: object properties: - tip_user_id: + specifier: + type: string + type: + type: string + timestamp: type: integer - description: Must tip the given user ID to unlock - follow_gate: + data: + $ref: '#/components/schemas/fan_remix_contest_winners_selected_notification_action_data' + approve_manager_request_notification_action: required: - - follow_user_id + - data + - specifier + - timestamp + - type type: object properties: - follow_user_id: + specifier: + type: string + type: + type: string + timestamp: type: integer - description: Must follow the given user ID to unlock - token_gate: + data: + $ref: '#/components/schemas/approve_manager_request_notification_action_data' + comment_thread_notification_action_data: required: - - token_gate + - comment_user_id + - entity_id + - entity_user_id + - type type: object properties: - token_gate: - type: object - description: Must hold an NFT of the given collection to unlock - allOf: - - $ref: '#/components/schemas/extended_token_gate' - extended_token_gate: + type: + type: string + example: Track + enum: + - Track + - Playlist + - Album + entity_id: + type: string + entity_user_id: + type: string + comment_user_id: + type: string + comment_id: + type: string + remix_notification_action: required: - - token_mint - - token_amount + - data + - specifier + - timestamp + - type type: object properties: - token_mint: + specifier: type: string - description: The mint of the token needed to unlock - token_amount: + type: + type: string + timestamp: type: integer - description: The amount of the token needed to unlock - extended_purchase_gate: + data: + $ref: '#/components/schemas/remix_notification_action_data' + send_tip_notification_action: required: - - usdc_purchase + - data + - specifier + - timestamp + - type type: object properties: - usdc_purchase: - type: object - description: Must pay the total price and split to the given addresses to - unlock - allOf: - - $ref: '#/components/schemas/extended_usdc_gate' - extended_usdc_gate: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/send_tip_notification_action_data' + track_added_to_playlist_notification_action: required: - - price - - splits + - data + - specifier + - timestamp + - type type: object properties: - price: + specifier: + type: string + type: + type: string + timestamp: type: integer - splits: - type: array - items: - $ref: '#/components/schemas/extended_payment_split' - extended_payment_split: + data: + $ref: '#/components/schemas/track_added_to_playlist_notification_action_data' + milestone_notification_action: required: - - amount - - payout_wallet - - percentage + - data + - specifier + - timestamp + - type type: object properties: - user_id: - type: integer - percentage: - type: number - eth_wallet: + specifier: type: string - payout_wallet: + type: type: string - amount: + timestamp: type: integer - nft_gate: + data: + $ref: '#/components/schemas/milestone_notification_action_data' + supporter_dethroned_notification_action: required: - - nft_collection + - data + - specifier + - timestamp + - type type: object properties: - nft_collection: - type: object - description: Must hold an NFT of the given collection to unlock - allOf: - - $ref: '#/components/schemas/nft_collection' - media_link: + specifier: + type: string + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/supporter_dethroned_notification_action_data' + usdc_purchase_buyer_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: - url: + specifier: type: string - mirrors: - type: array - items: - type: string - access_gate: + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/usdc_purchase_buyer_notification_action_data' + save_of_repost_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: - usdc_purchase: - $ref: '#/components/schemas/extended_purchase_gate' - follow_user_id: - type: integer - tip_user_id: + specifier: + type: string + type: + type: string + timestamp: type: integer - nft_collection: - type: object - token_gate: - $ref: '#/components/schemas/extended_token_gate' - nft_collection: + data: + $ref: '#/components/schemas/save_of_repost_notification_action_data' + repost_of_repost_notification_action_data: required: - - address - - chain - - name + - repost_of_repost_item_id + - type + - user_id type: object properties: - chain: - type: string - example: eth - enum: - - eth - - sol - standard: + type: type: string - example: ERC721 + example: track enum: - - ERC721 - - ERC1155 - address: + - track + - playlist + - album + user_id: type: string - name: + repost_of_repost_item_id: type: string - imageUrl: + remix_notification_action_data: + required: + - parent_track_id + - track_id + type: object + properties: + parent_track_id: type: string - externalLink: + track_id: type: string - track_response: + save_notification_action_data: + required: + - save_item_id + - type + - user_id type: object properties: - data: - $ref: '#/components/schemas/Track' - track_inspect: + type: + type: string + example: track + enum: + - track + - playlist + - album + user_id: + type: string + save_item_id: + type: string + announcement_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/blob_info' - blob_info: + $ref: '#/components/schemas/announcement_notification_action_data' + trending_underground_notification_action: required: - - content_type - - size + - data + - specifier + - timestamp + - type type: object properties: - size: - type: integer - content_type: + specifier: type: string - track_inspect_list: - type: object - properties: + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/blob_info' - track_comments_response: + $ref: '#/components/schemas/trending_underground_notification_action_data' + artist_remix_contest_ended_notification_action_data: + required: + - entity_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/comment' - trending_ids_response: + entity_id: + type: string + trending_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - $ref: '#/components/schemas/trending_times_ids' - trending_times_ids: - type: object - properties: - week: - type: array - items: - $ref: '#/components/schemas/track_id' - month: - type: array - items: - $ref: '#/components/schemas/track_id' - year: - type: array - items: - $ref: '#/components/schemas/track_id' - track_id: + $ref: '#/components/schemas/trending_notification_action_data' + cosign_notification_action: required: - - id + - data + - specifier + - timestamp + - type type: object properties: - id: + specifier: type: string - track_favorites_response: + type: + type: string + timestamp: + type: integer + data: + $ref: '#/components/schemas/cosign_notification_action_data' + usdc_purchase_buyer_notification_action_data: + required: + - amount + - buyer_user_id + - content_id + - content_type + - extra_amount + - seller_user_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/user' - track_reposts_response: + content_type: + type: string + buyer_user_id: + type: string + seller_user_id: + type: string + amount: + type: string + extra_amount: + type: string + content_id: + type: string + claimable_reward_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/user' - remixes_response: + $ref: '#/components/schemas/claimable_reward_notification_action_data' + supporter_rank_up_notification_action: required: - - count + - data + - specifier + - timestamp + - type type: object properties: - count: + specifier: + type: string + type: + type: string + timestamp: type: integer - tracks: - type: array - items: - $ref: '#/components/schemas/Track' - remixing_response: - type: object - properties: data: - type: array - items: - $ref: '#/components/schemas/Track' - tracks_count_response: + $ref: '#/components/schemas/supporter_rank_up_notification_action_data' + tastemaker_notification_action_data: + required: + - action + - tastemaker_item_id + - tastemaker_item_owner_id + - tastemaker_item_type + - tastemaker_user_id type: object properties: - data: - type: integer - track_comment_count_response: + tastemaker_item_owner_id: + type: string + tastemaker_item_id: + type: string + action: + type: string + tastemaker_item_type: + type: string + tastemaker_user_id: + type: string + reaction_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: - data: + specifier: + type: string + type: + type: string + timestamp: type: integer - track_comment_notification_response: - type: object - properties: data: - $ref: '#/components/schemas/comment_notification_setting' - comment_notification_setting: + $ref: '#/components/schemas/reaction_notification_action_data' + approve_manager_request_notification_action_data: required: - - is_muted + - grantee_address + - grantee_user_id + - user_id type: object properties: - is_muted: - type: boolean - stream_url_response: + user_id: + type: string + grantee_user_id: + type: string + grantee_address: + type: string + track_added_to_playlist_notification_action_data: required: - - data + - playlist_id + - playlist_owner_id + - track_id type: object properties: - data: + track_id: type: string - track_search: + playlist_id: + type: string + playlist_owner_id: + type: string + trending_playlist_notification_action_data: + required: + - genre + - playlist_id + - rank + - time_range type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/Track' - top_listener: + rank: + type: integer + genre: + type: string + playlist_id: + type: string + time_range: + type: string + example: week + enum: + - week + - month + - year + request_manager_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/top_listener' - stems_response: + $ref: '#/components/schemas/request_manager_notification_action_data' + artist_remix_contest_submissions_notification_action: + required: + - data + - specifier + - timestamp + - type type: object properties: + specifier: + type: string + type: + type: string + timestamp: + type: integer data: - type: array - items: - $ref: '#/components/schemas/stem' - stem: + $ref: '#/components/schemas/artist_remix_contest_submissions_notification_action_data' + cosign_notification_action_data: required: - - blocknumber - - category - - cid - - id - - orig_filename - - parent_id - - user_id + - parent_track_id + - track_id + - track_owner_id type: object properties: - id: + parent_track_id: type: string - parent_id: + track_id: type: string - category: + track_owner_id: type: string - cid: + fan_remix_contest_ending_soon_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: + specifier: type: string - user_id: + type: type: string - blocknumber: + timestamp: type: integer - orig_filename: - type: string - undisbursed_challenges: - type: object - properties: data: - type: array - items: - $ref: '#/components/schemas/undisbursed_challenge' - undisbursed_challenge: + $ref: '#/components/schemas/fan_remix_contest_ending_soon_notification_action_data' + claimable_reward_notification_action_data: required: - amount - challenge_id - - completed_at - - completed_blocknumber - - created_at - - handle - specifier - - user_id - - wallet type: object properties: + amount: + type: string + specifier: + type: string challenge_id: type: string - user_id: + artist_remix_contest_ending_soon_notification_action_data: + required: + - entity_id + - entity_user_id + type: object + properties: + entity_user_id: + type: string + entity_id: type: string + comment_mention_notification_action: + required: + - data + - specifier + - timestamp + - type + type: object + properties: specifier: type: string - amount: + type: type: string - completed_blocknumber: + timestamp: type: integer - handle: - type: string - wallet: + data: + $ref: '#/components/schemas/comment_mention_notification_action_data' + track_added_to_purchased_album_notification_action_data: + required: + - playlist_id + - playlist_owner_id + - track_id + type: object + properties: + track_id: type: string - created_at: + playlist_id: type: string - completed_at: + playlist_owner_id: type: string - cooldown_days: - type: integer - get_tips_response: + listen_streak_reminder_notification_action_data: + required: + - streak type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/tip' - tip: + streak: + type: integer + create_notification_action_data: + oneOf: + - $ref: '#/components/schemas/create_playlist_notification_action_data' + - $ref: '#/components/schemas/create_track_notification_action_data' + receive_tip_notification_action_data: required: - amount - - created_at + - reaction_value + - receiver_user_id + - sender_user_id + - tip_tx_signature type: object properties: amount: type: string - sender: - $ref: '#/components/schemas/user' - receiver: - $ref: '#/components/schemas/user' - created_at: + sender_user_id: type: string - developer_app_response: - type: object - properties: - data: - $ref: '#/components/schemas/developer_app' - dashboard_wallet_users_response: + receiver_user_id: + type: string + tip_tx_signature: + type: string + reaction_value: + type: integer + supporter_dethroned_notification_action_data: + required: + - dethroned_user_id + - receiver_user_id + - sender_user_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/dashboard_wallet_user' - dashboard_wallet_user: + dethroned_user_id: + type: string + sender_user_id: + type: string + receiver_user_id: + type: string + create_playlist_notification_action_data: required: - - user - - wallet + - is_album + - playlist_id type: object properties: - wallet: + is_album: + type: boolean + playlist_id: type: string - user: - $ref: '#/components/schemas/user' - comment_response: + trending_notification_action_data: + required: + - genre + - rank + - time_range + - track_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/comment' - comment_replies_response: + rank: + type: integer + genre: + type: string + track_id: + type: string + time_range: + type: string + example: week + enum: + - week + - month + - year + announcement_notification_action_data: + required: + - long_description + - push_body + - short_description + - title + - route type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/reply_comment' - unclaimed_id_response: + title: + type: string + push_body: + type: string + short_description: + type: string + long_description: + type: string + route: + type: string + send_tip_notification_action_data: + required: + - amount + - receiver_user_id + - sender_user_id + - tip_tx_signature type: object properties: - data: + amount: type: string - events_response: + sender_user_id: + type: string + receiver_user_id: + type: string + tip_tx_signature: + type: string + fan_remix_contest_ending_soon_notification_action_data: + required: + - entity_id + - entity_user_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/event' - event: + entity_user_id: + type: string + entity_id: + type: string + milestone_notification_action_data: + oneOf: + - $ref: '#/components/schemas/user_milestone_notification_action_data' + - $ref: '#/components/schemas/track_milestone_notification_action_data' + - $ref: '#/components/schemas/playlist_milestone_notification_action_data' + save_of_repost_notification_action_data: required: - - created_at - - event_data - - event_id - - event_type - - updated_at + - save_of_repost_item_id + - type - user_id type: object properties: - event_id: - type: string - event_type: - type: string - example: remix_contest - enum: - - remix_contest - - live_event - - new_release - user_id: - type: string - entity_type: + type: type: string example: track enum: - track - - collection - - user - entity_id: - type: string - end_date: - type: string - is_deleted: - type: boolean - created_at: + - playlist + - album + user_id: type: string - updated_at: + save_of_repost_item_id: type: string - event_data: - type: object - properties: {} - best_selling_response: + fan_remix_contest_winners_selected_notification_action_data: + required: + - entity_id + - entity_user_id type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/best_selling_item' - best_selling_item: + entity_user_id: + type: string + entity_id: + type: string + trending_underground_notification_action_data: required: - - content_id - - owner_id - - title + - genre + - rank + - time_range + - track_id type: object properties: - content_id: + rank: + type: integer + genre: type: string - content_type: + track_id: type: string - example: track + time_range: + type: string + example: week enum: - - track - - album - title: + - week + - month + - year + request_manager_notification_action_data: + required: + - grantee_address + - grantee_user_id + - user_id + type: object + properties: + user_id: type: string - owner_id: + grantee_user_id: type: string - coin: - type: object + grantee_address: + type: string + create_track_notification_action_data: required: - - name - - mint - - decimals - - owner_id - - has_discord - - created_at - - marketCap - - fdv - - liquidity - - lastTradeUnixTime - - lastTradeHumanTime - - price - - history24hPrice - - priceChange24hPercent - - uniqueWallet24h - - uniqueWalletHistory24h - - uniqueWallet24hChangePercent - - totalSupply - - circulatingSupply - - holder - - trade24h - - tradeHistory24h - - trade24hChangePercent - - sell24h - - sellHistory24h - - sell24hChangePercent - - buy24h - - buyHistory24h - - buy24hChangePercent - - v24h - - v24hUSD - - vHistory24h - - totalVolume - - totalVolumeUSD - - volumeBuy - - volumeBuyUSD - - volumeSell - - volumeSellUSD - - totalTrade - - buy - - sell - - dynamicBondingCurve + - track_id + type: object properties: - name: + track_id: type: string - description: The coin name - example: "Bonk Inu" - ticker: + artist_remix_contest_submissions_notification_action_data: + required: + - entity_id + - event_id + - milestone + type: object + properties: + event_id: type: string - description: The coin symbol - example: "$BONK" - mint: + milestone: + type: integer + entity_id: type: string - description: The coin mint address - example: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" - decimals: + supporter_rank_up_notification_action_data: + required: + - rank + - receiver_user_id + - sender_user_id + type: object + properties: + rank: type: integer - description: The number of decimals for the coin - example: 5 - owner_id: + sender_user_id: type: string - description: The ID of the user associated with the coin - example: "7eP5n" - escrow_recipient: + receiver_user_id: type: string - description: The escrow recipient address for custom-created coins without DBCs - example: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" - logo_uri: + reaction_notification_action_data: + required: + - reacted_to + - reaction_type + - reaction_value + - receiver_user_id + - sender_user_id + - sender_wallet + - tip_amount + type: object + properties: + reacted_to: type: string - description: URL to the coin's logo image - example: "https://arweave.net/hQiPZOsRZXGXBJd_82PhVdlM_hACsT_q6wqwf5cSY7I" - banner_image_url: + reaction_type: type: string - description: URL to the coin's banner image - example: "https://example.com/banner.png" - description: + reaction_value: + type: integer + receiver_user_id: type: string - description: A longform description about the coin - example: "The Official Bonk Inu token\nLorem ipsum dolor sit amet, consectetur adipiscing elit." - website: + sender_user_id: type: string - description: The official website for the coin - example: "https://www.bonkcoin.com/" - link_1: + sender_wallet: type: string - description: Generic link URL for the coin - example: "https://x.com/bear_token" - link_2: + tip_amount: type: string - description: Generic link URL for the coin - example: "https://instagram.com/bear_token" - link_3: + comment_mention_notification_action_data: + required: + - comment_user_id + - entity_id + - entity_user_id + - type + type: object + properties: + type: type: string - description: Generic link URL for the coin - example: "https://tiktok.com/@bear_token" - link_4: + example: Track + enum: + - Track + - Playlist + - Album + entity_id: type: string - description: Generic link URL for the coin - example: "https://bear-token.com" - has_discord: - type: boolean - description: Whether the coin has a Discord server - example: true - created_at: + entity_user_id: type: string - description: The date and time when the coin was added to Audius. - example: "2023-10-01T12:00:00Z" - address: + comment_user_id: type: string - description: The SPL token mint address - example: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" - symbol: + comment_id: type: string - description: The token symbol - example: "BONK" - marketCap: - type: number - description: Market capitalization in USD - example: 2625779824.994664 - fdv: - type: number - description: Fully diluted valuation in USD - example: 2625779824.994664 - extensions: - type: object - description: Token metadata and links - properties: - coingeckoId: - type: string - description: CoinGecko ID - example: "bonk" - description: - type: string - description: Token description - example: "The Official Bonk Inu token" - twitter: - type: string - description: Twitter URL - example: "https://twitter.com/bonk_inu" - website: - type: string - description: Website URL - example: "https://www.bonkcoin.com/" - discord: - type: string - description: Discord invite URL - example: "https://discord.gg/ubqvDDFUhf" - liquidity: - type: number - description: Current liquidity in USD - example: 18977326.389274083 - lastTradeUnixTime: - type: integer - description: Unix timestamp of the last trade - example: 1752620592 - lastTradeHumanTime: + user_milestone_notification_action_data: + required: + - threshold + - type + - user_id + type: object + properties: + type: type: string - description: ISO8601 time of the last trade - example: "2025-07-15T23:03:12" - price: - type: number - description: Current price in USD - example: 0.000029571022098881748 - history24hPrice: - type: number - description: Price 24 hours ago in USD - example: 0.000027195701160436288 - priceChange24hPercent: - type: number - description: 24h price change in percent - example: 8.73417796596848 - uniqueWallet24h: - type: integer - description: Unique wallets traded in last 24h - example: 20242 - uniqueWalletHistory24h: - type: integer - description: Unique wallets traded in previous 24h - example: 21155 - uniqueWallet24hChangePercent: - type: number - description: 24h change in unique wallets (percent) - example: -4.315764594658473 - totalSupply: - type: number - description: Total supply of the token - example: 88795707372386.03 - circulatingSupply: - type: number - description: Circulating supply of the token - example: 88795707372386.03 - holder: + threshold: type: integer - description: Number of holders - example: 957291 - trade24h: + user_id: + type: string + playlist_milestone_notification_action_data: + required: + - is_album + - playlist_id + - threshold + - type + type: object + properties: + type: + type: string + threshold: type: integer - description: Number of trades in last 24h - example: 449987 - tradeHistory24h: + playlist_id: + type: string + is_album: + type: boolean + track_milestone_notification_action_data: + required: + - threshold + - track_id + - type + type: object + properties: + type: + type: string + threshold: type: integer - description: Number of trades in previous 24h - example: 390400 - trade24hChangePercent: - type: number - description: 24h change in trade count (percent) - example: 15.263063524590164 - sell24h: + track_id: + type: string + get_supporting: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: type: integer - description: Number of sell trades in last 24h - example: 223845 - sellHistory24h: + latest_indexed_block: type: integer - description: Number of sell trades in previous 24h - example: 191979 - sell24hChangePercent: - type: number - description: 24h change in sell trades (percent) - example: 16.598690481771445 - buy24h: + latest_chain_slot_plays: type: integer - description: Number of buy trades in last 24h - example: 226142 - buyHistory24h: + latest_indexed_slot_plays: type: integer - description: Number of buy trades in previous 24h - example: 198421 - buy24hChangePercent: - type: number - description: 24h change in buy trades (percent) - example: 13.970799461750522 - v24h: - type: number - description: 24h trading volume (token units) - example: 2456470915352.043 - v24hUSD: - type: number - description: 24h trading volume in USD - example: 69961943.60091284 - vHistory24h: - type: number - description: Previous 24h trading volume (token units) - example: 1849367819551.6223 - vHistory24hUSD: - type: number - description: Previous 24h trading volume in USD - example: 49529721.91224754 - v24hChangePercent: - type: number - description: 24h change in volume (percent) - example: 32.82760137718911 - vBuy24h: - type: number - description: 24h buy volume (token units) - example: 1267704208631.2197 - vBuy24hUSD: - type: number - description: 24h buy volume in USD - example: 35985775.23314727 - vBuyHistory24h: - type: number - description: Previous 24h buy volume (token units) - example: 926415751610.5529 - vBuyHistory24hUSD: - type: number - description: Previous 24h buy volume in USD - example: 24916558.31987226 - vBuy24hChangePercent: - type: number - description: 24h change in buy volume (percent) - example: 36.83966474310746 - vSell24h: - type: number - description: 24h sell volume (token units) - example: 1188766706720.8232 - vSell24hUSD: - type: number - description: 24h sell volume in USD - example: 33976168.367765576 - vSellHistory24h: - type: number - description: Previous 24h sell volume (token units) - example: 922952067941.0695 - vSellHistory24hUSD: - type: number - description: Previous 24h sell volume in USD - example: 24613163.592375275 - vSell24hChangePercent: - type: number - description: 24h change in sell volume (percent) - example: 28.800481413161105 - numberMarkets: + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/supporting' + track_library_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: type: integer - description: Number of markets the token is traded on - example: 317 - totalVolume: - type: number - description: Total volume of coin traded (all time) - example: 158766463.26959822 - totalVolumeUSD: - type: number - description: Total volume of coin traded in USD (all time) - example: 20188521260.405678 - volumeBuy: - type: number - description: Total volume bought (all time) - example: 78227859.16098201 - volumeBuyUSD: - type: number - description: Total volume bought in USD (all time) - example: 20188521260.405678 - volumeSell: - type: number - description: Total volume sold (all time) - example: 80538604.1086162 - volumeSellUSD: - type: number - description: Total volume sold in USD (all time) - example: 20188521260.405678 - totalTrade: + latest_indexed_block: type: integer - description: Total number of trades (all time) - example: 258522892 - buy: + latest_chain_slot_plays: type: integer - description: Total number of buys (all time) - example: 87829497 - sell: + latest_indexed_slot_plays: type: integer - description: Total number of sells (all time) - example: 170693395 - dynamicBondingCurve: - type: object - description: Information about the dynamic bonding curve if one exists for the Coin - required: - - address - - price - - priceUSD - - curveProgress - - creatorQuoteFee - - totalTradingQuoteFee - - creatorWalletAddress - properties: - address: - type: string - description: Address of the bonding curve pool - example: "2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN" - price: - type: number - description: Current price in the pool's quote token (e.g., AUDIO) - example: 0.0028402095736478586927 - priceUSD: - type: number - description: Current price in USD - example: 0.000029571022098881748 - curveProgress: - type: number - description: Progress along the bonding curve (0.0 - 1.0) - example: 0.75 - isMigrated: - type: boolean - description: Whether the bonding curve has been migrated - example: false - creatorQuoteFee: - type: number - description: Creator quote fee for the bonding curve - example: 0.05 - totalTradingQuoteFee: - type: number - description: Total trading quote fee accumulated - example: 0.001 - creatorWalletAddress: - type: string - description: Address of the pool creator's wallet - example: "2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN" - artist_fees: - type: object - description: Information about the fees earned by the artist on the coin's trading. - properties: - unclaimed_fees: - type: number - description: Total unclaimed fees from all sources, in $AUDIO. - example: 2000000000 - total_fees: - type: number - description: Total fees earned from all sources, in $AUDIO. - example: 4000000000 - artist_locker: - type: object - description: Information about the artist locker associated with the coin. - properties: - address: - type: string - description: The address of the artist locker. - example: "9bC8rd4qy4EQTHjvCoKFttk5W5km4tUFcVfASCDGro84" - locked: - type: number - description: The amount currently locked in the artist locker. - example: 499178082191780800 - unlocked: - type: number - description: The amount currently unlocked in the artist locker. - example: 821917808219178 - claimable: - type: number - description: The amount claimable from the artist locker. Eg. the amount unlocked that hasn't already been claimed. - example: 821917808219178 - reward_pool: - type: object - description: Information about the reward pool associated with the coin. - properties: - address: - type: string - description: The address of the reward pool. - example: "6qUSoiMbTEZ4hpN3kuk5bjCRgcTJgUfXftVfTH1CSvZE" - balance: - type: number - description: The current balance of the reward pool. - example: 1000000000 - coin_response: + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + type: array + items: + $ref: '#/components/schemas/track_activity' + collection_library_response: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version type: object properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' data: - $ref: '#/components/schemas/coin' - coins_response: - type: object + type: array + items: + $ref: '#/components/schemas/collection_activity_without_tracks' + top_users_response: required: - - data + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' data: type: array items: - $ref: '#/components/schemas/coin' - create_coin_request: + $ref: '#/components/schemas/user' + get_supporter: + required: + - latest_chain_block + - latest_chain_slot_plays + - latest_indexed_block + - latest_indexed_slot_plays + - signature + - timestamp + - version + type: object + properties: + latest_chain_block: + type: integer + latest_indexed_block: + type: integer + latest_chain_slot_plays: + type: integer + latest_indexed_slot_plays: + type: integer + signature: + type: string + timestamp: + type: string + version: + $ref: '#/components/schemas/version_metadata' + data: + $ref: '#/components/schemas/supporter' + remix: + required: + - has_remix_author_reposted + - has_remix_author_saved + - parent_track_id + - user type: object + properties: + parent_track_id: + type: string + user: + $ref: '#/components/schemas/user' + has_remix_author_reposted: + type: boolean + has_remix_author_saved: + type: boolean + search_playlist: required: - - mint - - ticker - - decimals - - name + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id + type: object properties: - mint: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: type: string - description: The mint address of the coin - example: "bearR26zyyB3fNQm5wWv1ZfN8MPQDUMwaAuoG79b1Yj" - ticker: + permalink: type: string - description: The coin symbol/ticker - example: "BEAR" - decimals: + id: + type: string + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: + type: string + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: type: integer - description: The number of decimals for the coin (0-18) - minimum: 0 - maximum: 18 - example: 9 - name: + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: type: string - description: The coin name - example: "BEAR" - logo_uri: + access: + $ref: '#/components/schemas/access' + upc: type: string - description: The URI for the coin's logo image - example: "https://example.com/logo.png" - banner_image_url: + track_count: + type: integer + blocknumber: + type: integer + created_at: type: string - description: The URI for the coin's banner image - example: "https://example.com/banner.png" - format: uri - description: + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: type: string - description: The description of the coin - example: "A majestic bear token for wildlife conservation" - link_1: + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: type: string - description: Generic link URL for the coin - example: "https://x.com/bear_token" - format: uri - link_2: + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: type: string - description: Generic link URL for the coin - example: "https://instagram.com/bear_token" - format: uri - link_3: + cover_art_sizes: type: string - description: Generic link URL for the coin - example: "https://tiktok.com/@bear_token" - format: uri - link_4: + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: type: string - description: Generic link URL for the coin - example: "https://bear-token.com" - format: uri - create_coin_response: - type: object - properties: - data: + ddex_release_ids: type: object - required: - - mint - - ticker - - user_id - - decimals - - name - - created_at - properties: - mint: - type: string - description: The mint address of the coin - example: "bearR26zyyB3fNQm5wWv1ZfN8MPQDUMwaAuoG79b1Yj" - ticker: - type: string - description: The coin symbol/ticker - example: "BEAR" - user_id: - type: integer - description: The user ID who created the coin - example: 1 - decimals: - type: integer - description: The number of decimals for the coin - example: 9 - name: - type: string - description: The coin name - example: "BEAR" - logo_uri: - type: string - description: The URI for the coin's logo image - example: "https://example.com/logo.png" - banner_image_url: - type: string - description: The URI for the coin's banner image - example: "https://example.com/banner.png" - description: - type: string - description: The description of the coin - example: "A majestic bear token for wildlife conservation" - link_1: - type: string - description: Generic link URL for the coin - example: "https://x.com/bear_token" - link_2: - type: string - description: Generic link URL for the coin - example: "https://instagram.com/bear_token" - link_3: - type: string - description: Generic link URL for the coin - example: "https://tiktok.com/@bear_token" - link_4: - type: string - description: Generic link URL for the coin - example: "https://bear-token.com" - created_at: - type: string - format: date-time - description: The date and time when the coin was created - example: "2024-01-15T10:30:00Z" - update_coin_request: + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + search_track: + required: + - access + - artwork + - blocknumber + - comment_count + - cover_art_sizes + - created_at + - download + - duration + - favorite_count + - field_visibility + - genre + - has_current_user_reposted + - has_current_user_saved + - id + - is_available + - is_delete + - is_download_gated + - is_downloadable + - is_original_available + - is_owned_by_user + - is_scheduled_release + - is_stream_gated + - is_unlisted + - permalink + - play_count + - preview + - remix_of + - repost_count + - route_id + - stream + - title + - track_segments + - updated_at + - user + - user_id type: object - description: Request body for updating coin information properties: + artwork: + $ref: '#/components/schemas/track_artwork' description: type: string - description: The description of the coin (max 2500 characters) - example: "Updated description for the bear token" - maxLength: 2500 - banner_image_url: - type: string - description: URL for the coin's banner image - example: "https://example.com/banner.png" - format: uri - link_1: + genre: type: string - description: Generic link URL for the coin - example: "https://x.com/bear_token" - format: uri - link_2: + id: type: string - description: Generic link URL for the coin - example: "https://instagram.com/bear_token" - format: uri - link_3: + track_cid: type: string - description: Generic link URL for the coin - example: "https://tiktok.com/@bear_token" - format: uri - link_4: + preview_cid: type: string - description: Generic link URL for the coin - example: "https://bear-token.com" - format: uri - update_coin_response: - type: object - properties: - success: - type: boolean - description: Indicates if the update was successful - example: true - coin_insights: - type: object - description: | - Additional token information from Birdeye's defi token overview API. - Includes price, volume, supply, market cap, and other on-chain and market data. - required: - - mint - - marketCap - - fdv - - liquidity - - lastTradeUnixTime - - lastTradeHumanTime - - price - - history24hPrice - - priceChange24hPercent - - uniqueWallet24h - - uniqueWalletHistory24h - - uniqueWallet24hChangePercent - - totalSupply - - circulatingSupply - - holder - - trade24h - - tradeHistory24h - - trade24hChangePercent - - sell24h - - sellHistory24h - - sell24hChangePercent - - buy24h - - buyHistory24h - - buy24hChangePercent - - v24h - - v24hUSD - - vHistory24h - - totalVolume - - totalVolumeUSD - - volumeBuy - - volumeBuyUSD - - volumeSell - - volumeSellUSD - - totalTrade - - buy - - sell - - dynamicBondingCurve - properties: - address: + orig_file_cid: type: string - description: The SPL token mint address - example: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" - decimals: - type: integer - description: Number of decimals for the token - example: 5 - symbol: + orig_filename: type: string - description: The token symbol - example: "BONK" - name: + is_original_available: + type: boolean + mood: type: string - description: The token name - example: "Bonk" - marketCap: - type: number - description: Market capitalization in USD - example: 2625779824.994664 - fdv: - type: number - description: Fully diluted valuation in USD - example: 2625779824.994664 - extensions: - type: object - description: Token metadata and links - properties: - coingeckoId: - type: string - description: CoinGecko ID - example: "bonk" - description: - type: string - description: Token description - example: "The Official Bonk Inu token" - twitter: - type: string - description: Twitter URL - example: "https://twitter.com/bonk_inu" - website: - type: string - description: Website URL - example: "https://www.bonkcoin.com/" - discord: - type: string - description: Discord invite URL - example: "https://discord.gg/ubqvDDFUhf" - liquidity: - type: number - description: Current liquidity in USD - example: 18977326.389274083 - lastTradeUnixTime: - type: integer - description: Unix timestamp of the last trade - example: 1752620592 - lastTradeHumanTime: + release_date: type: string - description: ISO8601 time of the last trade - example: "2025-07-15T23:03:12" - price: - type: number - description: Current price in USD - example: 0.000029571022098881748 - history24hPrice: - type: number - description: Price 24 hours ago in USD - example: 0.000027195701160436288 - priceChange24hPercent: - type: number - description: 24h price change in percent - example: 8.73417796596848 - uniqueWallet24h: - type: integer - description: Unique wallets traded in last 24h - example: 20242 - uniqueWalletHistory24h: - type: integer - description: Unique wallets traded in previous 24h - example: 21155 - uniqueWallet24hChangePercent: - type: number - description: 24h change in unique wallets (percent) - example: -4.315764594658473 - totalSupply: - type: number - description: Total supply of the token - example: 88795707372386.03 - circulatingSupply: - type: number - description: Circulating supply of the token - example: 88795707372386.03 - holder: + remix_of: + $ref: '#/components/schemas/remix_parent' + repost_count: type: integer - description: Number of holders - example: 957291 - trade24h: + favorite_count: type: integer - description: Number of trades in last 24h - example: 449987 - tradeHistory24h: + comment_count: type: integer - description: Number of trades in previous 24h - example: 390400 - trade24hChangePercent: - type: number - description: 24h change in trade count (percent) - example: 15.263063524590164 - sell24h: + tags: + type: string + title: + type: string + user: + $ref: '#/components/schemas/user' + duration: type: integer - description: Number of sell trades in last 24h - example: 223845 - sellHistory24h: + is_downloadable: + type: boolean + play_count: type: integer - description: Number of sell trades in previous 24h - example: 191979 - sell24hChangePercent: - type: number - description: 24h change in sell trades (percent) - example: 16.598690481771445 - buy24h: + permalink: + type: string + is_streamable: + type: boolean + ddex_app: + type: string + playlists_containing_track: + type: array + items: + type: integer + pinned_comment_id: type: integer - description: Number of buy trades in last 24h - example: 226142 - buyHistory24h: + album_backlink: + $ref: '#/components/schemas/album_backlink' + access: + type: object + description: Describes what access the given user has + allOf: + - $ref: '#/components/schemas/access' + blocknumber: type: integer - description: Number of buy trades in previous 24h - example: 198421 - buy24hChangePercent: - type: number - description: 24h change in buy trades (percent) - example: 13.970799461750522 - v24h: - type: number - description: 24h trading volume (token units) - example: 2456470915352.043 - v24hUSD: - type: number - description: 24h trading volume in USD - example: 69961943.60091284 - vHistory24h: - type: number - description: Previous 24h trading volume (token units) - example: 1849367819551.6223 - vHistory24hUSD: - type: number - description: Previous 24h trading volume in USD - example: 49529721.91224754 - v24hChangePercent: - type: number - description: 24h change in volume (percent) - example: 32.82760137718911 - vBuy24h: - type: number - description: 24h buy volume (token units) - example: 1267704208631.2197 - vBuy24hUSD: - type: number - description: 24h buy volume in USD - example: 35985775.23314727 - vBuyHistory24h: - type: number - description: Previous 24h buy volume (token units) - example: 926415751610.5529 - vBuyHistory24hUSD: - type: number - description: Previous 24h buy volume in USD - example: 24916558.31987226 - vBuy24hChangePercent: - type: number - description: 24h change in buy volume (percent) - example: 36.83966474310746 - vSell24h: - type: number - description: 24h sell volume (token units) - example: 1188766706720.8232 - vSell24hUSD: - type: number - description: 24h sell volume in USD - example: 33976168.367765576 - vSellHistory24h: - type: number - description: Previous 24h sell volume (token units) - example: 922952067941.0695 - vSellHistory24hUSD: - type: number - description: Previous 24h sell volume in USD - example: 24613163.592375275 - vSell24hChangePercent: - type: number - description: 24h change in sell volume (percent) - example: 28.800481413161105 - numberMarkets: + description: The blocknumber this track was last updated + create_date: + type: string + cover_art_sizes: + type: string + cover_art_cids: + $ref: '#/components/schemas/cover_art' + created_at: + type: string + credits_splits: + type: string + isrc: + type: string + license: + type: string + iswc: + type: string + field_visibility: + $ref: '#/components/schemas/field_visibility' + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + has_current_user_reposted: + type: boolean + is_scheduled_release: + type: boolean + is_unlisted: + type: boolean + has_current_user_saved: + type: boolean + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + route_id: + type: string + stem_of: + $ref: '#/components/schemas/stem_parent' + track_segments: + type: array + items: + $ref: '#/components/schemas/track_segment' + updated_at: + type: string + user_id: + type: string + is_delete: + type: boolean + cover_art: + type: string + is_available: + type: boolean + ai_attribution_user_id: type: integer - description: Number of markets the token is traded on - example: 317 - totalVolume: - type: number - description: Total volume of coin traded (all time) - example: 158766463.26959822 - totalVolumeUSD: - type: number - description: Total volume of coin traded in USD (all time) - example: 20188521260.405678 - volumeBuy: - type: number - description: Total volume bought (all time) - example: 78227859.16098201 - volumeBuyUSD: - type: number - description: Total volume bought in USD (all time) - example: 20188521260.405678 - volumeSell: + allowed_api_keys: + type: array + items: + type: string + audio_upload_id: + type: string + preview_start_seconds: type: number - description: Total volume sold (all time) - example: 80538604.1086162 - volumeSellUSD: + bpm: type: number - description: Total volume sold in USD (all time) - example: 20188521260.405678 - totalTrade: - type: integer - description: Total number of trades (all time) - example: 258522892 - buy: - type: integer - description: Total number of buys (all time) - example: 87829497 - sell: + is_custom_bpm: + type: boolean + musical_key: + type: string + is_custom_musical_key: + type: boolean + audio_analysis_error_count: type: integer - description: Total number of sells (all time) - example: 170693395 - dynamicBondingCurve: + comments_disabled: + type: boolean + ddex_release_ids: type: object - description: Information about the dynamic bonding curve if one exists for the Coin - required: - - address - - price - - priceUSD - - curveProgress - - creatorQuoteFee - - totalTradingQuoteFee - - creatorWalletAddress - properties: - address: - type: string - description: Address of the bonding curve pool - example: "2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN" - price: - type: number - description: Current price in the pool's quote token (e.g., AUDIO) - example: 0.0028402095736478586927 - priceUSD: - type: number - description: Current price in USD - example: 0.000029571022098881748 - curveProgress: - type: number - description: Progress along the bonding curve (0.0 - 1.0) - example: 0.75 - isMigrated: - type: boolean - description: Whether the bonding curve has been migrated - example: false - creatorQuoteFee: - type: number - description: Creator quote fee for the bonding curve - example: 0.05 - totalTradingQuoteFee: - type: number - description: Total trading quote fee accumulated - example: 0.001 - creatorWalletAddress: - type: string - description: Address of the pool creator's wallet - example: "2AAsAwNPTNBk5N466xyPiwqdgbc5WLbDTdnn9gVuDKaN" - coin_insights_response: - type: object - properties: - data: - $ref: '#/components/schemas/coin_insights' - coin_member: - type: object - required: - - balance - - user_id - properties: - balance: - type: integer - description: The user's balance of the specific coin (in wei) - example: 100000 - user_id: - type: string - description: The ID of the user with a non-zero balance - example: "7eP5n" - coin_members_response: - type: object - required: - - data - properties: - data: + properties: {} + artists: type: array items: - $ref: '#/components/schemas/coin_member' - coin_members_count_response: - type: object - required: - - data - properties: - data: - type: integer - description: The total number of users with a non-zero balance of the specific coin - example: 42 - coins_volume_leaders_response: - type: object - required: - - data - properties: - data: + type: object + properties: {} + resource_contributors: type: array items: type: object - required: - - address - - volume - properties: - address: - type: string - volume: - type: number - user: - $ref: '#/components/schemas/user' - user_coin: + properties: {} + indirect_resource_contributors: + type: array + items: + type: object + properties: {} + rights_controller: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: + type: string + is_stream_gated: + type: boolean + description: Whether or not the owner has restricted streaming behind an + access gate + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_download_gated: + type: boolean + description: Whether or not the owner has restricted downloading behind + an access gate + download_conditions: + type: object + description: How to unlock the track download + allOf: + - $ref: '#/components/schemas/access_gate' + cover_original_song_title: + type: string + cover_original_artist: + type: string + is_owned_by_user: + type: boolean + description: Indicates whether the track is owned by the user for MRI sake + stream: + $ref: '#/components/schemas/url_with_mirrors' + download: + $ref: '#/components/schemas/url_with_mirrors' + preview: + $ref: '#/components/schemas/url_with_mirrors' + track: required: - - mint - - ticker - - decimals - - owner_id - - balance - - has_discord - - balance_usd + - access + - artwork + - blocknumber + - comment_count + - cover_art_sizes + - created_at + - download + - duration + - favorite_count + - field_visibility + - followee_favorites + - followee_reposts + - genre + - has_current_user_reposted + - has_current_user_saved + - id + - is_available + - is_delete + - is_download_gated + - is_downloadable + - is_original_available + - is_owned_by_user + - is_scheduled_release + - is_stream_gated + - is_unlisted + - permalink + - play_count + - preview + - remix_of + - repost_count + - route_id + - stream + - title + - track_segments + - updated_at + - user + - user_id type: object properties: - mint: + artwork: + $ref: '#/components/schemas/track_artwork' + description: type: string - description: The coin mint address - example: "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM" - ticker: + genre: + type: string + id: + type: string + track_cid: + type: string + preview_cid: + type: string + orig_file_cid: + type: string + orig_filename: + type: string + is_original_available: + type: boolean + mood: + type: string + release_date: + type: string + remix_of: + $ref: '#/components/schemas/remix_parent' + repost_count: + type: integer + favorite_count: + type: integer + comment_count: + type: integer + tags: + type: string + title: + type: string + user: + $ref: '#/components/schemas/user' + duration: + type: integer + is_downloadable: + type: boolean + play_count: + type: integer + permalink: + type: string + is_streamable: + type: boolean + ddex_app: type: string - description: The coin symbol - example: "$AUDIO" - decimals: + playlists_containing_track: + type: array + items: + type: integer + pinned_comment_id: type: integer - description: The number of decimals for the coin - example: 8 - owner_id: + album_backlink: + $ref: '#/components/schemas/album_backlink' + access: + type: object + description: Describes what access the given user has + allOf: + - $ref: '#/components/schemas/access' + blocknumber: + type: integer + description: The blocknumber this track was last updated + create_date: type: string - description: The ID of the user associated with the coin - example: "7eP5n" - logo_uri: + cover_art_sizes: type: string - nullable: true - description: URL to the coin's logo image - example: "https://example.com/logo.png" - banner_image_url: + cover_art_cids: + $ref: '#/components/schemas/cover_art' + created_at: type: string - nullable: true - description: URL to the coin's banner image - example: "https://example.com/banner.png" - has_discord: + credits_splits: + type: string + isrc: + type: string + license: + type: string + iswc: + type: string + field_visibility: + $ref: '#/components/schemas/field_visibility' + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + has_current_user_reposted: type: boolean - description: Whether the coin has a Discord server - example: true - balance: - type: integer - description: The balance of the coin in the user's account (in wei) - example: 1000000000 - balance_usd: - type: number - description: The balance of the coin in the user's account in USD - example: 1.23 - user_coins_response: - type: object - required: - - data - properties: - data: + is_scheduled_release: + type: boolean + is_unlisted: + type: boolean + has_current_user_saved: + type: boolean + followee_favorites: type: array items: - $ref: '#/components/schemas/user_coin' - user_coin_with_accounts: - type: object - required: - - mint - - ticker - - decimals - - balance - - balance_usd - - accounts - properties: - mint: + $ref: '#/components/schemas/favorite' + route_id: type: string - description: The coin mint address - example: "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM" - ticker: + stem_of: + $ref: '#/components/schemas/stem_parent' + track_segments: + type: array + items: + $ref: '#/components/schemas/track_segment' + updated_at: type: string - description: The coin symbol - example: "$AUDIO" - decimals: - type: integer - description: The number of decimals for the coin - example: 8 - logo_uri: + user_id: type: string - nullable: true - description: URL to the coin's logo image - example: "https://example.com/logo.png" - balance: + is_delete: + type: boolean + cover_art: + type: string + is_available: + type: boolean + ai_attribution_user_id: type: integer - description: The total balance of the coin in the user's account (in wei) - example: 1000000000 - balance_usd: - type: number - description: The total balance of the coin in the user's account in USD - example: 1.23 - accounts: + allowed_api_keys: type: array items: - $ref: '#/components/schemas/user_coin_account' - user_coin_account: - type: object - required: - - account - - owner - - balance - - balance_usd - - is_in_app_wallet - properties: - account: + type: string + audio_upload_id: type: string - description: The token account address - example: "CTyFguG69kwYrzk24P3UuBvY1rR5atu9kf2S6XEwAU8X" - owner: + preview_start_seconds: + type: number + bpm: + type: number + is_custom_bpm: + type: boolean + musical_key: type: string - description: The owner wallet of the token account - example: "HzZ3EKACbH6XEHs59Rt1adVzUKv5cTDE9o9YWFaMhwpF" - balance: + is_custom_musical_key: + type: boolean + audio_analysis_error_count: type: integer - description: The balance of the coin in the user's account (in wei) - example: 1000000000 - balance_usd: - type: number - description: The balance of the coin in the user's account in USD - example: 1.23 - is_in_app_wallet: + comments_disabled: type: boolean - description: Whether the account is in the user's in-app wallet - example: true - user_coin_response: - type: object - properties: - data: - $ref: '#/components/schemas/user_coin_with_accounts' - balance_history_response: - type: object - required: - - data - properties: - data: + ddex_release_ids: + type: object + properties: {} + artists: type: array items: - $ref: '#/components/schemas/balance_history_data_point' - balance_history_data_point: - type: object - required: - - timestamp - - balance_usd - properties: - timestamp: - type: integer - format: int64 - description: Unix timestamp in seconds - example: 1704067200 - balance_usd: - type: number - format: double - description: Total portfolio balance in USD at this timestamp - example: 1234.56 - claim_rewards_response: - type: object - required: - - data - properties: - data: + type: object + properties: {} + resource_contributors: type: array items: type: object - required: - - challengeId - - specifier - properties: - challengeId: - type: string - description: The challenge ID - example: "u" - specifier: - type: string - description: The challenge specifier - example: "7eP5n" - amount: - type: string - description: The reward amount - example: "1000000000" - signatures: - type: array - items: - type: string - description: Transaction signatures - example: ["5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA"] - error: - type: string - description: Error message if claim failed - example: "Insufficient balance" - claim_rewards_request: - type: object - required: - - userId - properties: - challengeId: - type: string - description: The challenge ID to filter rewards (optional) - example: "u" - specifier: - type: string - description: The specifier to filter rewards (optional) - example: "7eP5n" - userId: + properties: {} + indirect_resource_contributors: + type: array + items: + type: object + properties: {} + rights_controller: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: type: string - description: The user ID to claim rewards for - example: "7eP5n" - redeem_amount_response: - type: object - required: - - amount - properties: - amount: - type: integer - description: Static amount indicator (always 1) - example: 1 - reward_code_response: - type: object - required: - - code - - amount - properties: - code: + is_stream_gated: + type: boolean + description: Whether or not the owner has restricted streaming behind an + access gate + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_download_gated: + type: boolean + description: Whether or not the owner has restricted downloading behind + an access gate + download_conditions: + type: object + description: How to unlock the track download + allOf: + - $ref: '#/components/schemas/access_gate' + cover_original_song_title: type: string - description: The reward code - example: "XYZ123" - amount: - type: integer - description: The amount of coins rewarded by this code - example: 100 - reward_code_error_response: - type: object - properties: - error: + cover_original_artist: type: string - description: Error message indicating why the code cannot be redeemed - example: "used" - enum: - - used - - invalid - create_reward_code_request: - type: object + is_owned_by_user: + type: boolean + description: Indicates whether the track is owned by the user for MRI sake + stream: + $ref: '#/components/schemas/url_with_mirrors' + download: + $ref: '#/components/schemas/url_with_mirrors' + preview: + $ref: '#/components/schemas/url_with_mirrors' + collection_activity_without_tracks: + allOf: + - $ref: '#/components/schemas/activity' + - required: + - item + - item_type + type: object + properties: + item_type: + type: string + example: playlist + enum: + - playlist + item: + $ref: '#/components/schemas/playlist_without_tracks' + account: required: - - signature - - mint - - amount - properties: - signature: - type: string - description: Base64-encoded Solana Ed25519 signature of the string "code" - example: "3fG7xQh2L8vK9pN4mR5sT6uW7vX8yZ1aB2cD3eF4gH5iJ6kL7mN8oP9qR0sT1uV2wX3yZ4aB5cD6eF7gH8iJ9k=" - mint: - type: string - description: The coin mint address - example: "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM" - amount: - type: integer - description: The reward amount (must be greater than 0) - minimum: 1 - example: 100 - create_reward_code_response: + - playlists + - track_save_count + - user type: object - required: - - code - - mint - - reward_address - - amount properties: - code: - type: string - description: The generated 6-character alphanumeric reward code - example: "aB3d5F" - mint: - type: string - description: The coin mint address - example: "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM" - reward_address: - type: string - description: The reward address (authorized public key) - example: "9XeZbswbSSUU4AHVArQbTQjAEjAPhVweGU5cogBVkvh4" - amount: + user: + $ref: '#/components/schemas/user' + playlists: + type: array + items: + $ref: '#/components/schemas/account_collection' + playlist_library: + $ref: '#/components/schemas/playlist_library' + track_save_count: type: integer - description: The reward amount - example: 100 - prize_claim_request: - type: object + playlist_without_tracks: required: - - signature - - wallet - properties: - signature: - type: string - description: The Solana transaction signature for the 2 YAK payment - example: "5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA" - wallet: - type: string - description: The wallet address that sent the transaction - example: "HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC" - prize_claim_response: + - access + - added_timestamps + - blocknumber + - created_at + - favorite_count + - followee_favorites + - followee_reposts + - has_current_user_reposted + - has_current_user_saved + - id + - is_album + - is_delete + - is_image_autogenerated + - is_private + - is_scheduled_release + - is_stream_gated + - permalink + - playlist_contents + - playlist_name + - repost_count + - total_play_count + - track_count + - updated_at + - user + - user_id type: object - required: - - prize_id - - prize_name - - wallet properties: - prize_id: - type: string - description: The unique identifier of the prize won - example: "prize_1_yak_airdrop" - prize_name: + artwork: + $ref: '#/components/schemas/playlist_artwork' + description: type: string - description: The name of the prize won - example: "1 YAK Airdrop" - wallet: + permalink: type: string - description: The wallet address that claimed the prize - example: "HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC" - prize_type: + id: type: string - description: The type of prize (e.g., "coin_airdrop", "download") - example: "coin_airdrop" - action_data: - type: object - description: Prize-specific action data (e.g., redeem code/URL for coin airdrops, download URL for downloads) - additionalProperties: true - example: - code: "aB3d5F" - url: "/coins/YAK/redeem/aB3d5F" - prize_public: - type: object - required: - - prize_id - - name - - weight - properties: - prize_id: + is_album: + type: boolean + is_image_autogenerated: + type: boolean + playlist_name: type: string - description: The unique identifier of the prize - example: "prize_1_yak_airdrop" - name: + playlist_contents: + type: array + items: + $ref: '#/components/schemas/playlist_added_timestamp' + repost_count: + type: integer + favorite_count: + type: integer + total_play_count: + type: integer + user: + $ref: '#/components/schemas/user' + ddex_app: type: string - description: The name of the prize - example: "1 YAK Airdrop" - description: + access: + $ref: '#/components/schemas/access' + upc: type: string - description: Description of the prize - example: "Win 1 YAK coin airdrop" - weight: + track_count: type: integer - description: Weight for random selection (higher = more likely) - example: 1 - metadata: - type: object - description: Sanitized metadata (excludes sensitive URLs) - additionalProperties: true - example: - type: "coin_airdrop" - amount: 1000000000 - prizes_response: - type: object - required: - - data - properties: - data: - type: array - items: - $ref: '#/components/schemas/prize_public' - description: List of active prizes available for claiming - claimed_prize: - type: object - required: - - id - - wallet - - signature - - mint - - amount - - prize_id - - prize_name - - created_at - properties: - id: + blocknumber: type: integer - description: The unique identifier of the claimed prize record - example: 1 - wallet: + created_at: type: string - description: The wallet address that claimed the prize - example: "HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC" - signature: + followee_reposts: + type: array + items: + $ref: '#/components/schemas/repost' + followee_favorites: + type: array + items: + $ref: '#/components/schemas/favorite' + has_current_user_reposted: + type: boolean + has_current_user_saved: + type: boolean + is_delete: + type: boolean + is_private: + type: boolean + updated_at: type: string - description: The transaction signature used to claim the prize - example: "5j7s1QjmRKFuDbCWMRVRNibSV2VAAEcNKP6HWU7GwPdXkBZvhz8n4vQl7bBq8tN4Rz9x1Kj3mP5wQ8rT2Y6zA" - mint: + added_timestamps: + type: array + description: DEPRECATED. Use playlist_contents instead. + items: + $ref: '#/components/schemas/playlist_added_timestamp' + user_id: type: string - description: The coin mint address used for the claim - example: "ZDaUDL4XFdEct7UgeztrFQAptsvh4ZdhyZDZ1RpxYAK" - amount: - type: integer - description: The amount paid to claim the prize (in smallest unit, e.g., lamports) - example: 2000000000 - prize_id: + tracks: + type: array + items: + $ref: '#/components/schemas/track' + cover_art: type: string - description: The unique identifier of the prize won - example: "prize_1_yak_airdrop" - prize_name: + cover_art_sizes: type: string - description: The name of the prize won - example: "1 YAK Airdrop" - prize_type: + cover_art_cids: + $ref: '#/components/schemas/playlist_artwork' + is_stream_gated: + type: boolean + stream_conditions: + type: object + description: How to unlock stream access to the track + allOf: + - $ref: '#/components/schemas/access_gate' + is_scheduled_release: + type: boolean + release_date: type: string - description: The type of prize (e.g., "coin_airdrop", "download") - example: "coin_airdrop" - created_at: + ddex_release_ids: + type: object + properties: {} + artists: + type: array + items: + type: object + properties: {} + copyright_line: + type: object + properties: {} + producer_copyright_line: + type: object + properties: {} + parental_warning_type: type: string - format: date-time - description: When the prize was claimed - example: "2024-01-15T10:30:00Z" - claimed_prizes_response: + user_response_single: type: object - required: - - data properties: data: - type: array - items: - $ref: '#/components/schemas/claimed_prize' - description: List of claimed prizes for the wallet (action_data excluded for security) + $ref: '#/components/schemas/user' responses: ParseError: description: When a mask can't be parsed @@ -8159,4 +16723,4 @@ components: MaskError: description: When any error occurs on mask content: {} -x-original-swagger-version: "2.0" +x-original-swagger-version: '2.0' diff --git a/api/v1_explore_best_selling_test.go b/api/v1_explore_best_selling_test.go index 7baa634b..bd09e3c2 100644 --- a/api/v1_explore_best_selling_test.go +++ b/api/v1_explore_best_selling_test.go @@ -289,7 +289,7 @@ func TestExploreBestSelling(t *testing.T) { } } - status, body := testGet(t, app, "/v1/full/explore/best-selling", &BestSellingResponse) + status, body := testGet(t, app, "/v1/explore/best-selling", &BestSellingResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ diff --git a/api/v1_playlist_test.go b/api/v1_playlist_test.go index 223b0baa..15f335b1 100644 --- a/api/v1_playlist_test.go +++ b/api/v1_playlist_test.go @@ -14,7 +14,7 @@ func TestGetPlaylist(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/playlists/7eP5n", &playlistResponse) + status, body := testGet(t, app, "/v1/playlists/7eP5n", &playlistResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -30,7 +30,7 @@ func TestGetPlaylistFollowDownloadAccess(t *testing.T) { Data []dbv1.Playlist } // No access - _, body1 := testGet(t, app, "/v1/full/playlists/ML51L", &playlistResponse) + _, body1 := testGet(t, app, "/v1/playlists/ML51L", &playlistResponse) jsonAssert(t, body1, map[string]any{ "data.0.playlist_name": "Follow Gated Stream", "data.0.access": `{"stream":false,"download":false}`, @@ -39,7 +39,7 @@ func TestGetPlaylistFollowDownloadAccess(t *testing.T) { // With access _, body2 := testGetWithWallet( t, app, - "/v1/full/playlists/ML51L?user_id=ELKzn", + "/v1/playlists/ML51L?user_id=ELKzn", "0x4954d18926ba0ed9378938444731be4e622537b2", &playlistResponse, ) @@ -55,7 +55,7 @@ func TestGetPlaylistUsdcPurchaseStreamAccess(t *testing.T) { Data []dbv1.Playlist } // No access - _, body1 := testGet(t, app, "/v1/full/playlists/ELKzn", &playlistResponse) + _, body1 := testGet(t, app, "/v1/playlists/ELKzn", &playlistResponse) jsonAssert(t, body1, map[string]any{ "data.0.playlist_name": "Purchase Gated Stream", "data.0.access": `{"stream":false,"download":false}`, @@ -64,7 +64,7 @@ func TestGetPlaylistUsdcPurchaseStreamAccess(t *testing.T) { // With access _, body2 := testGetWithWallet( t, app, - "/v1/full/playlists/ELKzn?user_id=1D9On", + "/v1/playlists/ELKzn?user_id=1D9On", "0x855d28d495ec1b06364bb7a521212753e2190b95", &playlistResponse, ) @@ -82,7 +82,7 @@ func TestGetPlaylistUsdcPurchaseSelfAccess(t *testing.T) { // No access. User 3 is the owner, but has not signed authorization status, _ := testGet( t, app, - "/v1/full/playlists/ELKzn?user_id="+trashid.MustEncodeHashID(3), + "/v1/playlists/ELKzn?user_id="+trashid.MustEncodeHashID(3), &playlistResponse, ) assert.Equal(t, 403, status) @@ -90,7 +90,7 @@ func TestGetPlaylistUsdcPurchaseSelfAccess(t *testing.T) { // With access. User 3 is the owner, and has signed authorization _, body2 := testGetWithWallet( t, app, - "/v1/full/playlists/ELKzn?user_id="+trashid.MustEncodeHashID(3), + "/v1/playlists/ELKzn?user_id="+trashid.MustEncodeHashID(3), "0xc3d1d41e6872ffbd15c473d14fc3a9250be5b5e0", &playlistResponse, ) diff --git a/api/v1_playlists_test.go b/api/v1_playlists_test.go index da2ec1b4..75f518fe 100644 --- a/api/v1_playlists_test.go +++ b/api/v1_playlists_test.go @@ -9,7 +9,7 @@ import ( func TestPlaylistsEndpoint(t *testing.T) { app := testAppWithFixtures(t) - status, body := testGet(t, app, "/v1/full/playlists?id=7eP5n") + status, body := testGet(t, app, "/v1/playlists?id=7eP5n") assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -21,7 +21,7 @@ func TestPlaylistsEndpoint(t *testing.T) { func TestPlaylistsEndpointWithTracks(t *testing.T) { app := testAppWithFixtures(t) - status, body := testGet(t, app, "/v1/full/playlists?id=7eP5n&with_tracks=true") + status, body := testGet(t, app, "/v1/playlists?id=7eP5n&with_tracks=true") assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -37,7 +37,7 @@ func TestPlaylistsEndpointWithPlaylistPermalink(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/playlists?permalink=/PlaylistsByPermalink/playlist/playlist-by-permalink", &resp) + status, body := testGet(t, app, "/v1/playlists?permalink=/PlaylistsByPermalink/playlist/playlist-by-permalink", &resp) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -52,7 +52,7 @@ func TestPlaylistsEndpointWithAlbumPermalink(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/playlists?permalink=/AlbumsByPermalink/album/album-by-permalink", &resp) + status, body := testGet(t, app, "/v1/playlists?permalink=/AlbumsByPermalink/album/album-by-permalink", &resp) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ diff --git a/api/v1_resolve.go b/api/v1_resolve.go index 6db81bb2..1fde1c76 100644 --- a/api/v1_resolve.go +++ b/api/v1_resolve.go @@ -51,7 +51,7 @@ func (app *ApiServer) v1Resolve(c *fiber.Ctx) error { } if isFull { - return c.Redirect("/v1/full/tracks/"+trackId, fiber.StatusFound) + return c.Redirect("/v1/tracks/"+trackId, fiber.StatusFound) } return c.Redirect("/v1/tracks/"+trackId, fiber.StatusFound) } @@ -76,7 +76,7 @@ func (app *ApiServer) v1Resolve(c *fiber.Ctx) error { } if isFull { - return c.Redirect("/v1/full/playlists/"+playlistId, fiber.StatusFound) + return c.Redirect("/v1/playlists/"+playlistId, fiber.StatusFound) } return c.Redirect("/v1/playlists/"+playlistId, fiber.StatusFound) } @@ -97,7 +97,7 @@ func (app *ApiServer) v1Resolve(c *fiber.Ctx) error { } if isFull { - return c.Redirect("/v1/full/users/"+userId, fiber.StatusFound) + return c.Redirect("/v1/users/"+userId, fiber.StatusFound) } return c.Redirect("/v1/users/"+userId, fiber.StatusFound) } diff --git a/api/v1_track_remixing_test.go b/api/v1_track_remixing_test.go index 4e26d65f..f1b2432e 100644 --- a/api/v1_track_remixing_test.go +++ b/api/v1_track_remixing_test.go @@ -93,7 +93,7 @@ func TestV1TrackRemixing(t *testing.T) { database.Seed(app.pool.Replicas[0], fixtures) - status, body := testGet(t, app, "/v1/full/tracks/"+trashid.MustEncodeHashID(10)+"/remixing") + status, body := testGet(t, app, "/v1/tracks/"+trashid.MustEncodeHashID(10)+"/remixing") assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -102,13 +102,13 @@ func TestV1TrackRemixing(t *testing.T) { "data.1.id": trashid.MustEncodeHashID(1), }) - status, body = testGet(t, app, "/v1/full/tracks/"+trashid.MustEncodeHashID(11)+"/remixing") + status, body = testGet(t, app, "/v1/tracks/"+trashid.MustEncodeHashID(11)+"/remixing") jsonAssert(t, body, map[string]any{ "data.#": 0, }) - status, body = testGet(t, app, "/v1/full/tracks/"+trashid.MustEncodeHashID(12)+"/remixing") + status, body = testGet(t, app, "/v1/tracks/"+trashid.MustEncodeHashID(12)+"/remixing") jsonAssert(t, body, map[string]any{ "data.#": 0, @@ -118,7 +118,7 @@ func TestV1TrackRemixing(t *testing.T) { func TestV1TrackRemixingInvalidParams(t *testing.T) { app := emptyTestApp(t) - baseUrl := "/v1/full/tracks/" + trashid.MustEncodeHashID(10) + "/remixing" + baseUrl := "/v1/tracks/" + trashid.MustEncodeHashID(10) + "/remixing" status, _ := testGet(t, app, baseUrl+"?limit=invalid") assert.Equal(t, 400, status) diff --git a/api/v1_track_test.go b/api/v1_track_test.go index 6d2803bf..1ca9ff3d 100644 --- a/api/v1_track_test.go +++ b/api/v1_track_test.go @@ -14,7 +14,7 @@ func TestGetTrack(t *testing.T) { Data dbv1.Track } - status, body := testGet(t, app, "/v1/full/tracks/eYJyn", &trackResponse) + status, body := testGet(t, app, "/v1/tracks/eYJyn", &trackResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -30,7 +30,7 @@ func TestGetTrackFollowDownloadAcess(t *testing.T) { Data dbv1.Track } // No access - _, body1 := testGet(t, app, "/v1/full/tracks/eYRWn", &trackResponse) + _, body1 := testGet(t, app, "/v1/tracks/eYRWn", &trackResponse) jsonAssert(t, body1, map[string]any{ "data.title": "Follow Gated Download", "data.access.stream": true, @@ -40,7 +40,7 @@ func TestGetTrackFollowDownloadAcess(t *testing.T) { // With access _, body2 := testGetWithWallet( t, app, - "/v1/full/tracks/eYRWn?user_id=ELKzn", + "/v1/tracks/eYRWn?user_id=ELKzn", "0x4954d18926ba0ed9378938444731be4e622537b2", &trackResponse, ) @@ -57,7 +57,7 @@ func TestGetTrackTipStreamAccess(t *testing.T) { Data dbv1.Track } // No access - _, body1 := testGet(t, app, "/v1/full/tracks/L5x7n", &trackResponse) + _, body1 := testGet(t, app, "/v1/tracks/L5x7n", &trackResponse) jsonAssert(t, body1, map[string]any{ "data.title": "Tip Gated Stream", "data.access.stream": false, @@ -67,7 +67,7 @@ func TestGetTrackTipStreamAccess(t *testing.T) { // With access _, body2 := testGetWithWallet( t, app, - "/v1/full/tracks/L5x7n?user_id=ELKzn", + "/v1/tracks/L5x7n?user_id=ELKzn", "0x4954d18926ba0ed9378938444731be4e622537b2", &trackResponse, ) @@ -84,7 +84,7 @@ func TestGetTrackUsdcPurchaseStreamAccess(t *testing.T) { Data dbv1.Track } // No access - _, body1 := testGet(t, app, "/v1/full/tracks/ebdJL", &trackResponse) + _, body1 := testGet(t, app, "/v1/tracks/ebdJL", &trackResponse) jsonAssert(t, body1, map[string]any{ "data.title": "Pay Gated Stream", "data.access.stream": false, @@ -94,7 +94,7 @@ func TestGetTrackUsdcPurchaseStreamAccess(t *testing.T) { // With access _, body2 := testGetWithWallet( t, app, - "/v1/full/tracks/ebdJL?user_id=1D9On", + "/v1/tracks/ebdJL?user_id=1D9On", "0x855d28d495ec1b06364bb7a521212753e2190b95", &trackResponse, ) @@ -113,7 +113,7 @@ func TestGetTrackUsdcPurchaseSelfAccess(t *testing.T) { // No access. User 3 is the owner, but has not signed authorization status, _ := testGet( t, app, - "/v1/full/tracks/ebdJL?user_id="+trashid.MustEncodeHashID(3), + "/v1/tracks/ebdJL?user_id="+trashid.MustEncodeHashID(3), &trackResponse, ) assert.Equal(t, 403, status) @@ -121,7 +121,7 @@ func TestGetTrackUsdcPurchaseSelfAccess(t *testing.T) { // With access. User 3 is the owner, and has signed authorization _, body2 := testGetWithWallet( t, app, - "/v1/full/tracks/ebdJL?user_id="+trashid.MustEncodeHashID(3), + "/v1/tracks/ebdJL?user_id="+trashid.MustEncodeHashID(3), "0xc3d1d41e6872ffbd15c473d14fc3a9250be5b5e0", &trackResponse, ) diff --git a/api/v1_tracks_test.go b/api/v1_tracks_test.go index 8352bb2c..114e1ca6 100644 --- a/api/v1_tracks_test.go +++ b/api/v1_tracks_test.go @@ -13,7 +13,7 @@ func TestTracksEndpoint(t *testing.T) { Data []dbv1.Track } - status, body := testGet(t, app, "/v1/full/tracks?id=eYZmn", &resp) + status, body := testGet(t, app, "/v1/tracks?id=eYZmn", &resp) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -25,7 +25,7 @@ func TestTracksEndpoint(t *testing.T) { func TestGetTracksByPermalink(t *testing.T) { app := testAppWithFixtures(t) - status, body := testGet(t, app, "/v1/full/tracks?permalink=/TracksByPermalink/track-by-permalink") + status, body := testGet(t, app, "/v1/tracks?permalink=/TracksByPermalink/track-by-permalink") assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ diff --git a/api/v1_users_albums_test.go b/api/v1_users_albums_test.go index 77e7e809..b739129b 100644 --- a/api/v1_users_albums_test.go +++ b/api/v1_users_albums_test.go @@ -49,7 +49,7 @@ func TestGetUserAlbums(t *testing.T) { } { - status, body := testGet(t, app, "/v1/full/users/handle/one/albums", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/albums", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -57,7 +57,7 @@ func TestGetUserAlbums(t *testing.T) { }) } { - status, body := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/albums", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/albums", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -104,7 +104,7 @@ func TestGetUserAlbums_SortRecentDesc(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/albums?sort_method=recent&sort_direction=desc", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/albums?sort_method=recent&sort_direction=desc", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -150,7 +150,7 @@ func TestGetUserAlbums_SortPopularAsc(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/albums?sort_method=popular&sort_direction=asc", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/albums?sort_method=popular&sort_direction=asc", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(1), @@ -199,7 +199,7 @@ func TestGetUserAlbums_FilterAlbumsPublic(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/albums?filter_albums=public", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/albums?filter_albums=public", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.#": 1, @@ -248,7 +248,7 @@ func TestGetUserAlbums_FilterAlbumsPrivate(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/albums?filter_albums=private", &userAlbumsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/albums?filter_albums=private", &userAlbumsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.#": 2, diff --git a/api/v1_users_library_tracks.go b/api/v1_users_library_tracks.go index 0b6598d8..7959ec4c 100644 --- a/api/v1_users_library_tracks.go +++ b/api/v1_users_library_tracks.go @@ -19,9 +19,9 @@ type GetUsersLibraryTracksParams struct { } /* -/v1/full/users/aNzoj/library/tracks?limit=50&offset=50&query=&sort_direction=desc&sort_method=added_date&type=all&user_id=aNzoj +/v1/users/aNzoj/library/tracks?limit=50&offset=50&query=&sort_direction=desc&sort_method=added_date&type=all&user_id=aNzoj -/v1/full/users/aNzoj/library/tracks?limit=50&offset=0&query=&sort_direction=desc&sort_method=added_date&type=favorite&user_id=aNzoj +/v1/users/aNzoj/library/tracks?limit=50&offset=0&query=&sort_direction=desc&sort_method=added_date&type=favorite&user_id=aNzoj */ func (app *ApiServer) v1UsersLibraryTracks(c *fiber.Ctx) error { diff --git a/api/v1_users_library_tracks_test.go b/api/v1_users_library_tracks_test.go index 35085236..aa42205c 100644 --- a/api/v1_users_library_tracks_test.go +++ b/api/v1_users_library_tracks_test.go @@ -27,7 +27,7 @@ func TestUsersLibraryTracks(t *testing.T) { user1Id := trashid.MustEncodeHashID(1) // Test all library tracks - status, body := testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all", &response) + status, body := testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 2, "Should have at least saved and reposted tracks") @@ -37,7 +37,7 @@ func TestUsersLibraryTracks(t *testing.T) { }) // Test favorite tracks only - status, body = testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=favorite", &response) + status, body = testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=favorite", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 1, "Should have at least one favorite track") @@ -48,7 +48,7 @@ func TestUsersLibraryTracks(t *testing.T) { }) // Test repost tracks only - status, body = testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=repost", &response) + status, body = testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=repost", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 1, "Should have at least one reposted track") @@ -60,7 +60,7 @@ func TestUsersLibraryTracks(t *testing.T) { // Test purchase tracks only (user 11 has purchased track 303) user11Id := trashid.MustEncodeHashID(11) - status, body = testGet(t, app, "/v1/full/users/"+user11Id+"/library/tracks?type=purchase", &response) + status, body = testGet(t, app, "/v1/users/"+user11Id+"/library/tracks?type=purchase", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 1, "Should have at least one purchased track") @@ -130,21 +130,21 @@ func TestUsersLibraryTracksUnlistedFiltered(t *testing.T) { } // Test that unlisted tracks saved (not purchased) are filtered out from favorites - status, _ := testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=favorite", &response) + status, _ := testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=favorite", &response) assert.Equal(t, 200, status) assert.Equal(t, 1, len(response.Data), "Should only have public track") assert.Equal(t, int32(100), response.Data[0].ItemID, "Should only return public track") assert.NotNil(t, response.Data[0].Item, "Track metadata should not be null") // Test that unlisted purchased tracks ARE included and have metadata - status, _ = testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=purchase", &response) + status, _ = testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=purchase", &response) assert.Equal(t, 200, status) assert.Equal(t, 1, len(response.Data), "Should return unlisted purchased track") assert.Equal(t, int32(201), response.Data[0].ItemID, "Should return unlisted purchased track") assert.NotNil(t, response.Data[0].Item, "Unlisted purchased track metadata should not be null") // Test that unlisted purchased tracks are included in all - status, _ = testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all", &response) + status, _ = testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all", &response) assert.Equal(t, 200, status) assert.Equal(t, 2, len(response.Data), "Should have both public and unlisted purchased tracks") // Verify both tracks have metadata @@ -167,7 +167,7 @@ func TestUsersLibraryTracksSorting(t *testing.T) { } // Test sorting by title - status, _ := testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all&sort_method=title&sort_direction=asc", &response) + status, _ := testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all&sort_method=title&sort_direction=asc", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 2, "Should have multiple tracks") @@ -178,7 +178,7 @@ func TestUsersLibraryTracksSorting(t *testing.T) { } // Test sorting by added_date (default) - status, _ = testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all&sort_method=added_date&sort_direction=desc", &response) + status, _ = testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all&sort_method=added_date&sort_direction=desc", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 2, "Should have multiple tracks") } @@ -197,7 +197,7 @@ func TestUsersLibraryTracksQuery(t *testing.T) { } // Test query filtering by track title - status, _ := testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all&query=T1", &response) + status, _ := testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all&query=T1", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 1, "Should find tracks matching query") @@ -219,7 +219,7 @@ func TestUsersLibraryTracksMetadataNotNull(t *testing.T) { } // Test that all returned tracks have non-null metadata - status, _ := testGet(t, app, "/v1/full/users/"+user1Id+"/library/tracks?type=all", &response) + status, _ := testGet(t, app, "/v1/users/"+user1Id+"/library/tracks?type=all", &response) assert.Equal(t, 200, status) assert.GreaterOrEqual(t, len(response.Data), 1, "Should have at least one track") diff --git a/api/v1_users_playlists_test.go b/api/v1_users_playlists_test.go index 74926bc3..cde8d79b 100644 --- a/api/v1_users_playlists_test.go +++ b/api/v1_users_playlists_test.go @@ -49,7 +49,7 @@ func TestGetUserPlaylists(t *testing.T) { } { - status, body := testGet(t, app, "/v1/full/users/handle/one/playlists", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/playlists", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -57,7 +57,7 @@ func TestGetUserPlaylists(t *testing.T) { }) } { - status, body := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/playlists", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/playlists", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -104,7 +104,7 @@ func TestGetUserPlaylists_SortRecentDesc(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?sort_method=recent&sort_direction=desc", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/playlists?sort_method=recent&sort_direction=desc", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(2), @@ -150,7 +150,7 @@ func TestGetUserPlaylists_SortPopularAsc(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?sort_method=popular&sort_direction=asc", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/playlists?sort_method=popular&sort_direction=asc", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.0.id": trashid.MustEncodeHashID(1), @@ -199,7 +199,7 @@ func TestGetUserPlaylists_FilterPlaylistsPublic(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?filter_playlists=public", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/playlists?filter_playlists=public", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.#": 1, @@ -248,7 +248,7 @@ func TestGetUserPlaylists_FilterPlaylistsPrivate(t *testing.T) { Data []dbv1.Playlist } - status, body := testGet(t, app, "/v1/full/users/handle/one/playlists?filter_playlists=private", &userPlaylistsResponse) + status, body := testGet(t, app, "/v1/users/handle/one/playlists?filter_playlists=private", &userPlaylistsResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ "data.#": 2, diff --git a/api/v1_users_recommended_tracks_test.go b/api/v1_users_recommended_tracks_test.go index 4ed702ba..23dd93fe 100644 --- a/api/v1_users_recommended_tracks_test.go +++ b/api/v1_users_recommended_tracks_test.go @@ -67,7 +67,7 @@ func TestV1UsersRecommendedTracks(t *testing.T) { Data []dbv1.Track } - status, body := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks", &response) + status, body := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks", &response) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -88,12 +88,12 @@ func TestV1UsersRecommendedTracksInvalidParams(t *testing.T) { app := emptyTestApp(t) for _, val := range []string{"-1", "101", "invalid"} { - status, _ := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks?limit="+val) + status, _ := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks?limit="+val) assert.Equal(t, 400, status) } for _, val := range []string{"-1", "invalid"} { - status, _ := testGet(t, app, "/v1/full/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks?offset="+val) + status, _ := testGet(t, app, "/v1/users/"+trashid.MustEncodeHashID(1)+"/recommended-tracks?offset="+val) assert.Equal(t, 400, status) } } diff --git a/api/v1_users_tracks_ai_attributed_test.go b/api/v1_users_tracks_ai_attributed_test.go index ec57b0e9..7fe49a07 100644 --- a/api/v1_users_tracks_ai_attributed_test.go +++ b/api/v1_users_tracks_ai_attributed_test.go @@ -121,7 +121,7 @@ func TestGetUserTracksAiAttributed(t *testing.T) { Data []dbv1.Track } - baseUrl := "/v1/full/users/handle/testuser1/tracks/ai_attributed" + baseUrl := "/v1/users/handle/testuser1/tracks/ai_attributed" // First test uses marshaling struct to verify that works status, body := testGet(t, app, baseUrl, &userTracksResponse) @@ -297,7 +297,7 @@ func TestGetUserTracksAiAttributed(t *testing.T) { func TestGetUserTracksAiAttributedInvalidParams(t *testing.T) { app := testAppWithFixtures(t) - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks", trashid.MustEncodeHashID(500)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks", trashid.MustEncodeHashID(500)) // Test invalid sort_method url := fmt.Sprintf("%s?sort_method=invalid&sort_direction=desc", baseUrl) status, _ := testGet(t, app, url) diff --git a/api/v1_users_tracks_count_test.go b/api/v1_users_tracks_count_test.go index aa424650..a3299153 100644 --- a/api/v1_users_tracks_count_test.go +++ b/api/v1_users_tracks_count_test.go @@ -20,7 +20,7 @@ func TestGetUserTracksCount(t *testing.T) { Data int } - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks/count", trashid.MustEncodeHashID(600)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks/count", trashid.MustEncodeHashID(600)) // Test without filter - should return all tracks status, _ := testGet(t, app, baseUrl, &response) @@ -85,7 +85,7 @@ func TestGetUserTracksCountWithFilterTracks(t *testing.T) { Data int } - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks/count", trashid.MustEncodeHashID(600)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks/count", trashid.MustEncodeHashID(600)) // Test with public filter url := fmt.Sprintf("%s?filter_tracks=public", baseUrl) @@ -106,7 +106,7 @@ func TestGetUserTracksCountInvalidParams(t *testing.T) { fixtures := testTrackGateFixtures() database.Seed(app.pool.Replicas[0], fixtures) - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks/count", trashid.MustEncodeHashID(600)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks/count", trashid.MustEncodeHashID(600)) // Test invalid filter_tracks value url := fmt.Sprintf("%s?filter_tracks=invalid", baseUrl) diff --git a/api/v1_users_tracks_test.go b/api/v1_users_tracks_test.go index 11ad9bfc..ac7764b4 100644 --- a/api/v1_users_tracks_test.go +++ b/api/v1_users_tracks_test.go @@ -18,7 +18,7 @@ func TestGetUserTracks(t *testing.T) { } // Test support for handle - status, body := testGet(t, app, "/v1/full/users/handle/usertrackstester/tracks", &userTracksResponse) + status, body := testGet(t, app, "/v1/users/handle/usertrackstester/tracks", &userTracksResponse) assert.Equal(t, 200, status) jsonAssert(t, body, map[string]any{ @@ -29,7 +29,7 @@ func TestGetUserTracks(t *testing.T) { }) // Remaining assertions use the user_id version of the route - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks", trashid.MustEncodeHashID(500)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks", trashid.MustEncodeHashID(500)) status, body = testGet(t, app, baseUrl, &userTracksResponse) assert.Equal(t, 200, status) @@ -222,7 +222,7 @@ func TestGetUserTracks(t *testing.T) { func TestGetUserTracksInvalidParams(t *testing.T) { app := testAppWithFixtures(t) - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks", trashid.MustEncodeHashID(500)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks", trashid.MustEncodeHashID(500)) // Test invalid sort_method url := fmt.Sprintf("%s?sort_method=invalid&sort_direction=desc", baseUrl) status, _ := testGet(t, app, url) @@ -258,7 +258,7 @@ func TestGetUserTracksWithGateConditionFilter(t *testing.T) { Data []dbv1.Track } - baseUrl := fmt.Sprintf("/v1/full/users/%s/tracks", trashid.MustEncodeHashID(600)) + baseUrl := fmt.Sprintf("/v1/users/%s/tracks", trashid.MustEncodeHashID(600)) // Test without filter - should return all tracks status, _ := testGet(t, app, baseUrl, &userTracksResponse)