diff --git a/.changeset/purple-experts-allow.md b/.changeset/purple-experts-allow.md
new file mode 100644
index 00000000000..2421101869f
--- /dev/null
+++ b/.changeset/purple-experts-allow.md
@@ -0,0 +1,5 @@
+---
+"@audius/sdk": patch
+---
+
+Fix UploadsApi to make start() a function
diff --git a/docs/docs/developers/sdk/advanced-options.mdx b/docs/docs/developers/sdk/advanced-options.mdx
deleted file mode 100644
index 35df2f47ceb..00000000000
--- a/docs/docs/developers/sdk/advanced-options.mdx
+++ /dev/null
@@ -1,43 +0,0 @@
----
-id: advanced-options
-title: Advanced Options
-pagination_label: Advanced Options
-sidebar_label: Advanced Options
-description: Audius Protocol Documentation
----
-
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
-
-# AdvancedOptions
-
-You can pass a `AdvancedOptions` object to any SDK write method for further control of its behavior.
-
-## Examples
-
-```ts
-await audiusSdk.playlists.favoritePlaylist({
- playlistId: 'x5pJ3Az'
- userId: "7eP5n",
-}, {
- confirmationTimeout: 30000 // Time out if the transaction takes over 30 seconds to confirm
-});
-```
-
-```ts
-await audiusSdk.playlists.favoritePlaylist({
- playlistId: 'x5pJ3Az'
- userId: "7eP5n",
-}, {
- skipConfirmation: true // Don't wait to confirm the transaction; resolve immediately after it is sent. Only for advanced usage.
-});
-```
-
-## Properties
-
-`Optional` **confirmationTimeout**: number - The amount of time in milliseconds to wait for the
-transaction to be confirmed on the blockchain before timing out. Defaults to 45000.
-
-`Optional` **skipConfirmation**: boolean - Whether to resolve immediately after the transaction is
-sent, instead of waiting for the transaction to be confirmed before resolving. Only for advanced
-usage. Defaults to false.
diff --git a/docs/docs/developers/sdk/tracks.mdx b/docs/docs/developers/sdk/tracks.mdx
index 61acddade79..994c844a9d4 100644
--- a/docs/docs/developers/sdk/tracks.mdx
+++ b/docs/docs/developers/sdk/tracks.mdx
@@ -31,9 +31,10 @@ Get a track by id.
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :-------- | :------- | :------------------ | :----------- |
-| `trackId` | `string` | The ID of the track | **Required** |
+| Name | Type | Description | Required? |
+| :-------- | :------- | :------------------------------------ | :----------- |
+| `trackId` | `string` | The ID of the track | **Required** |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
@@ -137,10 +138,12 @@ Get a list of tracks using their IDs or permalinks.
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :---------- | :--------- | :------------------------------- | :--------- |
-| `id` | `string[]` | An array of IDs of tracks | _Optional_ |
-| `permalink` | `string[]` | An array of permalinks of tracks | _Optional_ |
+| Name | Type | Description | Required? |
+| :---------- | :--------- | :---------------------------------------- | :--------- |
+| `id` | `string[]` | An array of IDs of tracks | _Optional_ |
+| `permalink` | `string[]` | An array of permalinks of tracks | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
+| `isrc` | `string[]` | An array of ISRC codes to query tracks by | _Optional_ |
@@ -242,10 +245,13 @@ Get the top 100 trending (most popular) tracks on Audius.
Optionally create an object with the following fields and pass it as the first argument.
-| Name | Type | Description | Required? |
-| :------ | :--------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- | :--------- |
-| `genre` | `Genre` (can be imported from `@audius/sdk`) | If provided, the top 100 trending tracks of the genre will be returned | _Optional_ |
-| `time` | `GetTrendingTracksTimeEnum` (can be imported from `@audius/sdk`) | A time range for which to return the trending tracks. Default value is **GetTrendingTracksTimeEnum.AllTime** | _Optional_ |
+| Name | Type | Description | Required? |
+| :------- | :----------------------------------------- | :--------------------------------------------------------------------------------- | :--------- |
+| `genre` | `string` | If provided, the top 100 trending tracks of the genre will be returned | _Optional_ |
+| `time` | `'week' \| 'month' \| 'year' \| 'allTime'` | A time range for which to return the trending tracks. Default value is `'allTime'` | _Optional_ |
+| `offset` | `number` | The number of items to skip (for pagination) | _Optional_ |
+| `limit` | `number` | The number of items to fetch (for pagination) | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
@@ -347,6 +353,7 @@ Optionally create an object with the following fields and pass it as the first a
| :------- | :------- | :--------------------------------------------------------------------------- | :--------- |
| `limit` | `number` | If provided, will return only the given number of tracks. Default is **100** | _Optional_ |
| `offset` | `number` | An offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
@@ -447,9 +454,21 @@ Search for tracks.
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------ | :------- | :---------------------- | :----------- |
-| `query` | `string` | The query to search for | **Required** |
+| Name | Type | Description | Required? |
+| :-------------------- | :--------- | :------------------------------------------------------- | :--------- |
+| `query` | `string` | The query to search for | _Optional_ |
+| `offset` | `number` | The number of items to skip (for pagination) | _Optional_ |
+| `limit` | `number` | The number of items to fetch (for pagination) | _Optional_ |
+| `genre` | `string[]` | Array of genres to filter by | _Optional_ |
+| `sortMethod` | `string` | Sort method: `'relevant'`, `'popular'`, or `'recent'` | _Optional_ |
+| `mood` | `string[]` | Array of moods to filter by | _Optional_ |
+| `onlyDownloadable` | `string` | Filter to only downloadable tracks | _Optional_ |
+| `includePurchaseable` | `string` | Include purchaseable tracks in results | _Optional_ |
+| `isPurchaseable` | `string` | Filter to only purchaseable tracks | _Optional_ |
+| `hasDownloads` | `string` | Filter tracks that have stems/downloads | _Optional_ |
+| `key` | `string[]` | Array of musical keys to filter by (e.g., `['C', 'Am']`) | _Optional_ |
+| `bpmMin` | `string` | Minimum BPM to filter by | _Optional_ |
+| `bpmMax` | `string` | Maximum BPM to filter by | _Optional_ |
@@ -532,56 +551,22 @@ information about the tracks as described below.
---
-## getTrackStreamUrl
+## createTrack
-> #### getTrackStreamUrl(`params`)
+> #### createTrack(`params`, `requestInit?`)
-Get the url of the track's streamable mp3 file.
+Create a track by registering its metadata on the protocol. This method accepts metadata including
+CIDs (Content Identifiers) that reference previously uploaded files — it does **not** accept raw
+audio or image files directly.
-
-
+:::info Uploading files
-Create an object with the following fields and pass it as the first argument, as shown in the
-example above.
+To upload audio and image files, use the [Uploads API](/developers/sdk/uploads). Upload your files
+first to obtain CIDs, then pass those CIDs in the `metadata` object here. See the
+[full upload + create example](/developers/sdk/uploads#full-example-upload-and-create-a-track) for a
+complete walkthrough.
-| Name | Type | Description | Required? |
-| :-------- | :------- | :------------------ | :----------- |
-| `trackId` | `string` | The ID of the track | **Required** |
-
-
-
-
-```ts
-const url = await audiusSdk.tracks.getTrackStreamUrl({
- trackId: 'PjdWN',
-})
-const audio = new Audio(url)
-audio.play()
-```
-
-
-
-
-Returns a `Promise` containing a `string` url which can be used to stream the track.
-
-
-
-
----
-
-## uploadTrack
-
-> #### uploadTrack(`params`, `advancedOptions?`)
-
-Upload a track.
+:::
void` | A function that will be called with progress events as the files upload | _Optional_ |
-| `audioFile` | `File` | The audio file of the track | **Required** |
-| `userId` | `string` | The ID of the user | **Required** |
+| Name | Type | Description | Required? |
+| :--------- | :-------------------------------------------------- | :-------------------------------------------- | :----------- |
+| `userId` | `string` | The ID of the user | **Required** |
+| `metadata` | [`CreateTrackRequestBody`](#createtrackrequestbody) | An object containing the details of the track | **Required** |
-> #### `advancedOptions`
+See [`CreateTrackRequestBody`](#createtrackrequestbody) for all available metadata fields.
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+> #### `requestInit`
+
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -617,24 +602,39 @@ argument.
import { Mood, Genre } from '@audius/sdk'
import fs from 'fs'
-const coverArtBuffer = fs.readFileSync('path/to/cover-art.png')
+// First, upload files using the Uploads API
const trackBuffer = fs.readFileSync('path/to/track.mp3')
+const audioUpload = audiusSdk.uploads.createAudioUpload({
+ file: {
+ buffer: Buffer.from(trackBuffer),
+ name: 'track.mp3',
+ type: 'audio/mpeg',
+ },
+})
+const audioResult = await audioUpload.start()
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- userId: '7eP5n',
- imageFile: {
+const coverArtBuffer = fs.readFileSync('path/to/cover-art.png')
+const imageUpload = audiusSdk.uploads.createImageUpload({
+ file: {
buffer: Buffer.from(coverArtBuffer),
- name: 'coverArt',
+ name: 'cover-art.png',
+ type: 'image/png',
},
+})
+const coverArtCid = await imageUpload.start()
+
+// Then, create the track with the returned CIDs.
+// The audio upload result fields (trackCid, origFileCid, etc.) match
+// the metadata field names, so you can spread them directly.
+const { data } = await audiusSdk.tracks.createTrack({
+ userId: '7eP5n',
metadata: {
title: 'Monstera',
description: 'Dedicated to my favorite plant',
genre: Genre.METAL,
mood: Mood.AGGRESSIVE,
- },
- audioFile: {
- buffer: Buffer.from(trackBuffer),
- name: 'monsteraAudio',
+ ...audioResult,
+ coverArtCid: coverArtCid,
},
})
```
@@ -660,10 +660,11 @@ hash (`blockHash`) and block number (`blockNumber`) for the transaction.
## updateTrack
-> #### updateTrack(`params`, `advancedOptions?`)
+> #### updateTrack(`params`, `requestInit?`)
-Update a track. If cover art or any metadata fields are not provided, their values will be kept the
-same as before.
+Update a track's metadata. If any metadata fields are not provided, their values will be kept the
+same as before. To replace audio or cover art, upload new files via the
+[Uploads API](/developers/sdk/uploads) and pass the new CIDs in `metadata`.
` | An object containing the details of the track | **Required** |
-| `generatePreview` | `boolean` | Whether to regenerate the track preview (used when preview start time changes) | _Optional_ |
-| `onProgress` | `(progress: number, event: `[`UploadTrackProgressEvent`](/developers/sdk/progress-events#uploadtrackprogressevent)`) => void` | A function that will be called with progress events as files upload | _Optional_ |
+| Name | Type | Description | Required? |
+| :--------- | :-------------------------------------------------- | :---------------------------------------- | :----------- |
+| `trackId` | `string` | The ID of the track | **Required** |
+| `userId` | `string` | The ID of the user | **Required** |
+| `metadata` | [`UpdateTrackRequestBody`](#updatetrackrequestbody) | An object containing the fields to update | **Required** |
-> #### `advancedOptions`
+All fields are optional — only include the fields you want to change. See
+[`UpdateTrackRequestBody`](#updatetrackrequestbody) for all available fields.
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+> #### `requestInit`
+
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
```ts
-import fs from 'fs'
import { Mood } from '@audius/sdk'
-const coverArtBuffer = fs.readFileSync('path/to/updated-cover-art.png')
-
-const { trackId } = await audiusSdk.tracks.updateTrack({
+const { data } = await audiusSdk.tracks.updateTrack({
trackId: 'h5pJ3Bz',
- imageFile: {
- buffer: Buffer.from(coverArtBuffer),
- name: 'coverArt',
- },
+ userId: '7eP5n',
metadata: {
- description: 'Dedicated to my favorite plant... new cover art!',
+ description: 'Dedicated to my favorite plant... updated!',
mood: Mood.YEARNING,
},
- onProgress: (progress) => {
- console.log('Progress: ', progress / 100)
- },
- userId: '7eP5n',
})
```
@@ -740,7 +731,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
## deleteTrack
-> #### deleteTrack(`params`, `advancedOptions?`)
+> #### deleteTrack(`params`, `requestInit?`)
Delete a track
@@ -763,10 +754,11 @@ example above.
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
-> #### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -798,7 +790,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
## favoriteTrack
-> #### favoriteTrack(`params`, `advancedOptions?`)
+> #### favoriteTrack(`params`, `requestInit?`)
Favorite a track
@@ -828,10 +820,11 @@ example above.
}
```
-> #### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -863,7 +856,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
## unfavoriteTrack
-> #### unfavoriteTrack(`params`, `advancedOptions?`)
+> #### unfavoriteTrack(`params`, `requestInit?`)
Unfavorite a track
@@ -886,10 +879,11 @@ example above.
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
-> #### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -921,7 +915,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
## repostTrack
-> #### repostTrack(`params`, `advancedOptions?`)
+> #### repostTrack(`params`, `requestInit?`)
Repost a track
@@ -951,10 +945,11 @@ example above.
}
```
-> #### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -988,7 +983,7 @@ Return type:
## unrepostTrack
-> #### unrepostTrack(`params`, `advancedOptions?`)
+> #### unrepostTrack(`params`, `requestInit?`)
Unrepost a track
@@ -1012,10 +1007,11 @@ example above.
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
-> #### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
@@ -1047,3 +1043,260 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
---
+
+## Type Reference
+
+### CreateTrackRequestBody
+
+Metadata object for creating a new track. Fields marked **Required** must be provided.
+
+| Name | Type | Description | Required? |
+| :----------------------------- | :------------------------------------------------------ | :------------------------------------------------ | :----------- |
+| `title` | `string` | Track title | **Required** |
+| `genre` | `Genre` | Track genre | **Required** |
+| `trackCid` | `string` | CID of the transcoded audio (from Uploads API) | **Required** |
+| `trackId` | `string` | Track ID (auto-generated if not provided) | _Optional_ |
+| `description` | `string` | Track description | _Optional_ |
+| `mood` | `Mood` | Track mood | _Optional_ |
+| `bpm` | `number` | Beats per minute | _Optional_ |
+| `musicalKey` | `string` | Musical key of the track | _Optional_ |
+| `tags` | `string` | Comma-separated tags | _Optional_ |
+| `license` | `string` | License type | _Optional_ |
+| `isrc` | `string` | International Standard Recording Code | _Optional_ |
+| `iswc` | `string` | International Standard Musical Work Code | _Optional_ |
+| `releaseDate` | `Date` | Release date | _Optional_ |
+| `origFileCid` | `string` | CID of the original audio file (from Uploads API) | _Optional_ |
+| `origFilename` | `string` | Original filename of the audio file | _Optional_ |
+| `coverArtCid` | `string` | CID of the cover art image (from Uploads API) | _Optional_ |
+| `coverArtSizes` | `string` | Cover art sizes metadata | _Optional_ |
+| `previewCid` | `string` | CID of the preview clip (from Uploads API) | _Optional_ |
+| `previewStartSeconds` | `number` | Preview start time in seconds | _Optional_ |
+| `duration` | `number` | Track duration in seconds | _Optional_ |
+| `isDownloadable` | `boolean` | Whether the track is downloadable | _Optional_ |
+| `isUnlisted` | `boolean` | Whether the track is unlisted (hidden) | _Optional_ |
+| `isStreamGated` | `boolean` | Whether streaming is behind an access gate | _Optional_ |
+| `streamConditions` | [`AccessGate`](#accessgate) | Conditions for stream access gating | _Optional_ |
+| `downloadConditions` | [`AccessGate`](#accessgate) | Conditions for download access gating | _Optional_ |
+| `fieldVisibility` | [`FieldVisibility`](#fieldvisibility) | Controls visibility of individual track fields | _Optional_ |
+| `placementHosts` | `string` | Preferred storage node placement hosts | _Optional_ |
+| `stemOf` | [`StemParent`](#stemparent) | Parent track if this track is a stem | _Optional_ |
+| `remixOf` | [`RemixParentWrite`](#remixparentwrite) | Parent track(s) if this track is a remix | _Optional_ |
+| `noAiUse` | `boolean` | Whether AI use is prohibited | _Optional_ |
+| `isOwnedByUser` | `boolean` | Whether the track is owned by the user | _Optional_ |
+| `coverOriginalSongTitle` | `string` | Original song title (for cover tracks) | _Optional_ |
+| `coverOriginalArtist` | `string` | Original artist (for cover tracks) | _Optional_ |
+| `parentalWarningType` | `string` | Parental warning type | _Optional_ |
+| `territoryCodes` | `string[]` | Territory codes for distribution | _Optional_ |
+| `ddexApp` | `string` | DDEX application identifier | _Optional_ |
+| `ddexReleaseIds` | `object` | DDEX release identifiers | _Optional_ |
+| `artists` | `object[]` | DDEX resource contributors / artists | _Optional_ |
+| `resourceContributors` | [`DdexResourceContributor[]`](#ddexresourcecontributor) | DDEX resource contributors | _Optional_ |
+| `indirectResourceContributors` | [`DdexResourceContributor[]`](#ddexresourcecontributor) | DDEX indirect resource contributors | _Optional_ |
+| `rightsController` | [`DdexRightsController`](#ddexrightscontroller) | DDEX rights controller | _Optional_ |
+| `copyrightLine` | `CopyrightLine` | Copyright line | _Optional_ |
+| `producerCopyrightLine` | `ProducerCopyrightLine` | Producer copyright line | _Optional_ |
+
+---
+
+### UpdateTrackRequestBody
+
+Metadata object for updating an existing track. All fields are optional — only include the fields
+you want to change. Omitted fields retain their current values.
+
+| Name | Type | Description |
+| :-------------------- | :-------------------------------------- | :------------------------------------------------ |
+| `title` | `string` | Track title |
+| `genre` | `Genre` | Track genre |
+| `description` | `string` | Track description |
+| `mood` | `Mood` | Track mood |
+| `bpm` | `number` | Beats per minute |
+| `musicalKey` | `string` | Musical key of the track |
+| `tags` | `string` | Comma-separated tags |
+| `license` | `string` | License type |
+| `isrc` | `string` | International Standard Recording Code |
+| `iswc` | `string` | International Standard Musical Work Code |
+| `releaseDate` | `Date` | Release date |
+| `trackCid` | `string` | CID of the transcoded audio (from Uploads API) |
+| `origFileCid` | `string` | CID of the original audio file (from Uploads API) |
+| `origFilename` | `string` | Original filename of the audio file |
+| `coverArtCid` | `string` | CID of the cover art image (from Uploads API) |
+| `coverArtSizes` | `string` | Cover art sizes metadata |
+| `previewCid` | `string` | CID of the preview clip (from Uploads API) |
+| `previewStartSeconds` | `number` | Preview start time in seconds |
+| `duration` | `number` | Track duration in seconds |
+| `isDownloadable` | `boolean` | Whether the track is downloadable |
+| `isUnlisted` | `boolean` | Whether the track is unlisted (hidden) |
+| `isStreamGated` | `boolean` | Whether streaming is behind an access gate |
+| `streamConditions` | [`AccessGate`](#accessgate) | Conditions for stream access gating |
+| `downloadConditions` | [`AccessGate`](#accessgate) | Conditions for download access gating |
+| `fieldVisibility` | [`FieldVisibility`](#fieldvisibility) | Controls visibility of individual track fields |
+| `placementHosts` | `string` | Preferred storage node placement hosts |
+| `stemOf` | [`StemParent`](#stemparent) | Parent track if this track is a stem |
+| `remixOf` | [`RemixParentWrite`](#remixparentwrite) | Parent track(s) if this track is a remix |
+| `parentalWarningType` | `string` | Parental warning type |
+| `ddexApp` | `string` | DDEX application identifier |
+
+---
+
+### AccessGate
+
+`AccessGate` is a union type representing the conditions required to access a gated track. Used by
+both `streamConditions` and `downloadConditions`. Provide **one** of the following:
+
+#### FollowGate
+
+Require the listener to follow a specific user.
+
+```ts
+{
+ followUserId: number // The user ID that must be followed
+}
+```
+
+#### TipGate
+
+Require the listener to have tipped a specific user.
+
+```ts
+{
+ tipUserId: number // The user ID that must be tipped
+}
+```
+
+#### PurchaseGate
+
+Require the listener to purchase access with USDC.
+
+```ts
+{
+ usdcPurchase: {
+ price: number // Price in cents
+ splits: Array<{
+ userId: number // User ID of the payment recipient
+ percentage: number // Percentage of the price (0-100)
+ }>
+ }
+}
+```
+
+#### TokenGate
+
+Require the listener to hold a specific SPL token.
+
+```ts
+{
+ tokenGate: {
+ tokenMint: string // The mint address of the SPL token
+ tokenAmount: number // The minimum amount of the token required
+ }
+}
+```
+
+**Example — USDC purchase-gated track:**
+
+```ts
+const metadata = {
+ title: 'Premium Track',
+ genre: Genre.ELECTRONIC,
+ trackCid: '...',
+ isStreamGated: true,
+ streamConditions: {
+ usdcPurchase: {
+ price: 199, // $1.99 in cents
+ splits: [{ userId: 12345, percentage: 100 }],
+ },
+ },
+}
+```
+
+---
+
+### FieldVisibility
+
+Controls which track fields are publicly visible. Each field is a `boolean`.
+
+```ts
+{
+ mood: boolean
+ tags: boolean
+ genre: boolean
+ share: boolean
+ playCount: boolean
+ remixes: boolean
+}
+```
+
+:::note
+
+For public tracks, `genre`, `mood`, `tags`, `share`, and `playCount` are set to `true` by default.
+For stream-gated (non-USDC) tracks, `remixes` is set to `false` by default.
+
+:::
+
+---
+
+### RemixParentWrite
+
+Identifies the parent track(s) that a remix is derived from.
+
+```ts
+{
+ tracks: Array<{
+ parentTrackId: string // The ID of the parent track
+ }>
+}
+```
+
+**Example:**
+
+```ts
+const metadata = {
+ title: 'My Remix',
+ genre: Genre.ELECTRONIC,
+ trackCid: '...',
+ remixOf: {
+ tracks: [{ parentTrackId: 'D7KyD' }],
+ },
+}
+```
+
+---
+
+### StemParent
+
+Identifies the parent track when uploading a stem (e.g. vocals, drums, bass).
+
+```ts
+{
+ category: string // Stem category (e.g. 'VOCAL', 'DRUMS', 'BASS', 'INSTRUMENTAL', 'OTHER')
+ parentTrackId: number // The numeric ID of the parent track
+}
+```
+
+---
+
+### DdexResourceContributor
+
+Represents a contributor to the track (used for DDEX metadata).
+
+```ts
+{
+ name: string // Contributor name
+ roles: string[] // Contributor roles (e.g. 'Composer', 'Lyricist')
+ sequenceNumber?: number // Optional ordering index
+}
+```
+
+---
+
+### DdexRightsController
+
+Represents the rights controller for the track (used for DDEX metadata).
+
+```ts
+{
+ name: string // Rights controller name
+ roles: string[] // Roles (e.g. 'RightsController')
+ rightsShareUnknown?: string // Optional rights share info
+}
+```
diff --git a/docs/docs/developers/sdk/uploads.mdx b/docs/docs/developers/sdk/uploads.mdx
new file mode 100644
index 00000000000..7d54c7035c1
--- /dev/null
+++ b/docs/docs/developers/sdk/uploads.mdx
@@ -0,0 +1,214 @@
+---
+id: uploads
+title: Uploads
+pagination_label: Uploads
+sidebar_label: Uploads
+description: Audius Protocol Documentation
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+---
+
+The Uploads API provides methods for uploading audio and image files to Audius storage nodes. These
+methods return CIDs (Content Identifiers) that you then pass to write methods like
+[`createTrack`](/developers/sdk/tracks#createtrack) or
+[`updateTrack`](/developers/sdk/tracks#updatetrack).
+
+:::tip
+
+File uploads are a separate step from track/playlist creation. You first upload your files using the
+Uploads API to get CIDs, then pass those CIDs as metadata when creating or updating content.
+
+:::
+
+---
+
+## createAudioUpload
+
+> #### createAudioUpload(`params`)
+
+Upload an audio file to a storage node. Returns the resulting CIDs and audio analysis metadata
+(duration, BPM, musical key).
+
+
+
+
+Create an object with the following fields and pass it as the first argument.
+
+| Name | Type | Description | Required? |
+| :-------------------- | :---------------------------------------- | :--------------------------------------------------- | :----------- |
+| `file` | `File` | The audio file to upload | **Required** |
+| `onProgress` | `(loaded: number, total: number) => void` | A callback for tracking upload progress | _Optional_ |
+| `previewStartSeconds` | `number` | Start time in seconds for generating a preview clip | _Optional_ |
+| `placementHosts` | `string[]` | A list of storage node hosts to prefer for placement | _Optional_ |
+
+
+
+
+```ts
+import fs from 'fs'
+
+const trackBuffer = fs.readFileSync('path/to/track.mp3')
+
+const upload = audiusSdk.uploads.createAudioUpload({
+ file: {
+ buffer: Buffer.from(trackBuffer),
+ name: 'track.mp3',
+ type: 'audio/mpeg',
+ },
+ onProgress: (loaded, total) => {
+ console.log(`Upload progress: ${Math.round((loaded / total) * 100)}%`)
+ },
+ previewStartSeconds: 30,
+})
+
+const result = await upload.start()
+console.log(result)
+```
+
+
+
+
+The method returns an object with:
+
+- `abort` — a function you can call to cancel the upload.
+- `start()` — a function that begins the upload and returns a `Promise` that resolves with the
+ upload result once complete.
+
+The resolved value of `start()` contains:
+
+```ts
+{
+ trackCid: string // CID of the transcoded 320kbps audio
+ previewCid?: string // CID of the preview clip (if previewStartSeconds was provided)
+ origFileCid: string // CID of the original uploaded file
+ origFilename: string // Original filename
+ duration: number // Duration in seconds
+ bpm?: number // Detected BPM (beats per minute)
+ musicalKey?: string // Detected musical key
+}
+```
+
+
+
+
+---
+
+## createImageUpload
+
+> #### createImageUpload(`params`)
+
+Upload an image file (e.g. cover art or profile picture) to a storage node. Returns the resulting
+CID.
+
+
+
+
+Create an object with the following fields and pass it as the first argument.
+
+| Name | Type | Description | Required? |
+| :--------------- | :---------------------------------------- | :---------------------------------------------------------------------------- | :----------- |
+| `file` | `File` | The image file to upload | **Required** |
+| `onProgress` | `(loaded: number, total: number) => void` | A callback for tracking upload progress | _Optional_ |
+| `isBackdrop` | `boolean` | Set to `true` for wide/banner images (e.g. profile banners). Default: `false` | _Optional_ |
+| `placementHosts` | `string[]` | A list of storage node hosts to prefer for placement | _Optional_ |
+
+
+
+
+```ts
+import fs from 'fs'
+
+const coverArtBuffer = fs.readFileSync('path/to/cover-art.png')
+
+const upload = audiusSdk.uploads.createImageUpload({
+ file: {
+ buffer: Buffer.from(coverArtBuffer),
+ name: 'cover-art.png',
+ type: 'image/png',
+ },
+})
+
+const coverArtCid = await upload.start()
+console.log(coverArtCid) // CID string
+```
+
+
+
+
+The method returns an object with:
+
+- `abort` — a function you can call to cancel the upload.
+- `start()` — a function that begins the upload and returns a `Promise` resolving with the CID of
+ the uploaded image (`string`).
+
+
+
+
+---
+
+## Full Example: Upload and Create a Track
+
+This example demonstrates the full workflow of uploading files via the Uploads API and then creating
+a track with the returned CIDs.
+
+```ts
+import { Mood, Genre } from '@audius/sdk'
+import fs from 'fs'
+
+// Step 1: Upload the audio file
+const trackBuffer = fs.readFileSync('path/to/track.mp3')
+const audioUpload = audiusSdk.uploads.createAudioUpload({
+ file: {
+ buffer: Buffer.from(trackBuffer),
+ name: 'track.mp3',
+ type: 'audio/mpeg',
+ },
+ previewStartSeconds: 30,
+})
+const audioResult = await audioUpload.start()
+
+// Step 2: Upload cover art
+const coverArtBuffer = fs.readFileSync('path/to/cover-art.png')
+const imageUpload = audiusSdk.uploads.createImageUpload({
+ file: {
+ buffer: Buffer.from(coverArtBuffer),
+ name: 'cover-art.png',
+ type: 'image/png',
+ },
+})
+const coverArtCid = await imageUpload.start()
+
+// Step 3: Create the track using the CIDs from the uploads.
+// The audio upload result fields (trackCid, previewCid, origFileCid, etc.)
+// match the metadata field names, so you can spread them directly.
+const { data } = await audiusSdk.tracks.createTrack({
+ userId: '7eP5n',
+ metadata: {
+ title: 'Monstera',
+ description: 'Dedicated to my favorite plant',
+ genre: Genre.METAL,
+ mood: Mood.AGGRESSIVE,
+ ...audioResult,
+ coverArtCid: coverArtCid,
+ },
+})
+```
diff --git a/docs/docs/developers/sdk/users.mdx b/docs/docs/developers/sdk/users.mdx
index 660c52f47ea..dbbed38f803 100644
--- a/docs/docs/developers/sdk/users.mdx
+++ b/docs/docs/developers/sdk/users.mdx
@@ -78,7 +78,7 @@ user as described below.
### getBulkUsers
-#### getBulkUser(`params`)
+#### getBulkUsers(`params`)
Gets a list of users.
@@ -97,9 +97,10 @@ console.log(users)
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :--- | :--------- | :------------------- | :----------- |
-| `id` | `string[]` | The IDs of the users | **Required** |
+| Name | Type | Description | Required? |
+| :------- | :--------- | :------------------------------------ | :--------- |
+| `id` | `string[]` | The IDs of the users | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -226,47 +227,6 @@ information about the wallets as described below.
---
-### getDeveloperApps
-
-#### getDeveloperApps(`params`)
-
-Get the developer apps a user owns.
-
-Example:
-
-```ts
-const { data: developerApps } = await audiusSdk.users.getDeveloperApps({
- id: 'eAZl3',
-})
-
-console.log(developerApps)
-```
-
-#### Params
-
-Create an object with the following fields and pass it as the first argument, as shown in the
-example above.
-
-| Name | Type | Description | Required? |
-| :--- | :------- | :----------------- | :----------- |
-| `id` | `string` | The ID of the user | **Required** |
-
-#### Returns
-
-Returns a `Promise` containing an object with a `data` field. `data` is an array of items containing
-information about the developer apps as described below.
-
-```ts
-{
- address: string;
- description?: string;
- name: string;
- userId: string;
-}[];
-```
-
----
-
### getFavorites
#### getFavorites(`params`)
@@ -335,6 +295,7 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of followers to return. Default value is **10** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -404,6 +365,7 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -432,11 +394,13 @@ console.log(relatedUsers)
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------- | :------- | :----------------------------------------------------------------- | :----------- |
-| `id` | `string` | The ID of the user | **Required** |
-| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
-| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| Name | Type | Description | Required? |
+| :--------------- | :-------- | :----------------------------------------------------------------- | :----------- |
+| `id` | `string` | The ID of the user | **Required** |
+| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
+| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
+| `filterFollowed` | `boolean` | Filter to only users that the current user follows | _Optional_ |
#### Returns
@@ -468,6 +432,7 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of reposts to return. Default value is **100** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -514,6 +479,7 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -547,6 +513,7 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -596,20 +563,20 @@ information about the supporters as described below.
---
-### getSupportings
+### getSupportedUsers
-#### getSupportings(`params`)
+#### getSupportedUsers(`params`)
Get users that a user is supporting (they have sent them a tip).
Example:
```ts
-const { data: supportings } = await audiusSdk.users.getSupportings({
+const { data: supportedUsers } = await audiusSdk.users.getSupportedUsers({
id: 'eAZl3',
})
-console.log(supportings)
+console.log(supportedUsers)
```
#### Params
@@ -622,11 +589,12 @@ example above.
| `id` | `string` | The ID of the user | **Required** |
| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
Returns a `Promise` containing an object with a `data` field. `data` is an array of items containing
-information about the supportings as described below.
+information about the supported users as described below.
```ts
{
@@ -690,11 +658,11 @@ console.log(tags)
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------- | :------- | :----------------------------------------------------------------- | :----------- |
-| `id` | `string` | The ID of the user | **Required** |
-| `limit` | `number` | The maximum number of users to return. Default value is **10** | _Optional_ |
-| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | _Optional_ |
+| Name | Type | Description | Required? |
+| :------- | :------- | :------------------------------------------------------------ | :----------- |
+| `id` | `string` | The ID of the user | **Required** |
+| `limit` | `number` | The maximum number of tags to return. Default value is **10** | _Optional_ |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -765,9 +733,10 @@ console.log(user)
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------- | :------- | :--------------------- | :----------- |
-| `handle` | `string` | The handle of the user | **Required** |
+| Name | Type | Description | Required? |
+| :------- | :------- | :------------------------------------ | :----------- |
+| `handle` | `string` | The handle of the user | **Required** |
+| `userId` | `string` | The ID of the user making the request | _Optional_ |
#### Returns
@@ -775,44 +744,6 @@ The return type is the same as [`getUser`](#getuser)
---
-### getUserIdByWallet
-
-#### getUserIdByWallet(`params`)
-
-Get a user ID by an associated wallet address.
-
-Example:
-
-```ts
-const { data: userId } = await audiusSdk.users.getUserIdByWallet({
- associatedWallet: '6f229f7e8462f198e5be9139175a0b460a9fa35b',
-})
-
-console.log(userId)
-```
-
-#### Params
-
-Create an object with the following fields and pass it as the first argument, as shown in the
-example above.
-
-| Name | Type | Description | Required? |
-| :----------------- | :------- | :---------------------------------------- | :----------- |
-| `associatedWallet` | `string` | A wallet address associated with the user | **Required** |
-
-#### Returns
-
-Returns a `Promise` containing an object with a `data` field. `data` is an object containing the
-user id as described below.
-
-```ts
-{
- userId: string
-}
-```
-
----
-
### searchUsers
#### searchUsers(`params`)
@@ -834,9 +765,14 @@ console.log(users)
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------ | :------- | :---------------------------- | :----------- |
-| `query` | `string` | The query for which to search | **Required** |
+| Name | Type | Description | Required? |
+| :----------- | :--------- | :---------------------------------------------------- | :--------- |
+| `query` | `string` | The query for which to search | _Optional_ |
+| `offset` | `number` | The number of items to skip (for pagination) | _Optional_ |
+| `limit` | `number` | The number of items to fetch (for pagination) | _Optional_ |
+| `genre` | `string[]` | Array of genres to filter by | _Optional_ |
+| `sortMethod` | `string` | Sort method: `'relevant'`, `'popular'`, or `'recent'` | _Optional_ |
+| `isVerified` | `string` | Filter to only verified users | _Optional_ |
#### Returns
@@ -844,31 +780,34 @@ The return type is the same as [`getFollowers`](#getfollowers)
---
-### updateProfile
+### updateUser
-#### updateProfile(`params`, `advancedOptions?`)
+#### updateUser(`params`, `requestInit?`)
-Update a user profile.
+Update a user profile. To update a profile picture or cover photo, first upload the image via the
+[Uploads API](/developers/sdk/uploads) and pass the resulting CID in `metadata`.
Example:
```ts
-import { Mood, Genre } from '@audius/sdk'
import fs from 'fs'
+// Upload a new profile picture
const profilePicBuffer = fs.readFileSync('path/to/profile-pic.png')
-
-await audiusSdk.users.updateProfile({
- userId: '7eP5n',
- profilePictureFile: {
+const profilePicUpload = audiusSdk.uploads.createImageUpload({
+ file: {
buffer: Buffer.from(profilePicBuffer),
- name: 'profilePic',
+ name: 'profilePic.png',
},
+})
+const profilePicCid = await profilePicUpload.start()
+
+await audiusSdk.users.updateUser({
+ id: '7eP5n',
+ userId: '7eP5n',
metadata: {
bio: 'up and coming artist from the Bronx',
- },
- onProgress: (progress) => {
- console.log(`Uploading: ${Math.round((progress.loaded / progress.total) * 100)}%`)
+ profilePicture: profilePicCid,
},
})
```
@@ -878,39 +817,41 @@ await audiusSdk.users.updateProfile({
Create an object with the following fields and pass it as the first argument, as shown in the
example above.
-| Name | Type | Description | Required? |
-| :------------------- | :-------------------------------------------------------------------------------- | :---------------------------------------------------------------------------- | :----------- |
-| `profilePictureFile` | `File` | A file to be used as the profile picture | _Optional_ |
-| `coverArtFile` | `File` | A file to be used as the cover art. This is the header on a profile page | _Optional_ |
-| `metadata` | _see code block below_ | An object with details about the user | **Required** |
-| `onProgress` | `(progress: { loaded: number, total: number, transcode: number }) => void` | A function that will be called with progress events as the image files upload | _Optional_ |
-| `userId` | `string` | The ID of the user | **Required** |
-
-```json title="updateProfile metadata payload"
-{
- name?: string;
- handle?: string;
- bio?: string;
- website?: string;
- donation?: string;
- location?: string;
- profileType?: 'label' | null;
- metadataMultihash?: string;
- isDeactivated?: boolean;
- artistPickTrackId?: string;
- allowAiAttribution?: boolean;
- twitterHandle?: string;
- instagramHandle?: string;
- tiktokHandle?: string;
- splUsdcPayoutWallet?: string | null;
- coinFlairMint?: string | null;
-}
-```
-
-#### `advancedOptions`
-
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+| Name | Type | Description | Required? |
+| :--------- | :---------------------- | :------------------------------------ | :----------- |
+| `id` | `string` | The ID of the user | **Required** |
+| `userId` | `string` | The ID of the user (same as id) | **Required** |
+| `metadata` | `UpdateUserRequestBody` | An object with details about the user | **Required** |
+
+`UpdateUserRequestBody` — all fields are optional, only include the fields you want to change:
+
+| Name | Type | Description |
+| :-------------------- | :---------------- | :-------------------------------------------- |
+| `name` | `string` | Display name |
+| `handle` | `string` | User handle (set only if not already set) |
+| `bio` | `string` | User bio |
+| `location` | `string` | User location |
+| `website` | `string` | Website URL |
+| `donation` | `string` | Donation link |
+| `twitterHandle` | `string` | Twitter handle (without @) |
+| `instagramHandle` | `string` | Instagram handle (without @) |
+| `tiktokHandle` | `string` | TikTok handle (without @) |
+| `profilePicture` | `string` | Profile picture CID or URL (from Uploads API) |
+| `profilePictureSizes` | `string` | Profile picture sizes metadata |
+| `coverPhoto` | `string` | Cover photo CID or URL (from Uploads API) |
+| `coverPhotoSizes` | `string` | Cover photo sizes metadata |
+| `profileType` | `'label' \| null` | Set to `'label'` for record label profiles |
+| `isDeactivated` | `boolean` | Whether the user is deactivated |
+| `artistPickTrackId` | `string` | Track hash ID for artist pick |
+| `allowAiAttribution` | `boolean` | Whether to allow AI attribution |
+| `splUsdcPayoutWallet` | `string` | Solana USDC payout wallet address |
+| `coinFlairMint` | `string` | Coin flair mint address |
+
+> #### `requestInit`
+
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
#### Returns
@@ -928,7 +869,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
### followUser
-#### followUser(`params`, `advancedOptions?`)
+#### followUser(`params`, `requestInit?`)
Follow a user.
@@ -951,10 +892,11 @@ example above.
| `userId` | `string` | The ID of the user | **Required** |
| `followeeUserId` | `string` | The ID of the user to follow | **Required** |
-#### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
#### Returns
@@ -972,7 +914,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
### unfollowUser
-#### unfollowUser(`params`, `advancedOptions?`)
+#### unfollowUser(`params`, `requestInit?`)
Unfollow a user.
@@ -995,10 +937,11 @@ example above.
| `userId` | `string` | The ID of the user | **Required** |
| `followeeUserId` | `string` | The ID of the user to unfollow | **Required** |
-#### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
#### Returns
@@ -1016,7 +959,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
### subscribeToUser
-#### subscribeToUser(`params`, `advancedOptions?`)
+#### subscribeToUser(`params`, `requestInit?`)
Subscribe to a user.
@@ -1039,10 +982,11 @@ example above.
| `userId` | `string` | The ID of the user | **Required** |
| `subscribeeUserId` | `string` | The ID of the user to subscribe to | **Required** |
-#### `advancedOptions
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
#### Returns
@@ -1060,7 +1004,7 @@ Returns a `Promise` containing an object with the block hash (`blockHash`) and b
### unsubscribeFromUser
-#### unsubscribeFromUser(`params`, `advancedOptions?`)
+#### unsubscribeFromUser(`params`, `requestInit?`)
Unsubscribe from a user.
@@ -1083,10 +1027,11 @@ example above.
| `userId` | `string` | The ID of the user | **Required** |
| `subscribeeUserId` | `string` | The ID of the user to unsubscribe from | **Required** |
-#### `advancedOptions`
+> #### `requestInit`
-You can pass an optional [`advancedOptions`](/developers/sdk/advanced-options) object as the second
-argument.
+You can pass an optional
+[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the second
+argument to customize the underlying fetch request (e.g. set custom headers, abort signal, etc.).
#### Returns
diff --git a/docs/docs/developers/upload-track-metadata.mdx b/docs/docs/developers/upload-track-metadata.mdx
deleted file mode 100644
index 335407b5619..00000000000
--- a/docs/docs/developers/upload-track-metadata.mdx
+++ /dev/null
@@ -1,191 +0,0 @@
----
-id: upload-track-metadata
-title: Upload Track Metadata
-pagination_label: Upload Track Metadata
-sidebar_label: Upload Track Metadata
-description: Audius Protocol Documentation
----
-
-# **`interface`** UploadTrackMetadata
-
-Contains required and optional fields for uploading a track.
-
-## Properties
-
-```ts
-{
- // Required fields:
- title: string;
- genre: Genre; // Can import `Genre` enum from @audius/sdk
-
-
- // Optional fields:
- description?: string;
- mood?: Mood; // Can import `Mood` enum from @audius/sdk
- releaseDate?: Date; // Should not be in the future. Defaults to today's date.
- tags?: string; // Comma separated list of tags
- remixOf?: { tracks: Array<{ parentTrackId: string }> }; // For specifying the track(s) that your track is a remix of
- isStreamGated?: boolean; // Whether streaming your track is only available to users who meet certain criteria, which must be specified by `streamConditions`.
- streamConditions?: AccessConditions; // See "Specifying Stream Conditions" section below
- isDownloadGated?: boolean; // Whether downloading your track is only available to users who meet certain criteria, which must be specified by `downloadConditions`. Note that stream gated tracks are automatically download gated, whereas the reverse is not true.
- downloadConditions?: AccessConditions;
- isUnlisted?: boolean; // If set to true, only users with a link to your track will be able to listen, and your track will not show up in your profile or in any feed. Defaults to false.
- fieldVisibility?: {
- mood?: boolean;
- tags?: boolean;
- genre?: boolean;
- share?: boolean;
- playCount?: boolean;
- remixes?: boolean;
- }; // For specifying which fields/features are visible on a hidden track. Only applicable if `isUnlisted` is set to true. All default to true.
- isrc?: string; // International Standard Recording Code
- iswc?: string // International Standard Musical Word Code
- license?: string; // License type, e.g. Attribution-NonCommercial-ShareAlike CC BY-NC-SA
-}
-```
-
-## Example with all fields specified
-
-```ts
-import { Mood, Genre } from '@audius/sdk'
-import fs from 'fs'
-
-const coverArtBuffer = fs.readFileSync('path/to/cover-art.png')
-const trackBuffer = fs.readFileSync('path/to/track.mp3')
-
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- userId: '7eP5n',
- imageFile: {
- buffer: Buffer.from(coverArtBuffer),
- name: 'coverArt',
- },
- metadata: {
- title: 'Monstera',
- genre: Genre.METAL,
-
- // Optional metadata:
- description: 'Dedicated to my favorite plant',
- mood: Mood.DEVOTIONAL,
- releaseDate: new Date('2022-09-30'),
- tags: 'plantlife,love,monstera',
- remixOf: { tracks: [{ parentTrackId: 'KVx2xpO' }] },
- isStreamGated: true,
- streamConditions: {
- tipUserId: '7eP5n', // Require tipping user to unlock track for streaming
- },
- isDownloadGated: true,
- downloadConditions: {
- usdcPurchase: {
- // Require usdc purchase to unlock track for download
- price: 1,
- splits: {
- // usdc user bank and usdc amount padded with correct number of decimals
- FwtT6g2tmwbgY6gf4NWBhupJBqJjgkaHRzCJpA1YHrL2: 10000,
- },
- },
- },
- isUnlisted: true,
- fieldVisibility: {
- mood: true,
- tags: true,
- genre: true,
- share: false,
- playCount: false,
- remixes: true,
- },
- isrc: 'USAT21812345',
- iswc: 'T-123.456.789-0',
- license: 'Attribution-NonCommercial-ShareAlike CC BY-NC-SA',
- },
- audioFile: {
- buffer: Buffer.from(trackBuffer),
- name: 'monsteraAudio',
- },
-})
-```
-
-## Specifying Stream Conditions
-
-Use the `AccessConditions` field to specify the criteria required to unlock a track.
-
-### Tip-gated
-
-Require the listener to tip the artist to unlock the track.
-
-#### Example
-
-```ts
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- // ...
- metadata: {
- // ...
- isStreamGated: true,
- streamConditions: {
- tipUserId: '7eP5n', // Require tipping user with user ID "7eP5n" to unlock track
- },
- },
-})
-```
-
-### Follow-gated
-
-Require the listener to follow the artist to unlock the track.
-
-#### Example
-
-```ts
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- // ...
- metadata: {
- // ...
- isStreamGated: true,
- streamConditions: {
- followUserId: '7eP5n', // Require following user with user ID "7eP5n" to unlock track
- },
- },
-})
-```
-
-### NFT-gated
-
-Require the listener to hold an Ethereum or Solana NFT to unlock the track.
-
-#### Ethereum NFT example
-
-```ts
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- // ...
- metadata: {
- // ...
- isStreamGated: true,
- streamConditions: {
- chain: 'eth',
- address: '0xAbCdEfGhIjKlMnOpQrStUvWxYz', // The Ethereum address of the NFT contract
- standard: 'ERC-721', // The standard followed by the NFT - either "ERC-721" or "ERC-1155"
- name: 'Example NFT', // The name of the NFT
- slug: 'example-nft', // The slug of the NFT collection. E.g. if your collection is located at https://opensea.io/collection/example-nft, the slug is "example-nft".
- imageUrl: 'https://www.example.com/nft-image.png', // Optional: URL to the image representing the NFT
- externalLink: 'https://www.example.com/nft-details', // Optional: URL to an external resource providing more details about the NFT
- },
- },
-})
-```
-
-#### Solana NFT example
-
-```ts
-const { trackId } = await audiusSdk.tracks.uploadTrack({
- // ...
- metadata: {
- // ...
- isStreamGated: true,
- streamConditions: {
- chain: 'sol',
- address: 'ABCDEF1234567890', // The address of the NFT on the Solana blockchain
- name: 'Example NFT', // The name of the NFT
- imageUrl: 'https://www.example.com/nft-image.png', // Optional: URL to the image representing the NFT
- externalLink: 'https://www.example.com/nft-details', // Optional: URL to an external resource providing more details about the NFT
- },
- },
-})
-```
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 281c36bf410..b8d872ac744 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -1,4 +1,4 @@
-const apiSidebar = require('./docs/developers/api/sidebar.generated.js');
+const apiSidebar = require('./docs/developers/api/sidebar.generated.js')
module.exports = {
learn: [],
@@ -27,13 +27,12 @@ module.exports = {
items: [
'developers/sdk/overview',
'developers/sdk/tracks',
- 'developers/upload-track-metadata',
+ 'developers/sdk/uploads',
'developers/sdk/users',
'developers/sdk/playlists',
'developers/sdk/albums',
'developers/sdk/resolve',
'developers/sdk/oauth',
- 'developers/sdk/advanced-options',
],
collapsed: false,
},
diff --git a/packages/sdk/src/sdk/api/uploads/UploadsApi.ts b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts
index 3960b4cf5c1..85652f029ee 100644
--- a/packages/sdk/src/sdk/api/uploads/UploadsApi.ts
+++ b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts
@@ -42,18 +42,19 @@ export class UploadsApi {
})
return {
abort: upload.abort,
- start: upload.start().then((res) => ({
- trackCid: res.results['320'],
- previewCid:
- previewStartSeconds !== undefined && previewStartSeconds !== null
- ? res.results[`320_preview|${previewStartSeconds}`]
- : undefined,
- origFileCid: res.orig_file_cid,
- origFilename: res.orig_filename,
- duration: parseInt(res?.probe?.format?.duration ?? '0', 10),
- bpm: res.audio_analysis_results?.bpm,
- musicalKey: res.audio_analysis_results?.key
- }))
+ start: () =>
+ upload.start().then((res) => ({
+ trackCid: res.results['320'],
+ previewCid:
+ previewStartSeconds !== undefined && previewStartSeconds !== null
+ ? res.results[`320_preview|${previewStartSeconds}`]
+ : undefined,
+ origFileCid: res.orig_file_cid,
+ origFilename: res.orig_filename,
+ duration: parseInt(res?.probe?.format?.duration ?? '0', 10),
+ bpm: res.audio_analysis_results?.bpm,
+ musicalKey: res.audio_analysis_results?.key
+ }))
}
}
@@ -88,7 +89,7 @@ export class UploadsApi {
})
return {
abort: upload.abort,
- start: upload.start().then((res) => res.orig_file_cid)
+ start: () => upload.start().then((res) => res.orig_file_cid)
}
}
}
diff --git a/packages/sdk/src/sdk/createSdkWithServices.ts b/packages/sdk/src/sdk/createSdkWithServices.ts
index efba71c9fb6..4a5e8febac2 100644
--- a/packages/sdk/src/sdk/createSdkWithServices.ts
+++ b/packages/sdk/src/sdk/createSdkWithServices.ts
@@ -31,7 +31,6 @@ import {
addAppInfoMiddleware,
addRequestSignatureMiddleware
} from './middleware'
-import { addBearerTokenMiddleware } from './middleware/addBearerTokenMiddleware'
import { OAuth } from './oauth'
import {
PaymentRouterClient,
@@ -461,19 +460,11 @@ const initializeApis = ({
})
]
- if ('bearerToken' in config) {
- middleware.push(
- addBearerTokenMiddleware({
- bearerToken: config.bearerToken,
- logger: services.logger
- })
- )
- }
-
const apiClientConfig = new Configuration({
fetchApi: fetch,
middleware,
- basePath
+ basePath,
+ accessToken: 'bearerToken' in config ? config.bearerToken : undefined
})
const tracks = new TracksApi(apiClientConfig, services)
diff --git a/packages/sdk/src/sdk/createSdkWithoutServices.ts b/packages/sdk/src/sdk/createSdkWithoutServices.ts
index 9e01fa07989..57325ea8dd3 100644
--- a/packages/sdk/src/sdk/createSdkWithoutServices.ts
+++ b/packages/sdk/src/sdk/createSdkWithoutServices.ts
@@ -28,7 +28,6 @@ import {
addAppInfoMiddleware,
addRequestSignatureMiddleware
} from './middleware'
-import { addBearerTokenMiddleware } from './middleware/addBearerTokenMiddleware'
import { OAuth } from './oauth'
import { Logger, Storage, StorageNodeSelector } from './services'
import { type SdkConfig } from './types'
@@ -55,10 +54,6 @@ export const createSdkWithoutServices = (config: SdkConfig) => {
const middleware: Middleware[] = []
- if (bearerToken) {
- middleware.push(addBearerTokenMiddleware({ bearerToken, logger }))
- }
-
if (apiSecret || services?.audiusWalletClient) {
middleware.push(
addRequestSignatureMiddleware({
@@ -89,7 +84,8 @@ export const createSdkWithoutServices = (config: SdkConfig) => {
const apiConfig = new Configuration({
fetchApi: fetch,
middleware,
- basePath
+ basePath,
+ accessToken: bearerToken
})
// Initialize OAuth
diff --git a/packages/sdk/src/sdk/middleware/addBearerTokenMiddleware.ts b/packages/sdk/src/sdk/middleware/addBearerTokenMiddleware.ts
deleted file mode 100644
index 38e434527fe..00000000000
--- a/packages/sdk/src/sdk/middleware/addBearerTokenMiddleware.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import type {
- FetchParams,
- Middleware,
- RequestContext
-} from '../api/generated/default'
-import { Logger, type LoggerService } from '../services'
-
-export const addBearerTokenMiddleware = (services: {
- bearerToken: string
- logger?: LoggerService
-}): Middleware => {
- const bearerToken = services.bearerToken
- const logger = services.logger ?? new Logger()
- return {
- pre: async (context: RequestContext): Promise => {
- const existingHeaders = context.init.headers as Record
- if (existingHeaders.Authorization) {
- logger.warn(
- 'Request already has an Authorization header. Skipping adding bearer token.'
- )
- return context
- }
- return {
- ...context,
- url: context.url,
- init: {
- ...context.init,
- headers: {
- ...context.init.headers,
- Authorization: `Bearer ${bearerToken}`
- }
- }
- }
- }
- }
-}