# Audius Developer Docs
> Audius is a fully decentralized music platform. Build on the largest open music catalog on the internet.
Albums in Audius are playlists with `isAlbum` set to `true`. Use the [Playlists API](playlists) for
all album operations — pass `isAlbum: true` in the metadata when creating or updating.
See [createPlaylist](playlists#createplaylist) and [updatePlaylist](playlists#updateplaylist) for
details.
## Getting Started with the Audius SDK
### Overview
The Audius JavaScript (TypeScript) SDK allows you to easily interact with the Audius protocol. Use
the SDK to:
* 🔍 Search and display users, tracks, and playlists
* 🎵 Stream and upload tracks
* ❤️ Favorite, repost, and curate playlists
* ✍️ Allow your users to [log in with their Audius account](/developers/guides/log-in-with-audius)
and act on their behalf
...and much more!
### API Plans
Audius offers two API plans:
| Plan | Rate Limit | Monthly Requests |
| ------------- | ------------------ | ---------------------- |
| **Free** | 10 requests/second | 500,000 requests/month |
| **Unlimited** | Unlimited | Unlimited |
The Free plan is always free with no restrictions. For higher limits and support, contact [api@audius.co](mailto\:api@audius.co) about the Unlimited plan.
### Get Your API Key and Bearer Token
1. Visit the [Audius API Plans page](https://api.audius.co/plans) and click "Create API Key" to generate your credentials.
2. You will receive an **API Key** and a **Bearer Token**. Save both securely — treat them like passwords.
:::tip
The bearer token is the recommended way to authenticate with the Audius API.
:::
### Install the SDK
* [Node.js](#nodejs)
* [HTML + JS](#html--js)
#### Node.js
If your project is in a Node.js environment, run this in your terminal:
```bash
npm install @audius/sdk
```
[@audius/sdk on NPM](https://www.npmjs.com/package/@audius/sdk)
#### HTML + JS
Otherwise, include the SDK script tag in your web page. The Audius SDK will then be assigned to
`window.audiusSdk`.
```html
```
### Initialize the SDK
Initialize the SDK with your API key and bearer token.
#### Node.js example
```js title="In Node.js environment"
import { sdk } from '@audius/sdk'
const audiusSdk = sdk({
apiKey: 'Your API Key goes here',
bearerToken: 'Your Bearer Token goes here',
})
```
#### HTML + JS example
```js title="In web page"
const audiusSdk = window.audiusSdk({
apiKey: 'Your API Key goes here',
})
```
:::warning
DO NOT include the bearer token if you are running the SDK in the browser or anywhere on the client.
The bearer token is what allows your app to write on behalf of the users that have authorized it to
do so. Keep your bearer token secure and never expose it in client-side code that could be inspected.
:::
### Make your first API call using the SDK!
Once you have the initialized SDK instance, it's smooth sailing to making your first API calls.
```js
// Fetch your first track!
const track = await audiusSdk.tracks.getTrack({ trackId: 'D7KyD' })
console.log(track, 'Track fetched!')
// Favorite a track
const userId = (
await audiusSdk.users.getUserByHandle({
handle: 'Your Audius handle goes here',
})
).data?.id
await audiusSdk.tracks.favoriteTrack({
trackId: 'D7KyD',
userId,
})
```
### Full Node.js example
```js title="app.js" showLineNumbers
import { sdk } from '@audius/sdk'
const audiusSdk = sdk({
apiKey: 'Your API Key goes here',
bearerToken: 'Your Bearer Token goes here',
})
const track = await audiusSdk.tracks.getTrack({ trackId: 'D7KyD' })
console.log(track, 'Track fetched!')
const userId = (
await audiusSdk.users.getUserByHandle({
handle: 'Your Audius handle goes here',
})
).data?.id
await audiusSdk.tracks.favoriteTrack({
trackId: 'D7KyD',
userId,
})
console.log('Track favorited!')
```
### Full HTML + JS example
```html title="index.html" showLineNumbers
Example content
```
### What's next?
* [Get authorization](/developers/guides/log-in-with-audius) to access your app's users' Audius
accounts
* [Explore the API docs](/sdk/tracks) to see what else you can do with the Audius SDK
### Direct API Access
You can also access the Audius API directly without the SDK:
**REST API:**
```bash
curl -X GET "https://api.audius.co/v1/tracks/trending" \
-H "Authorization: Bearer "
```
**gRPC:**
```bash
grpcurl -H "authorization: Bearer " \
grpc.audius.co:443 list
```
For more details, visit the [API documentation](https://docs.audius.co/api) or the [Swagger definition](https://api.audius.co/v1).
## OAuth
Enable users to either authorize your app to perform actions on their behalf (i.e. grant write
permissions), or simply verify who they are and give you access to their Audius profile info (no
write permissions granted).
### Methods
#### init
##### init(`params`)
Enables the Audius OAuth functionality. Call this before using any other `oauth` methods.
**Params**
* successCallback `(profile: UserProfile, encodedJwt: string) => void` - function to be called when
the user successfully authorizes or authenticates with Audius. This function will be called with
the user's profile information, which is an object with the following shape:
```ts
// `UserProfile` type:
{
userId: number; // unique Audius user identifier
email: string;
name: string; // user's display name
handle: string;
verified: boolean; // whether the user has the Audius "verified" checkmark
/** URLs for the user's profile picture, if any.
* If the user has a profile picture, three sizes will be available: 150x150, 480x480, and 1000x1000.
* If the user has no profile picture, this field will be empty.
*/
profilePicture: {"150x150": string, "480x480": string, "1000x1000": string } | { misc: string } | undefined | null
apiKey: string | null // the API key for your application if specified
sub: number; // alias for userId
iat: string; // timestamp for when auth was performed
}
```
The second argument (`encodedJwt`) is for advanced use cases that want to pass the JWT to a
backend for server-side verification.
* errorCallback *optional* `(errorMessage: string) => void` - function to be called when an error
occurs during the authentication flow. This function will be called with a string describing the
error.
**Returns**: Nothing
**Example**
```js
audiusSdk.oauth.init({
successCallback: (user) => console.log('Logged in user', user),
errorCallback: (error) => console.log('Got error', error),
})
```
#### renderButton
##### renderButton(`params`)
Replaces the element passed in the first parameter with the Log In with Audius button.
**Params**
* element `HTMLElement` - HTML element to replace with the Log In with Audius button
* scope *optional* `'write' | 'read'` - Whether your app is requesting read or write permissions to
the user's account. Specify "write" if you'd like the user to authorize your app to perform
actions on their behalf, such as uploading a track or favoriting a playlist. Specify "read" if you
simply need the user's email and profile information, and do not need any write permissions.
Defaults to "read".
* buttonOptions *optional* `ButtonOptions` - optional object containing the customization settings
for the button to be rendered. Here are the options available:
```ts
// type ButtonOptions =
{
// Size of the button:
size?: "small | 'medium' | 'large'
// Corner style of the button:
corners?: 'default' | 'pill'
// Your own text for the button; default is "Log In with Audius":
customText?: string
// Whether to disable the button's "grow" animation on hover:
disableHoverGrow?: boolean
// Whether the button should take up the full width of its parent element:
fullWidth?: boolean
}
```
Use [this playground](https://9ncjui.csb.app/) to see how these customizations affect the button
appearance and determine what config works best for your app.
:::note
The `write` scope grants your app permission to perform most actions on the user's behalf, but it
does NOT allow any access to DMs or wallets.
:::
**Returns**: Nothing
**Example**
```js
audiusSdk.oauth.renderButton({
element: document.getElementById('audiusLogInButton'),
scope: 'write',
buttonOptions: {
size: 'large',
},
})
```
#### login
##### login(`params`)
Opens the Log In with Audius popup, which begins the authentication flow. Use this if you have your
own link or button implementation, and don't want to use `renderButton`.
**Params**
* scope *optional* `'write' | 'read'` - Whether your app is requesting read or write permissions to
the user's account. Specify "write" if you'd like the user to authorize your app to perform
actions on their behalf, such as uploading a track or favoriting a playlist. Specify "read" if you
simply need the user's email and profile information, and do not need any write permissions.
Defaults to "read".
:::note
The `write` scope grants your app permission to perform most actions on the user's behalf, but it
does NOT allow any access to DMs or wallets.
:::
**Returns**: Nothing
**Example**
```js title="script.js"
function logInWithAudius() {
audiusSdk.oauth.login({ scope: 'write' })
}
```
```html title="index.html"
Continue with Audius!
```
#### isWriteAccessGranted
##### isWriteAccessGranted(`params`)
Checks whether your app has write permissions to the given user's account.
**Params**
* userId `string` - User's Audius user ID
**Returns**: `Promise`
**Example**
```js
async function hasSkrillexAuthorizedApp() {
await audiusSdk.oauth.isWriteAccessGranted({ userId: 'eAZl3' })
}
```
***
### getPlaylist
> ##### getPlaylist(`params`)
Get a playlist by id.
Example:
```ts
const { data: playlist } = await audiusSdk.playlists.getPlaylist({
playlistId: 'D7KyD',
})
console.log(playlist)
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
##### Returns
Returns a `Promise` containing an object with a `data` field. `data` contains a
[`PlaylistResponse`](#playlistresponse) object.
***
### getBulkPlaylists
> ##### getBulkPlaylists(`params`)
Get a list of playlists by ID, UPC, or permalink.
Example:
```ts
const { data: playlists } = await audiusSdk.playlists.getBulkPlaylists({
id: ['D7KyD', '68yPZb'],
})
console.log(playlists)
```
##### 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[]` | An array of playlist IDs | *Optional* |
| `permalink` | `string[]` | An array of permalinks of playlists | *Optional* |
| `upc` | `string[]` | An array of UPC codes | *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
[`PlaylistResponse`](#playlistresponse) objects.
***
### getPlaylistTracks
> ##### getPlaylistTracks(`params`)
Get the tracks in a playlist.
Example:
```ts
const { data: tracks } = await audiusSdk.playlists.getPlaylistTracks({
playlistId: 'D7KyD',
})
console.log(tracks)
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
##### Returns
The return type is the same as [`getBulkTracks`](tracks#getbulktracks)
***
### getTrendingPlaylists
> ##### getTrendingPlaylists(`params?`)
Get the top trending playlists on Audius.
Example:
```ts
const { data: playlists } = await audiusSdk.playlists.getTrendingPlaylists()
console.log(playlists)
```
##### 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? |
| :----- | :-------------------------------------------------------------- | :-------------------------------------------------------- | :--------- |
| `time` | [`GetTrendingPlaylistsTimeEnum`](#gettrendingplayliststimeenum) | The time period for trending playlists. Default: `'week'` | *Optional* |
##### Returns
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`PlaylistResponse`](#playlistresponse) objects.
***
### searchPlaylists
> ##### searchPlaylists(`params`)
Search for playlists.
Example:
```ts
const { data: playlists } = await audiusSdk.playlists.searchPlaylists({
query: 'skrillex',
})
console.log(playlists)
```
##### 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? |
| :------ | :------- | :---------------------- | :----------- |
| `query` | `string` | The query to search for | **Required** |
##### Returns
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`PlaylistResponse`](#playlistresponse) objects.
***
### createPlaylist
> ##### createPlaylist(`params`, `requestInit?`)
Create a new playlist.
To upload a cover image, use the [Uploads API](/sdk/uploads). Upload your image first to
obtain a CID, then pass it as `playlistImageSizesMultihash` in the `metadata` object.
Example:
```ts
// Optional: Upload a cover image first
const { start: startImageUpload } = audiusSdk.uploads.createImageUpload({
file: coverImageFile,
})
const coverArtCid = await startImageUpload()
const { data } = await audiusSdk.playlists.createPlaylist({
userId: '7eP5n',
metadata: {
playlistName: 'Summer Vibes 2024',
description: 'The best summer tracks',
playlistContents: [
{ trackId: 'ePR5Ll', timestamp: Math.round(Date.now() / 1000) },
{ trackId: 'GManBz', timestamp: Math.round(Date.now() / 1000) },
],
playlistImageSizesMultihash: coverArtCid,
},
})
console.log('New playlist ID:', data.playlistId)
```
##### 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? |
| :--------- | :-------------------------------------------------------- | :-------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `metadata` | [`CreatePlaylistRequestBody`](#createplaylistrequestbody) | The playlist metadata | **Required** |
See [`CreatePlaylistRequestBody`](#createplaylistrequestbody) for all available metadata fields.
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
playlistId?: string
transactionHash?: string
}
```
***
### updatePlaylist
> ##### updatePlaylist(`params`, `requestInit?`)
Update an existing playlist's metadata. If any metadata fields are not provided, their values will
be kept the same as before. To replace cover art, upload a new image via the
[Uploads API](/sdk/uploads) and pass the new CID in `metadata`.
Example:
```ts
// Optional: Upload a new cover image
const { start: startImageUpload } = audiusSdk.uploads.createImageUpload({
file: newCoverImageFile,
})
const coverArtCid = await startImageUpload()
const { data } = await audiusSdk.playlists.updatePlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
metadata: {
playlistName: 'Summer Vibes 2024 (Updated)',
description: 'Updated playlist description',
playlistContents: [
{ trackId: 'ePR5Ll', timestamp: Math.round(Date.now() / 1000) },
{ trackId: 'GManBz', timestamp: Math.round(Date.now() / 1000) },
{ trackId: 'KWJm0x', timestamp: Math.round(Date.now() / 1000) },
],
playlistImageSizesMultihash: coverArtCid,
},
})
console.log('Updated playlist:', data)
```
##### 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? |
| :----------- | :-------------------------------------------------------- | :------------------------------ | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
| `metadata` | [`UpdatePlaylistRequestBody`](#updateplaylistrequestbody) | The playlist metadata to update | **Required** |
All fields are optional — only include the fields you want to change. See
[`UpdatePlaylistRequestBody`](#updateplaylistrequestbody) for all available fields.
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### deletePlaylist
> ##### deletePlaylist(`params`, `requestInit?`)
Delete a playlist.
Example:
```ts
await audiusSdk.playlists.deletePlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
})
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### favoritePlaylist
> ##### favoritePlaylist(`params`, `requestInit?`)
Favorite a playlist.
Example:
```ts
await audiusSdk.playlists.favoritePlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
})
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### unfavoritePlaylist
> ##### unfavoritePlaylist(`params`, `requestInit?`)
Unfavorite a playlist.
Example:
```ts
await audiusSdk.playlists.unfavoritePlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
})
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### repostPlaylist
> ##### repostPlaylist(`params`, `requestInit?`)
Repost a playlist.
Example:
```ts
await audiusSdk.playlists.repostPlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
})
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### unrepostPlaylist
> ##### unrepostPlaylist(`params`, `requestInit?`)
Unrepost a playlist.
Example:
```ts
await audiusSdk.playlists.unrepostPlaylist({
playlistId: 'D7KyD',
userId: '7eP5n',
})
```
##### 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? |
| :----------- | :------- | :--------------------- | :----------- |
| `playlistId` | `string` | The ID of the playlist | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### Type Reference
#### PlaylistResponse
| Field | Type | Description |
| :--------------------- | :--------- | :------------------------------------------------------------- |
| `artwork` | `object` | Artwork images (see below) |
| `artwork._1000x1000` | `string` | URL of 1000x1000 artwork |
| `artwork._150x150` | `string` | URL of 150x150 artwork |
| `artwork._480x480` | `string` | URL of 480x480 artwork |
| `coverArtSizes` | `string` | CID of the cover art sizes |
| `description` | `string` | Playlist description |
| `favoriteCount` | `number` | Number of favorites |
| `id` | `string` | Playlist ID |
| `isAlbum` | `boolean` | Whether this is an album |
| `isImageAutogenerated` | `boolean` | Whether the cover art is auto-generated |
| `isPrivate` | `boolean` | Whether the playlist is private |
| `permalink` | `string` | Permalink URL |
| `playlistContents` | `object[]` | Array of track entries (trackId, timestamp, metadataTimestamp) |
| `playlistName` | `string` | Name of the playlist |
| `repostCount` | `number` | Number of reposts |
| `totalPlayCount` | `number` | Total play count |
| `user` | `object` | The playlist owner (see [User](users)) |
#### GetTrendingPlaylistsTimeEnum
| Value | String | Description |
| :-------- | :---------- | :------------------ |
| `Week` | `'week'` | Past week (default) |
| `Month` | `'month'` | Past month |
| `Year` | `'year'` | Past year |
| `AllTime` | `'allTime'` | All time |
#### CreatePlaylistRequestBody
| Field | Type | Required | Description |
| ----------------------------- | ------------------------------------------------------------- | -------- | ----------------------------------------------------- |
| `playlistName` | `string` | Yes | The name of the playlist |
| `playlistId` | `string` | No | Optional playlist ID (auto-generated if not provided) |
| `playlistImageSizesMultihash` | `string` | No | CID of the cover art image (from Uploads API) |
| `description` | `string` | No | The playlist description |
| `isAlbum` | `boolean` | No | Whether this is an album |
| `isPrivate` | `boolean` | No | Whether the playlist is private |
| `genre` | [`Genre`](tracks#genre-values) | No | Playlist/album genre |
| `mood` | [`Mood`](tracks#mood-values) | No | Playlist/album mood |
| `tags` | `string` | No | Comma-separated tags |
| `license` | `string` | No | License type |
| `upc` | `string` | No | Universal Product Code (for albums) |
| `releaseDate` | `Date` | No | Release date |
| `playlistContents` | [`PlaylistAddedTimestamp[]`](#playlistaddedtimestamp) | No | Array of tracks to include in the playlist |
| `isStreamGated` | `boolean` | No | Whether streaming is behind an access gate |
| `isScheduledRelease` | `boolean` | No | Whether this is a scheduled release |
| `streamConditions` | [`AccessGate`](tracks#accessgate) | No | Conditions for stream access gating |
| `ddexApp` | `string` | No | DDEX application identifier |
| `ddexReleaseIds` | `object` | No | DDEX release identifiers |
| `artists` | [`DdexResourceContributor[]`](tracks#ddexresourcecontributor) | No | DDEX resource contributors / artists |
| `copyrightLine` | `object` | No | Copyright line |
| `producerCopyrightLine` | `object` | No | Producer copyright line |
| `parentalWarningType` | `string` | No | Parental warning type |
| `isImageAutogenerated` | `boolean` | No | Whether the image is autogenerated |
#### UpdatePlaylistRequestBody
All fields are optional — only include the fields you want to change.
| Field | Type | Required | Description |
| ----------------------------- | ------------------------------------------------------------- | -------- | ---------------------------------------------- |
| `playlistName` | `string` | No | The name of the playlist |
| `playlistImageSizesMultihash` | `string` | No | CID of the cover art image (from Uploads API) |
| `description` | `string` | No | The playlist description |
| `isAlbum` | `boolean` | No | Whether this is an album |
| `isPrivate` | `boolean` | No | Whether the playlist is private |
| `genre` | [`Genre`](tracks#genre-values) | No | Playlist/album genre |
| `mood` | [`Mood`](tracks#mood-values) | No | Playlist/album mood |
| `tags` | `string` | No | Comma-separated tags |
| `license` | `string` | No | License type |
| `upc` | `string` | No | Universal Product Code (for albums) |
| `releaseDate` | `Date` | No | Release date |
| `playlistContents` | [`PlaylistAddedTimestamp[]`](#playlistaddedtimestamp) | No | Array of tracks in the playlist (replaces all) |
| `isStreamGated` | `boolean` | No | Whether streaming is behind an access gate |
| `isScheduledRelease` | `boolean` | No | Whether this is a scheduled release |
| `streamConditions` | [`AccessGate`](tracks#accessgate) | No | Conditions for stream access gating |
| `ddexApp` | `string` | No | DDEX application identifier |
| `ddexReleaseIds` | `object` | No | DDEX release identifiers |
| `artists` | [`DdexResourceContributor[]`](tracks#ddexresourcecontributor) | No | DDEX resource contributors / artists |
| `copyrightLine` | `object` | No | Copyright line |
| `producerCopyrightLine` | `object` | No | Producer copyright line |
| `parentalWarningType` | `string` | No | Parental warning type |
| `isImageAutogenerated` | `boolean` | No | Whether the image is autogenerated |
#### PlaylistAddedTimestamp
Represents a track entry within a playlist.
| Field | Type | Required | Description |
| ------------------- | -------- | -------- | -------------------------------------------- |
| `trackId` | `string` | Yes | The ID of the track |
| `timestamp` | `number` | Yes | Timestamp when the track was added (seconds) |
| `metadataTimestamp` | `number` | No | Optional metadata timestamp (seconds) |
import { Tabs } from '../../components/Tabs.jsx'
import { TabItem } from '../../components/Tabs.jsx'
## Progress Events
Some methods have optional `onProgress` callbacks to handle progress events for uploading resources.
The first argument to the `onProgress` callback is always a rolled up progress value from 0-1. The
second will be one of the following types depending on the method.
### UploadTrackProgressEvent
```ts
{
key: 'audio' | 'image' // Which file upload triggered the event
loaded: number // How much of the file has been uploaded, in bytes
total: number // Total number of bytes of the file
transcode: number // Completion percent (from 0-1) of the transcoding
}
```
### UploadPlaylistProgressEvent
```ts
{
key: 'image' | number // The index of the audioFile or 'image'
loaded: number // How much of the file has been uploaded, in bytes
total: number // Total number of bytes of the file
transcode: number // Completion percent (from 0-1) of the transcoding
}
```
import { Tabs } from '../../components/Tabs.jsx'
import { TabItem } from '../../components/Tabs.jsx'
#### resolve
##### resolve(`params`)
Resolve a provided Audius app URL to the API resource it represents.
Example:
```ts
const { data: track } = await audiusSdk.resolve({
url: 'https://audius.co/camouflybeats/hypermantra-86216',
})
console.log(track)
```
##### 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? |
| :---- | :------- | :--------------------------------- | :----------- |
| `url` | `string` | The url of the resource to resolve | **Required** |
##### Returns
Returns a `Promise` containing an object with a `data` field. `data` is an object representing the
resolved resource as described below.
```ts
{
data: Track | Playlist | Album
}
```
***
***
### getTrack
> ##### getTrack(`params`, `requestInit?`)
Get a track by id.
Example:
```ts
const { data: track } = await audiusSdk.tracks.getTrack({
trackId: 'D7KyD',
})
console.log(track)
```
##### 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? |
| :-------- | :------- | :------------------------------------ | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user making the request | *Optional* |
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
Returns a `Promise` containing an object with a `data` field. `data` contains a
[`TrackResponse`](#trackresponse) object.
***
### getBulkTracks
> ##### getBulkTracks(`params`, `requestInit?`)
Get a list of tracks using their IDs or permalinks.
Example:
```ts
const { data: tracks } = await audiusSdk.tracks.getBulkTracks({
id: ['D7KyD', 'PjdWN', 'Jwo2A'],
})
console.log(tracks)
```
##### 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[]` | 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* |
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
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`TrackResponse`](#trackresponse) objects.
***
### getTrendingTracks
> ##### getTrendingTracks(`params`, `requestInit?`)
Get the top 100 trending (most popular) tracks on Audius.
Example:
```ts
const { data: tracks } = await audiusSdk.tracks.getTrendingTracks()
console.log(tracks)
```
##### Params
Optionally create an object with the following fields and pass it as the first argument, as shown in
the example above.
| Name | Type | Description | Required? |
| :------- | :----------------------------------------- | :--------------------------------------------------------------------------------- | :--------- |
| `genre` | [`Genre`](#genre-values) | 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* |
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
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`TrackResponse`](#trackresponse) objects.
***
### getUndergroundTrendingTracks
> ##### getUndergroundTrendingTracks(`params`, `requestInit?`)
Get the top 100 trending underground tracks on Audius.
Example:
```ts
const { data: tracks } = await audiusSdk.tracks.getUndergroundTrendingTracks()
console.log(tracks)
```
##### Params
Optionally create an object with the following fields and pass it as the first argument, as shown in
the example above.
| Name | Type | Description | Required? |
| :------- | :------- | :--------------------------------------------------------------------------- | :--------- |
| `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* |
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
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`TrackResponse`](#trackresponse) objects.
***
### searchTracks
> ##### searchTracks(`params`, `requestInit?`)
Search for tracks.
Example:
```ts
const { data: tracks } = await audiusSdk.tracks.searchTracks({
query: 'skrillex',
})
console.log(tracks)
```
##### 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? |
| :-------------------- | :------------------------- | :------------------------------------------------------- | :--------- |
| `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` | [`Genre[]`](#genre-values) | Array of genres to filter by | *Optional* |
| `sortMethod` | `string` | Sort method: `'relevant'`, `'popular'`, or `'recent'` | *Optional* |
| `mood` | [`Mood[]`](#mood-values) | 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* |
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
Returns a `Promise` containing an object with a `data` field. `data` is an array of
[`TrackResponse`](#trackresponse) objects.
***
### createTrack
> ##### createTrack(`params`, `requestInit?`)
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]
To upload audio and image files, use the [Uploads API](/sdk/uploads). Upload your files
first to obtain CIDs, then pass those CIDs in the `metadata` object here. See the
[full upload + create example](/sdk/uploads#full-example-upload-and-create-a-track) for a
complete walkthrough.
:::
Example:
```ts
import fs from 'fs'
// 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 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()
// 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: 'Metal',
mood: 'Aggressive',
...audioResult,
coverArtSizes: coverArtCid,
},
})
```
##### 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? |
| :--------- | :-------------------------------------------------- | :-------------------------------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `metadata` | [`CreateTrackRequestBody`](#createtrackrequestbody) | An object containing the details of the track | **Required** |
See [`CreateTrackRequestBody`](#createtrackrequestbody) for all available metadata fields.
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
Returns a `Promise` resolving to an object with the following fields:
````ts
{
transactionHash?: string
trackId?: string
}
---
## updateTrack
> #### updateTrack(`params`, `requestInit?`)
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](/sdk/uploads) and pass the new CIDs in `metadata`.
Example:
```ts
const { data } = await audiusSdk.tracks.updateTrack({
trackId: 'h5pJ3Bz',
userId: '7eP5n',
metadata: {
description: 'Dedicated to my favorite plant... updated!',
mood: 'Yearning',
},
})
````
##### 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? |
| :--------- | :-------------------------------------------------- | :---------------------------------------- | :----------- |
| `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** |
All fields are optional — only include the fields you want to change. See
[`UpdateTrackRequestBody`](#updatetrackrequestbody) for all available fields.
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### deleteTrack
> ##### deleteTrack(`params`, `requestInit?`)
Delete a track
Example:
```ts
await audiusSdk.tracks.deleteTrack({
trackId: 'h5pJ3Bz',
userId: '7eP5n',
})
```
##### 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? |
| :-------- | :------- | :------------------ | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### favoriteTrack
> ##### favoriteTrack(`params`, `requestInit?`)
Favorite a track
Example:
```ts
await audiusSdk.tracks.favoriteTrack({
trackId: 'x5pJ3Az'
userId: "7eP5n",
});
```
##### 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? |
| :--------- | :--------------------- | :------------------------------------------------------------------- | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
| `metadata` | *see code block below* | Set `isSaveOfRepost` to true if you are favoriting a reposted track. | *Optional* |
```json title="favoriteTrack metadata payload"
{
isSaveOfRepost: boolean
}
```
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### unfavoriteTrack
> ##### unfavoriteTrack(`params`, `requestInit?`)
Unfavorite a track
Example:
```ts
await audiusSdk.tracks.unfavoriteTrack({
trackId: 'x5pJ3Az'
userId: "7eP5n",
});
```
##### 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? |
| :-------- | :------- | :------------------ | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### repostTrack
> ##### repostTrack(`params`, `requestInit?`)
Repost a track
Example:
```ts
await audiusSdk.tracks.repostTrack({
trackId: 'x5pJ3Az'
userId: "7eP5n",
});
```
##### 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? |
| :--------- | :--------------------- | :-------------------------------------------------------------------- | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
| `metadata` | *see code block below* | Set `isRepostOfRepost` to true if you are reposting a reposted track. | *Optional* |
```json title="repostTrack metadata payload"
{
isRepostOfRepost: boolean
}
```
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### unrepostTrack
> ##### unrepostTrack(`params`, `requestInit?`)
Unrepost a track
Example:
```ts
await audiusSdk.tracks.unrepostTrack({
trackId: 'x5pJ3Az',
userId: '7eP5n',
})
```
##### 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? |
| :-------- | :------- | :------------------ | :----------- |
| `trackId` | `string` | The ID of the track | **Required** |
| `userId` | `string` | The ID of the user | **Required** |
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
Returns a `Promise` resolving to an object with the following fields:
```ts
{
transactionHash?: string
}
```
***
### Type Reference
#### TrackResponse
| Field | Type | Description |
| :------------------- | :----------------------- | :-------------------------------------------------------- |
| `artwork` | `object` | Artwork images (see below) |
| `artwork._1000x1000` | `string` | URL of 1000x1000 artwork |
| `artwork._150x150` | `string` | URL of 150x150 artwork |
| `artwork._480x480` | `string` | URL of 480x480 artwork |
| `description` | `string` | Track description |
| `downloadable` | `boolean` | Whether the track is downloadable |
| `duration` | `number` | Track duration in seconds |
| `favoriteCount` | `number` | Number of favorites |
| `genre` | [`Genre`](#genre-values) | Track genre |
| `id` | `string` | Track ID |
| `isStreamable` | `string` | Whether the track is streamable |
| `mood` | [`Mood`](#mood-values) | Track mood |
| `permalink` | `string` | Permalink URL |
| `playCount` | `number` | Number of plays |
| `releaseDate` | `string` | Release date |
| `remixOf` | `object` | Remix parent info (`tracks: { parentTrackId: string }[]`) |
| `repostCount` | `number` | Number of reposts |
| `tags` | `string[]` | Array of tags |
| `title` | `string` | Track title |
| `trackCid` | `string` | CID of the track audio |
| `user` | `object` | The track owner (see [User](users)) |
***
#### 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`](#genre-values) | 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`](#mood-values) | 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* |
| `coverArtSizes` | `string` | CID of the cover art image (from Uploads API) | *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`](#genre-values) | Track genre |
| `description` | `string` | Track description |
| `mood` | [`Mood`](#mood-values) | 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 |
| `coverArtSizes` | `string` | CID of the cover art image (from Uploads API) |
| `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: '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: '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
}
```
***
#### Genre Values
Valid genre values for the `genre` field. Use these exact string values:
`'Electronic'`, `'Rock'`, `'Metal'`, `'Alternative'`, `'Hip-Hop/Rap'`, `'Experimental'`, `'Punk'`,
`'Folk'`, `'Pop'`, `'Ambient'`, `'Soundtrack'`, `'World'`, `'Jazz'`, `'Acoustic'`, `'Funk'`,
`'R&B/Soul'`, `'Devotional'`, `'Classical'`, `'Reggae'`, `'Podcasts'`, `'Country'`, `'Spoken Word'`,
`'Comedy'`, `'Blues'`, `'Kids'`, `'Audiobooks'`, `'Latin'`, `'Lo-Fi'`, `'Hyperpop'`, `'Dancehall'`,
`'Techno'`, `'Trap'`, `'House'`, `'Tech House'`, `'Deep House'`, `'Disco'`, `'Electro'`, `'Jungle'`,
`'Progressive House'`, `'Hardstyle'`, `'Glitch Hop'`, `'Trance'`, `'Future Bass'`, `'Future House'`,
`'Tropical House'`, `'Downtempo'`, `'Drum & Bass'`, `'Dubstep'`, `'Jersey Club'`, `'Vaporwave'`,
`'Moombahton'`
***
#### Mood Values
Valid mood values for the `mood` field. Use these exact string values:
`'Peaceful'`, `'Romantic'`, `'Sentimental'`, `'Tender'`, `'Easygoing'`, `'Yearning'`,
`'Sophisticated'`, `'Sensual'`, `'Cool'`, `'Gritty'`, `'Melancholy'`, `'Serious'`, `'Brooding'`,
`'Fiery'`, `'Defiant'`, `'Aggressive'`, `'Rowdy'`, `'Excited'`, `'Energizing'`, `'Empowering'`,
`'Stirring'`, `'Upbeat'`, `'Other'`
import { Tabs } from '../../components/Tabs.jsx'
import { TabItem } from '../../components/Tabs.jsx'
***
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`](/sdk/tracks#createtrack) or
[`updateTrack`](/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 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: 'Metal',
mood: 'Aggressive',
...audioResult,
coverArtSizes: coverArtCid,
},
})
```
import { Tabs } from '../../components/Tabs.jsx'
import { TabItem } from '../../components/Tabs.jsx'
#### getUser
##### getUser(`params`)
Get a user.
Example:
```ts
const { data: user } = await audiusSdk.users.getUser({
id: 'eAZl3',
})
console.log(user)
```
##### 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` contains information about the
user as described below.
```ts
{
albumCount: number;
artistPickTrackId?: string;
bio?: string;
coverPhoto?: {
_2000?: string;
_640?: string;
};
doesFollowCurrentUser?: boolean;
ercWallet: string;
followeeCount: number;
followerCount: number;
handle: string;
id: string;
isAvailable: boolean;
isDeactivated: boolean;
isVerified: boolean;
location?: string;
name: string;
playlistCount: number;
profilePicture?: {
_1000x1000?: string;
_150x150?: string;
_480x480?: string;
};
repostCount: number;
splWallet: string;
supporterCount: number;
supportingCount: number;
totalAudioBalance: number;
trackCount: number;
};
```
***
#### getBulkUsers
##### getBulkUsers(`params`)
Gets a list of users.
Example:
```ts
const { data: users } = await audiusSdk.users.getBulkUsers({
id: ['eAZl3', 'v7O9O'],
})
console.log(users)
```
##### 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 IDs of the users | *Optional* |
| `userId` | `string` | The ID of the user making the request | *Optional* |
##### Returns
Returns an array of users, see [`getUser`](#getuser).
***
#### getAIAttributedTracksByUserHandle
##### getAIAttributedTracksByUserHandle(`params`)
Get the AI generated tracks attributed to a user using the user's handle.
Example:
```ts
const { data: tracks } = await audiusSdk.users.getAIAttributedTracksByUserHandle({
handle: 'skrillex',
})
console.log(tracks)
```
##### 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? |
| :-------------- | :------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | :----------- |
| `handle` | `string` | The handle of the user | **Required** |
| `limit` | `number` | The maximum number of tracks to return. Default value is **10** | *Optional* |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | *Optional* |
| `filterTracks` | `GetAIAttributedTracksByUserHandleFilterTracksEnum` (can be imported from `@audius/sdk`) | A filter to apply to the returned tracks. Default value is **GetAIAttributableTracksByUserHandleFilterTracksEnum.All** | *Optional* |
| `query` | `string` | A query to search for in a user's tracks | *Optional* |
| `sortDirection` | `GetAIAttributedTracksByUserHandleSortDirectionEnum` (can be imported from `@audius/sdk`) | A sort direction to apply to the returned tracks. Default value is **GetAIAttributableTracksByUserHandleSortDirectionEnum.Asc** | *Optional* |
| `sortMethod` | `GetAIAttributedTracksByUserTracksByUserHandleSortMethodEnum` (can be imported from `@audius/sdk`) | A sort method to apply to the returned tracks | *Optional* |
##### Returns
The return type is the same as [`getBulkTracks`](tracks#getbulktracks)
***
#### getAuthorizedApps
##### getAuthorizedApps(`params`)
Get the apps that a user has authorized to write to their account.
Example:
```ts
const { data: apps } = await audiusSdk.users.getAuthorizedApps({
id: 'eAZl3',
})
console.log(apps)
```
##### 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 authorized apps as described below.
```ts
{
address: string;
description?: string;
grantCreatedAt: string;
grantUpdatedAt: string;
grantorUserId: string;
name: string;
}[];
```
***
#### getConnectedWallets
##### getConnectedWallets(`params`)
Get a user's connected ERC and SPL wallets.
Example:
```ts
const { data: wallets } = await audiusSdk.users.getConnectedWallets({
id: 'eAZl3',
})
console.log(wallets)
```
##### 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 object containing
information about the wallets as described below.
```ts
{
ercWallets: string[];
splWallets: string[];
};
```
***
#### getFavorites
##### getFavorites(`params`)
Get a user's favorites.
Example:
```ts
const { data: favorites } = await audiusSdk.users.getFavorites({
id: 'eAZl3',
})
console.log(favorites)
```
##### 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 favorites as described below.
```ts
{
createdAt: string
favoriteItemId: string // The ID of the track, playlist, or album
favoriteType: string // The type of favorite ("track", "playlist", or "album")
userId: string
}
;[]
```
***
#### getFollowers
##### getFollowers(`params`)
Get a user's followers
Example:
```ts
const { data: followers } = await audiusSdk.users.getFollowers({
id: 'eAZl3',
})
console.log(followers)
```
##### 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** |
| `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
Returns a `Promise` containing an object with a `data` field. `data` is an array of items containing
information about the followers as described below.
```ts
{
albumCount: number;
artistPickTrackId?: string;
bio?: string;
coverPhoto?: {
_2000?: string;
_640?: string;
};
doesFollowCurrentUser?: boolean;
ercWallet: string;
followeeCount: number;
followerCount: number;
handle: string;
id: string;
isAvailable: boolean;
isDeactivated: boolean;
isVerified: boolean;
location?: string;
name: string;
playlistCount: number;
profilePicture?: {
_1000x1000?: string;
_150x150?: string;
_480x480?: string;
};
repostCount: number;
splWallet: string;
supporterCount: number;
supportingCount: number;
totalAudioBalance: number;
trackCount: number;
}[];
```
***
#### getFollowing
##### getFollowing(`params`)
Get users that a user is following
Example:
```ts
const { data: following } = await audiusSdk.users.getFollowing({
id: 'eAZl3',
})
console.log(following)
```
##### 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** |
| `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
The return type is the same as [`getFollowers`](#getfollowers)
***
#### getRelatedUsers
##### getRelatedUsers(`params`)
Get a list of users that might be of interest to followers of this user.
Example:
```ts
const { data: relatedUsers } = await audiusSdk.users.getRelatedUsers({
id: 'eAZl3',
})
console.log(relatedUsers)
```
##### 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** |
| `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
The return type is the same as [`getFollowers`](#getfollowers)
***
##### getReposts(`params`)
Get a user's reposts.
Example:
```ts
const { data: reposts } = await audiusSdk.users.getReposts({
id: 'eAZl3',
})
console.log(reposts)
```
##### 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** |
| `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
Returns a `Promise` containing an object with a `data` field. `data` is an array of items containing
information about the reposts as described below.
Return type:
```ts
{
item?: {
id: string;
}; // The entire item is returned, always contains id
itemType?: string; // The type of the item ("track", "playlist", or "album")
timestamp?: string;
}[];
```
***
#### getSubscribers
##### getSubscribers(`params`)
Get users that are subscribed to a user.
Example:
```ts
const { data: subscribers } = await audiusSdk.users.getSubscribers({
id: 'eAZl3',
})
console.log(subscribers)
```
##### 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** |
| `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
The return type is the same as [`getFollowers`](#getfollowers)
***
#### getSupporters
##### getSupporters(`params`)
Get users that are supporting a user (they have sent them a tip).
Example:
```ts
const { data: supporters } = await audiusSdk.users.getSupporters({
id: 'eAZl3',
})
console.log(supporters)
```
##### 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** |
| `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
Returns a `Promise` containing an object with a `data` field. `data` is an array of items containing
information about the supporters as described below.
```ts
{
amount: string;
rank: number;
sender: {
albumCount: number;
artistPickTrackId?: string;
bio?: string;
coverPhoto?: {
_2000?: string;
_640?: string;
};
doesFollowCurrentUser?: boolean;
ercWallet: string;
followeeCount: number;
followerCount: number;
handle: string;
id: string;
isAvailable: boolean;
isDeactivated: boolean;
isVerified: boolean;
location?: string;
name: string;
playlistCount: number;
profilePicture?: {
_1000x1000?: string;
_150x150?: string;
_480x480?: string;
};
repostCount: number;
splWallet: string;
supporterCount: number;
supportingCount: number;
totalAudioBalance: number;
trackCount: number;
};
}[];
```
***
#### getSupportedUsers
##### getSupportedUsers(`params`)
Get users that a user is supporting (they have sent them a tip).
Example:
```ts
const { data: supportedUsers } = await audiusSdk.users.getSupportedUsers({
id: 'eAZl3',
})
console.log(supportedUsers)
```
##### 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** |
| `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 supported users as described below.
```ts
{
amount: string;
rank: number;
receiver: {
albumCount: number;
artistPickTrackId?: string;
bio?: string;
coverPhoto?: {
_2000?: string;
_640?: string;
};
doesFollowCurrentUser?: boolean;
ercWallet: string;
followeeCount: number;
followerCount: number;
handle: string;
id: string;
isAvailable: boolean;
isDeactivated: boolean;
isVerified: boolean;
location?: string;
name: string;
playlistCount: number;
profilePicture?: {
_1000x1000?: string;
_150x150?: string;
_480x480?: string;
};
repostCount: number;
splWallet: string;
supporterCount: number;
supportingCount: number;
totalAudioBalance: number;
trackCount: number;
};
}[];
```
***
#### getTopTrackTags
##### getTopTrackTags(`params`)
Get the most used track tags by a user.
Example:
```ts
const { data: tags } = await audiusSdk.users.getTopTrackTags({
id: 'eAZl3',
})
console.log(tags)
```
##### 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** |
| `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
Returns a `Promise` containing an object with a `data` field. `data` is an array of strings
representing the tags
```ts
string[]
```
***
#### getTracksByUser
##### getTracksByUser(`params`)
Get a user's tracks.
Example:
```ts
const { data: tracks } = await audiusSdk.users.getTracksByUser({
id: 'eAZl3',
})
console.log(tracks)
```
##### 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** |
| `limit` | `number` | The maximum number of tracks to return. Default value is **10** | *Optional* |
| `offset` | `number` | The offset to apply to the list of results. Default value is **0** | *Optional* |
| `filterTracks` | `GetTracksByUserFilterTracksEnum` (can be imported from `@audius/sdk`) | A filter to apply to the returned tracks. Default value is **GetTracksByUserFilterTracksEnum.All** | *Optional* |
| `query` | `string` | A query to search for in a user's tracks | *Optional* |
| `sortDirection` | `GetTracksByUserSortDirectionEnum` (can be imported from `@audius/sdk`) | A sort direction to apply to the returned tracks. Default value is **GetTracksByUserSortDirectionEnum.Asc** | *Optional* |
| `sortMethod` | `GetTracksByUserSortMethodEnum` (can be imported from `@audius/sdk`) | A sort method to apply to the returned tracks | *Optional* |
##### Returns
The return type is the same as [`getBulkTracks`](tracks#getbulktracks)
***
#### getUserByHandle
##### getUserByHandle(`params`)
Get a user by their handle.
Example:
```ts
const { data: user } = await audiusSdk.users.getUserByHandle({
handle: 'skrillex',
})
console.log(user)
```
##### 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? |
| :------- | :------- | :------------------------------------ | :----------- |
| `handle` | `string` | The handle of the user | **Required** |
| `userId` | `string` | The ID of the user making the request | *Optional* |
##### Returns
The return type is the same as [`getUser`](#getuser)
***
#### searchUsers
##### searchUsers(`params`)
Search for users.
Example:
```ts
const { data: users } = await audiusSdk.users.searchUsers({
query: 'skrillex',
})
console.log(users)
```
##### 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? |
| :----------- | :------------------------------- | :---------------------------------------------------- | :--------- |
| `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` | [`Genre[]`](tracks#genre-values) | 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
The return type is the same as [`getFollowers`](#getfollowers)
***
#### updateUser
##### updateUser(`params`, `requestInit?`)
Update a user profile. To update a profile picture or cover photo, first upload the image via the
[Uploads API](/sdk/uploads) and pass the resulting CID in `metadata`.
Example:
```ts
import fs from 'fs'
// Upload a new profile picture
const profilePicBuffer = fs.readFileSync('path/to/profile-pic.png')
const profilePicUpload = audiusSdk.uploads.createImageUpload({
file: {
buffer: Buffer.from(profilePicBuffer),
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',
profilePicture: profilePicCid,
},
})
```
##### `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** |
| `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
Returns a `Promise` containing an object with the block hash (`blockHash`) and block number
(`blockNumber`) for the transaction.
```ts
{
blockHash: string
blockNumber: number
}
```
***
#### followUser
##### followUser(`params`, `requestInit?`)
Follow a user.
Example:
```ts
await audiusSdk.users.followUser({
userId: '7eP5n',
followeeUserId: '2kN2a', // User id to follow
})
```
##### `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? |
| :--------------- | :------- | :--------------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `followeeUserId` | `string` | The ID of the user to follow | **Required** |
> ##### `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
Returns a `Promise` containing an object with the block hash (`blockHash`) and block number
(`blockNumber`) for the transaction.
```ts
{
blockHash: string
blockNumber: number
}
```
***
#### unfollowUser
##### unfollowUser(`params`, `requestInit?`)
Unfollow a user.
Example:
```ts
await audiusSdk.users.unfollowUser({
userId: '7eP5n',
followeeUserId: '2kN2a', // User id to unfollow
})
```
##### `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? |
| :--------------- | :------- | :----------------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `followeeUserId` | `string` | The ID of the user to unfollow | **Required** |
> ##### `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
Returns a `Promise` containing an object with the block hash (`blockHash`) and block number
(`blockNumber`) for the transaction.
```ts
{
blockHash: string
blockNumber: number
}
```
***
#### subscribeToUser
##### subscribeToUser(`params`, `requestInit?`)
Subscribe to a user.
Example:
```ts
await audiusSdk.users.subscribeToUser({
userId: '7eP5n',
subscribeeUserId: '2kN2a', // User id to subscribe to
})
```
##### `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? |
| :----------------- | :------- | :--------------------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `subscribeeUserId` | `string` | The ID of the user to subscribe to | **Required** |
> ##### `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
Returns a `Promise` containing an object with the block hash (`blockHash`) and block number
(`blockNumber`) for the transaction.
```ts
{
blockHash: string
blockNumber: number
}
```
***
#### unsubscribeFromUser
##### unsubscribeFromUser(`params`, `requestInit?`)
Unsubscribe from a user.
Example:
```ts
await audiusSdk.users.unsubscribeFromUser({
userId: '7eP5n',
subscribeeUserId: '2kN2a', // User id to unsubscribe from
})
```
##### `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? |
| :----------------- | :------- | :------------------------------------- | :----------- |
| `userId` | `string` | The ID of the user | **Required** |
| `subscribeeUserId` | `string` | The ID of the user to unsubscribe from | **Required** |
> ##### `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
Returns a `Promise` containing an object with the block hash (`blockHash`) and block number
(`blockNumber`) for the transaction.
```ts
{
blockHash: string
blockNumber: number
}
```
:::info[Built with Audius]
This Audius Go SDK was built by the community!
***
**Useful Links**
* Check out the [Code on GitHub](https://github.com/alecsavvy/gaudius) for the latest information.
:::
***
### Usage
#### Library
```bash
go get github.com/alecsavvy/gaudius
```
```go
package main
import "github.com/alecsavvy/gaudius"
func main() {
sdk, err := gaudius.NewSdk()
if err != nil {
log.Fatal("sdk init failed: ", err)
}
}
```
#### Examples
```bash
git clone https://github.com/alecsavvy/gaudius.git
make example tx-subscriber
```
:::info[Built with Audius]
This Audius Music Unreal Engine Plugin was built by the community!
***
**Useful Links**
* Download from the
[Unreal Marketplace](https://www.unrealengine.com/marketplace/en-US/product/audius-music)
* Check out the [Code on GitHub](https://github.com/DigiKrafting/Audius_Unreal_Plugin) for the
latest information.
:::
***
### Usage
#### In Editor
Drag the `Audius_Player_Actor` into your level and configure options.
{/* prettier-ignore */}
This example uses port 5173 on localhost.

#### C++ Usage
Add "Audius" to the `PublicDependencyModuleNames` in your `_project_.Build.cs`
```cpp
PublicDependencyModuleNames.AddRange(new string[] {
"Core",
"CoreUObject",
"Engine",
"InputCore",
"HeadMountedDisplay",
"GameplayTags",
"Audius"
});
```
```cpp
#include "Audius_Actor_Base.h"
#include "Kismet/GameplayStatics.h"
```
```cpp
FTransform Audius_Actor_SpawnTransform(FRotator::ZeroRotator, FVector::ZeroVector);
AAudius_Actor_Base* Audius_Actor_Base = Cast(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, AAudius_Actor_Base::StaticClass(), Audius_Actor_SpawnTransform));
if (Audius_Actor_Base != nullptr) {
Audius_Actor_Base->Audius_Actor_Type = EAudius_Actor_Type::Player;
Audius_Actor_Base->Audius_Queue_Ended_Action = EAudius_Queue_Ended_Action::Replay;
Audius_Actor_Base->Audius_Default_Stream = EAudius_Default_Stream::Trending_Underground;
Audius_Actor_Base->Audius_Auto_Play = false;
UGameplayStatics::FinishSpawningActor(Audius_Actor_Base, Audius_Actor_SpawnTransform);
}
```
### Overview
The Audius Ethereum contracts are meant to accomplish the following goals for the Audius protocol:
* Create the Audius token through an ERC-20
* Keep track of different service versions
* Allow service providers to stake and register services to run
* Allow delegation from users holding Audius token to service providers running services on the
network
* Allow network to mint new tokens for stakers and delegators to earn staking rewards
* Enable protocol governance to carry out protocol actions such as slash, and static value updates
:::info
All contracts are built on top of
[OpenZeppelin's Proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies) through
`AudiusAdminUpgradeabilityProxy` which extends `AdminUpgradeabilityProxy`, enabling logic upgrades
to be performed through the [Governance contract](#governance).
:::
***
### Contracts
> [Github Code available here](https://github.com/AudiusProject/apps/tree/main/eth-contracts/contracts)
#### AudiusToken
> This contract defines the Audius Protocol token, `$AUDIO`
The `$AUDIO` token is a ERC-20 token contract with initial supply of 1 billion tokens, each
divisible up to 18 decimal places, and is `Mintable`, `Pausable`, and `Burnable`.
| Mainnet Contract | Sepolia Testnet Contract |
| ----------------------------------------------------------- | ---------------------------------------------------------------- |
| [`0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998`][AudiusToken] | [`0x1376180Ee935AA64A27780F4BE97726Df7B0e2B2`][AudiusToken_test] |
[AudiusToken]: https://etherscan.io/address/0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998#code
[AudiusToken_test]: https://sepolia.etherscan.io/address/0x1376180Ee935AA64A27780F4BE97726Df7B0e2B2
#### ClaimsManager
> This contract is responsible for allocating and minting new tokens as well as managing claim
> rounds.
A claim round is a period of time during which service providers with valid stakes can retrieve the
reward apportioned to them in the network. Claims are processed here and new value transferred to
[Staking](#staking) for the claimer, but the values in both
[ServiceProviderFactory](#serviceproviderfactory) and [DelegateManager](#delegatemanager) are
updated through calls to [DelegateManager](#delegatemanager).
| Mainnet Contract | Sepolia Testnet Contract |
| ------------------------------------------------------------- | ------------------------------------------------------------------ |
| [`0x44617F9dCEd9787C3B06a05B35B4C779a2AA1334`][ClaimsManager] | [`0xcdFFAE230aeDC376478b16f369489A3b450fc2c8`][ClaimsManager_test] |
[ClaimsManager]: https://etherscan.io/address/0x44617F9dCEd9787C3B06a05B35B4C779a2AA1334#code
[ClaimsManager_test]: https://sepolia.etherscan.io/address/0xcdFFAE230aeDC376478b16f369489A3b450fc2c8
#### DelegateManager
> This contract is responsible for tracking delegation state, making claims, and handling slash
> operations.
This contract allows any Audio Token holder to delegate to an existing Node Operator, earning
rewards by providing additional stake the Node Operator while allocating a known percentage of their
rewards to the Node Operator.
This contract manages the proportional distribution of stake between the Node Operator and
delegators.
All claim and slash operations flow through this contract in order to update values tracked outside
of the [Staking contract](#staking) appropriately and maintain consistency between total value
within the [Staking contract](#staking) and value tracked by the
[DelegateManager contract](#delegatemanager) and the
[ServiceProviderFactory contract](#serviceproviderfactory).
| Mainnet Contract | Sepolia Testnet Contract |
| --------------------------------------------------------------- | -------------------------------------------------------------------- |
| [`0x4d7968ebfD390D5E7926Cb3587C39eFf2F9FB225`][DelegateManager] | [`0xDA74d6FfbF268Ac441404f5a61f01103451E8697`][DelegateManager_test] |
[DelegateManager]: https://etherscan.io/address/0x4d7968ebfD390D5E7926Cb3587C39eFf2F9FB225#code
[DelegateManager_test]: https://sepolia.etherscan.io/address/0xDA74d6FfbF268Ac441404f5a61f01103451E8697
#### EthRewardsManager
| Mainnet Contract | Sepolia Testnet Contract |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------- |
| [`0x5aa6B99A2B461bA8E97207740f0A689C5C39C3b0`][EthRewardsManager] | [`0x563483ccD66a49Ca730275F8cf37Dd3E6Da864f1`][EthRewardsManager_test] |
[EthRewardsManager]: https://etherscan.io/address/0x5aa6B99A2B461bA8E97207740f0A689C5C39C3b0#code
[EthRewardsManager_test]: https://sepolia.etherscan.io/address/0x563483ccD66a49Ca730275F8cf37Dd3E6Da864f1
#### Governance
> This contract allows protocol participants to change protocol direction by submitting and voting
> on proposals.
Each proposal represents an executable function call on a contract in the [Registry](#registry).
Once submitted, there is a period of time during which other participants can submit their votes -
`Yes` or `No` - on the proposal.
After the voting period has concluded, the proposal outcome is calculated as a the sum of the stakes
of the `Yes` voters minus the sum of the stakes of the `No` voters.
Any non-negative value results in a successful proposal, at which point the specified function call
is executed, and the proposal is closed.
Only addresses that have staked in [Staking.sol](#staking) and are represented through the
[Registry](#registry) can submit and vote on proposals.
| Mainnet Contract | Sepolia Testnet Contract |
| ---------------------------------------------------------- | --------------------------------------------------------------- |
| [`0x4DEcA517D6817B6510798b7328F2314d3003AbAC`][Governance] | [`0x04973b4416f7e3D62374Ef8b5ABD4a98e4dD401C`][Governance_test] |
[Governance]: https://etherscan.io/address/0x4DEcA517D6817B6510798b7328F2314d3003AbAC#code
[Governance_test]: https://sepolia.etherscan.io/address/0x04973b4416f7e3D62374Ef8b5ABD4a98e4dD401C
#### Registry
Contract through which external clients and Governance interact with the remaining contracts within
the protocol. Each contract is registered using a key with which its address can be queried.
| Mainnet Contract | Sepolia Testnet Contract |
| -------------------------------------------------------- | ------------------------------------------------------------- |
| [`0xd976d3b4f4e22a238c1A736b6612D22f17b6f64C`][Registry] | [`0xc682C2166E11690B64338e11633Cb8Bb60B0D9c0`][Registry_test] |
[Registry]: https://etherscan.io/address/0xd976d3b4f4e22a238c1A736b6612D22f17b6f64C#code
[Registry_test]: https://sepolia.etherscan.io/address/0xc682C2166E11690B64338e11633Cb8Bb60B0D9c0
#### ServiceProviderFactory
> This contract is responsible for tracking Service Provider state within the Audius network.
A service provider is the account associated with a given service endpoint.
Each service provider can increase/decrease stake within dynamic bounds determined by the
combination of endpoints they have registered, accept delegation from other token holders, define a
reward cut for delegation, and continue registering endpoints as necessary.
This contract forwards staking requests to the actual [Staking](#staking) contract but tracks the
amount of stake for the deployer - [Staking](#staking) tracks the sum of delegate stake + deployer
stake.
| Contract | Sepolia Testnet Contract Mainnet |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [`0xD17A9bc90c582249e211a4f4b16721e7f65156c8`][ServiceProviderFactory] | [`0x377BE01aD31360d0DFB16035A4515954395A8185`][ServiceProviderFactory_test] |
[ServiceProviderFactory]: https://etherscan.io/address/0xD17A9bc90c582249e211a4f4b16721e7f65156c8#code
[ServiceProviderFactory_test]: https://sepolia.etherscan.io/address/0x377BE01aD31360d0DFB16035A4515954395A8185
A Note on Terminology
Through out the Smart Contracts that define the Audius protocol, the term "service provider" is used
along with the "service endpoint(s)" that they operate.
More current terminology is "Node Operator" and "Audius Node" respectively.
* **Old**: `Service Providers` operate `service endpoints`
* **New**: `Node Operators` operate `Audius Nodes`
#### ServiceTypeManager
> This contract is responsible for maintaining known `service types`, associated versioning
> information and service type stake requirements within the Audius Protocol.
Service types are used to identify services being registered within the protocol, for example
`creator-node` or `discovery-provider`.
Service type stake requirements enforce a minimum and maximum stake amount for each endpoint of a
given type that service providers register.
| Mainnet Contract | Sepolia Testnet Contract |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| [`0x9EfB0f4F38aFbb4b0984D00C126E97E21b8417C5`][ServiceTypeManager] | [`0x9fd76d2cD48022526F3a164541E6552291F4a862`][ServiceTypeManager_test] |
[ServiceTypeManager]: https://etherscan.io/address/0x9EfB0f4F38aFbb4b0984D00C126E97E21b8417C5#code
[ServiceTypeManager_test]: https://sepolia.etherscan.io/address/0x9fd76d2cD48022526F3a164541E6552291F4a862
A Note on Terminology
Through out the Smart Contracts that define the Audius protocol, the terms `creator-node` and
`discovery-provider` are used to define `service types`.
More current terminology is `content-node` and `discovery-node` are `Audius Node` types
respectively.
* **Old**: `creator-node` and `discovery-provider` are `service types`
* **New**: `content-node` and `discovery-node` are `Audius Node` types.
#### Staking
> This contract manages token staking functions and state across the Audius Protocol
For every service provider address in Audius Protocol, this contract:
* Stores tokens and manages account balances
* Tracks total stake history
* The total stake (represented as the sun of the deployer stake plus the delegate stake)
* Tracks last claim block
| Mainnet Contract | Sepolia Testnet Contract |
| ------------------------------------------------------- | ------------------------------------------------------------ |
| [`0xe6D97B2099F142513be7A2a068bE040656Ae4591`][Staking] | [`0x5bcF21A4D5Bab9B0869B9c55D233f80135C814C6`][Staking_test] |
[Staking]: https://etherscan.io/address/0xe6D97B2099F142513be7A2a068bE040656Ae4591#code
[Staking_test]: https://sepolia.etherscan.io/address/0x5bcF21A4D5Bab9B0869B9c55D233f80135C814C6
#### TrustedNotifierManager
This contract serves as the on chain registry of trusted notifier services. Other services may look
up and adjust their selected trusted notifier accordingly.
| Contract | Sepolia Testnet Contract Mainnet |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [`0x6f08105c8CEef2BC5653640fcdbBE1e7bb519D39`][TrustedNotifierManager] | [`0x71f8D2aC2f63A481d597d2A6cc160787A048525C`][TrustedNotifierManager_test] |
[TrustedNotifierManager]: https://etherscan.io/address/0x6f08105c8CEef2BC5653640fcdbBE1e7bb519D39#code
[TrustedNotifierManager_test]: https://sepolia.etherscan.io/address/0x71f8D2aC2f63A481d597d2A6cc160787A048525C
#### WormholeClient
This contract serves as the interface between the Audius Protocol and
[Wormhole](https://solana.com/ecosystem/wormhole).
| Mainnet Contract | Sepolia Testnet Contract |
| -------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`0x6E7a1F7339bbB62b23D44797b63e4258d283E095`][wormholeclient] | [`0x2Eb3BF862e7a724A151e78CEB1173FB332E174a0`][wormholeclient_test] |
[wormholeclient]: https://etherscan.io/address/0x6E7a1F7339bbB62b23D44797b63e4258d283E095#code
[wormholeclient_test]: https://sepolia.etherscan.io/address/0x2Eb3BF862e7a724A151e78CEB1173FB332E174a0
Links and resources from [audius.co/agents.md](https://audius.co/agents.md).
### Docs & API
* [Docs](https://docs.audius.co)
* [API](https://api.audius.co)
* [API Reference](/api)
* [API Plans (keys)](https://api.audius.co/plans)
* [SDK (npm)](https://www.npmjs.com/package/@audius/sdk)
* [SDK Overview](/sdk)
* [SDK Tracks](/sdk/tracks)
* [SDK Users](/sdk/users)
* [SDK Playlists](/sdk/playlists)
### Guides
* [Create Audius App](/developers/guides/create-audius-app)
* [Log in with Audius](/developers/guides/log-in-with-audius)
* [Image Loading & Mirrors](/developers/guides/image-mirrors)
### Protocol & Tools
* [Open Audio Protocol](https://openaudio.org)
* [Protocol Dashboard](https://dashboard.audius.org)
* [Link Profile](/reference/protocol-dashboard/link-profile) - Connect an Audius account to the Protocol Dashboard
### Developer Resources
* [GitHub Org](https://github.com/audiusproject)
* [Audius (app)](https://audius.co)
* [API Settings](https://audius.co/settings) - Manage Your Apps, API keys
* [skill.md](https://audius.co/skill.md) - SDK setup, API credentials, code snippets
* [llms.txt](https://audius.co/llms.txt) - AI overview
### Open Audio Protocol
* [OAP agents.md](https://openaudio.org/agents.md)
* [OAP skill.md](https://openaudio.org/skill.md)
* [OAP llms.txt](https://openaudio.org/llms.txt)
### Programs
:::info[Testnet on Mainnet?]
Please note that all Audius Protocol Testnet Programs are deployed to Solana Mainnet.
:::
> [Github Code available here](https://github.com/AudiusProject/apps/tree/main/solana-programs)
#### Audius Plays
This program handles recording track play counts on chain. Every time a user listens to a track on
Audius, it is recorded on the Solana blockchain.
| Mainnet Program | Testnet Program |
| ------------------------------------------------------------- | ------------------------------------------------------------------ |
| [`7K3UpbZViPnQDLn2DAM853B9J5GBxd1L1rLHy4KqSmWG`][AudiusPlays] | [`ApR7QbouRviwoE6nLL83omE6GdtM6yUJAsuXw5sPDCQV`][AudiusPlays_test] |
[AudiusPlays]: https://solscan.io/account/7K3UpbZViPnQDLn2DAM853B9J5GBxd1L1rLHy4KqSmWG
[AudiusPlays_test]: https://solscan.io/account/ApR7QbouRviwoE6nLL83omE6GdtM6yUJAsuXw5sPDCQV
* [Code on GitHub](https://github.com/AudiusProject/apps/tree/main/solana-programs/track_listen_count)
#### Claimable Tokens
This program powers the Audis reward system, and allows Solana non-custodial token accounts to be
created for Audius users that are represented by an Ethereum wallet. This program is also referred
to as the "User Bank".
| Mainnet Program | Testnet Program |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------- |
| [`Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ`][ClaimableTokens] | [`2sjQNmUfkV6yKKi4dPR8gWRgtyma5aiymE3aXL2RAZww`][ClaimableTokens_test] |
[ClaimableTokens]: https://solscan.io/account/Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ
[ClaimableTokens_test]: https://solscan.io/account/2sjQNmUfkV6yKKi4dPR8gWRgtyma5aiymE3aXL2RAZww
* [Code on GitHub](https://github.com/AudiusProject/apps/tree/main/solana-programs/claimable-tokens)
#### Payment Router
This program is responsible for distributing any SPL Token across different provided Solana
associated token accounts, with amounts determined by a given percent split.
It is intended to be used with `SPL-AUDIO` and `SPL-USDC`. While payments can be made independently
of the `Payment Router` program, it is designed to improve space-efficiency and usability off-chain.
| Mainnet Program | Testnet Program |
| -------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`paytYpX3LPN98TAeen6bFFeraGSuWnomZmCXjAsoqPa`][PaymentRouter] | [`sp28KA2bTnTA4oSZ3r9tTSKfmiXZtZQHnYYQqWfUyVa`][PaymentRouter_test] |
[PaymentRouter]: https://solscan.io/account/paytYpX3LPN98TAeen6bFFeraGSuWnomZmCXjAsoqPa
[PaymentRouter_test]: https://solscan.io/account/sp28KA2bTnTA4oSZ3r9tTSKfmiXZtZQHnYYQqWfUyVa
* [Code on GitHub](https://github.com/AudiusProject/apps/tree/main/solana-programs/payment-router)
#### Reward Manager
This program allows for Audius users to claim rewards given attestations from Node Operators that
they have successfully completed a challenge.
For example, to claim the “I’ve completed my profile reward,” a user may ask for a set of Discovery
Node Operators to provide a cryptographic proof that they have completed the challenge and submit
that to chain. If the signatures are valid, tokens are dispensed
| Mainnet Program | Testnet Program |
| --------------------------------------------------------------- | -------------------------------------------------------------------- |
| [`DDZDcYdQFEMwcu2Mwo75yGFjJ1mUQyyXLWzhZLEVFcei`][RewardManager] | [`CDpzvz7DfgbF95jSSCHLX3ERkugyfgn9Fw8ypNZ1hfXp`][RewardManager_test] |
[RewardManager]: https://solscan.io/account/DDZDcYdQFEMwcu2Mwo75yGFjJ1mUQyyXLWzhZLEVFcei
[RewardManager_test]: https://solscan.io/account/CDpzvz7DfgbF95jSSCHLX3ERkugyfgn9Fw8ypNZ1hfXp
* [Code on GitHub](https://github.com/AudiusProject/apps/tree/main/solana-programs/reward-manager)
#### Staking Bridge
This program has 2 main functions:
1. Swap `SPL-USDC` tokens to `SPL-AUDIO` tokens via the [Raydium AMM Program](https://raydium.io/).
2. Convert `SPL-AUDIO` tokens to `ERC20-AUDIO` tokens via the Wormhole Token Bridge.
The methods in this program are intentionally permissionless, allowing any user willing to pay
transaction fees to interact.
The methods of the Staking Bridge are independent to reduce price impact of swaps and fees
associated with bridging tokens.
| Mainnet Program | Testnet Program |
| -------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`stkB5DZziVJT1C1VmzvDdRtdWxfs5nwcHViiaNBDK31`][StakingBridge] | [`stkuyR7dTzxV1YnoDo5tfuBmkuKn7zDatimYRDTmQvj`][StakingBridge_test] |
[StakingBridge]: https://solscan.io/account/stkB5DZziVJT1C1VmzvDdRtdWxfs5nwcHViiaNBDK31
[StakingBridge_test]: https://solscan.io/account/stkuyR7dTzxV1YnoDo5tfuBmkuKn7zDatimYRDTmQvj
* [Code on GitHub](https://github.com/AudiusProject/apps/tree/main/solana-programs/staking-bridge)
The Audius Whitepaper lays the theoretical & technical groundwork for how the platform is built and
continues to grow.
* [PDF download](https://whitepaper.audius.co)
* [Blog post](https://blog.audius.co/posts/the-audius-white-paper-a-decentralized-community-owned-music-sharing-protocol)
***
Questions? Ask & engage with the community on:
* [Discord](https://discord.com/invite/audius)
* [Reddit](https://www.reddit.com/r/audius/)
> Help other users identify you by connecting your [Audius][audius-co] account to the [Audius
> Protocol Dashboard][protocol-dashboard].
Once you've linked your Audius account, your Profile Picture and Display Name will be visible to
users throughout the protocol dashboard.
### Connect to Protocol Dashboard
1. Navigate to the [Audius Protocol Dashboard][protocol-dashboard]
2. Click the "Connect Wallet" button on the upper right
{/* prettier-ignore */}
Wallet Connect Button
3. Select your web3 wallet in the wallet selection modal and sign in.
{/* prettier-ignore */}
Wallet Selection Modal
4. By default, a Gravatar style icon will be used to represent the wallet across the Dashboard.
{/* prettier-ignore */}
Protocol Dashboard default profile icon
***
### Connect to Audius Profile
To connect your Audius profile to the Dashboard,
1. Click the "Connect Audius Profile" button in the upper right corner.
{/* prettier-ignore */}
Protocol Dashboard "Connect Audius Profile" button
2. In the modal, confirm your understanding and proceed by clicking the "Connect Profile" button.
{/* prettier-ignore */}
Review and confirm the next step by clicking "Connect Profile" in the modal.
3. In the pop over, enter the credentials of the Audius account you want to connect to your Protocol
Dashboard account and click "Sign In & Authorize App"
Sign in with your Audius account to continue
:::tip[Already signed in?]
If you are already signed in to an Audius account it will be chosen as the default. If you would
like to use a different Audius account, be sure to sign out and sign in with the correct account
before clicking "Authorize App".
If you are not currently signed in to an Audius account, you will be prompted to do so.
:::
4. Your wallet app will present a signature request to confirm the account connection. Sign this
message to complete the link.
{/* prettier-ignore */}
Using MetaMask as an example, sign the request.
5. Complete! Now your Audius account profile image will be shown on the Audius Protocol Dashboard!
{/* prettier-ignore */}
Account icon when an Audius account is connected to the Protocol Dashboard.
{/* prettier-ignore */}
[audius-co]: https://audius.co/
[protocol-dashboard]: https://dashboard.audius.org/
### How Audius Governance Works
Governance is the process by which AUDIO token holders enact change to Audius through on-chain
proposals.
It allows the community to directly shape future iterations of the platform and is the core
principle driving Audius’s decentralized infrastructure.
In this post, we’ll cover how governance works in Audius, and what you can do as an AUDIO holder to
get involved.
***
### Governance Portal
:::tip
The Audius Protocol Dashboard [Governance tab](https://dashboard.audius.org/#/governance) provides a
view of Audius Governance and an interface to vote on proposals with a connected wallet:
:::
Here you can see a list of all `Active` and `Resolved` proposals in chronological order along with
whether they have passed or failed.
Every governance proposal comes with a breakdown of the following parameters:
| Parameter | Description |
| :-----------: | --------------------------------------------------------- |
| `Proposer` | The address responsible for submitting the proposal |
| `Description` | A quick synthesis of what the governance proposal entails |
| `For` | The amount of votes in favor of the proposal |
| `Against` | The amount of votes against the proposal |
> All proposals are subject to 5% of staked $AUDIO quorum and 50% majority.
This means that for a proposal to pass, at least 5% of all staked $AUDIO must vote on the proposal
and more than 50% of the votes must be ‘For’ the proposal.
Today, only those running a node may make a proposal on-chain. In future, the set of permitted
proposers could be expanded in any way the community sees fit.
***
### Governance Process
Effective governance is much more than voting on proposals on-chain, and something that we want to
make even more accessible at Audius.
Here’s a breakdown of Audius’ evolving governance ecosystem, including the tools, processes and
logistics behind AUDIO voting.
```mermaid
flowchart LR
A(Community Feedback)-->B(Forum Post)-->C(Submit to Governance Portal)-->D(On-Chain Vote)-->E(Execute);
```
Please note that some users may be more inclined than others to facilitate the duration of this
process, and we recommend anyone interested in shaping Audius to contribute in whatever ways
possible, even if that means just starting a conversation around a topic on Discord!
#### On-chain Voting
Using Figment’s
[most recent governance proposal](https://dashboard.audius.org/#/governance/proposal/9) as an
example, you can see that different node operators and delegators voted in favor of extending the
voting time from 48 to 72 hours.
Given that the total number of votes (1 AUDIO, 1 vote) was above the quorum requirement of \~11M
$AUDIO and the 50% majority (100% voted in favor) the proposal passed!
In doing so, the changes
[from this proposal](https://etherscan.io/tx/0xd4e14895b2a22b48469a43923ab7b30bee75f9a688941933430b3dae9510b8a6)
were
[executed through the governance contract](https://etherscan.io/tx/0x4396652fb9c1116cec5900f412608dfba7a3ec1b9967f4109a8ec3e09d3a75af),
changing the voting window from 48 hours to 72 hours!
#### Community MultiSig
Once a vote has been passed, the governance contract executes the proposal.
However, Audius also features a community multisig as a veto of last resort, referenced in the
whitepaper in the “short-circuiting” subsection of the governance section.
This means that a set of 9 Audius community members have the ability to stop a malicious proposal
from passing. In the event the multisig is used, 6 of the 9 signers must sign a transaction to
nullify the proposal.
As Audius continues to mature, the community can at any time vote to remove this veto ability from
the system as well.
More details on the signers of this multisig as well as the intent for its use will be shared in a
future blog post.
***
### Evolving Governance
Audius governance is an evolving process geared at giving all $AUDIO holders a voice of future
iterations of the platform.
The process detailed above is likely to change in line with new tools, product upgrades and onramps
to allow for all token users to easily review and participate in governance decisions, regardless of
their technical knowledge.
We’re excited to share more details around governance in the near future and look forward to
building out the community-owned streaming protocol that is Audius!
### Contributing
Audius welcomes contributions of all sizes from the open source community. Check out the open issues
in one of the repos below or reach out on [Discord](https://discord.gg/audius) to get started.
***
### Repositories
| Repository | Description |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| [Protocol](https://github.com/AudiusProject/apps/issues) | Specification of the protocol |
| [Audius Docker Compose](https://github.com/AudiusProject/audius-docker-compose/issues) | Launch and manage Audius services using Docker Compose |
| [Hedgehog](https://github.com/AudiusProject/hedgehog/issues) | Metamask alternative that manages a user's private key and wallet on the browser |
| [React Starter](https://github.com/AudiusProject/audius-sdk-react-starter/issues) | React starter app using the Audius Javascript SDK |
***
### Documentation
This site serves as the central hub for documentation on the protocol. If you have feedback, please
open an issue or create a pull request on [GitHub](https://github.com/AudiusProject/apps)
or use the "Edit this Page" link at the bottom of each page.
***
### Community
[Developer Channel on Discord](https://discord.com/channels/557662127305785361/864882574127398913) -
Public chat for developer support.
Audius is a decentralized, community-owned and artist-controlled music-sharing protocol. Audius
provides a blockchain-based alternative to existing streaming platforms to help artists publish and
monetize their work and distribute it directly to fans.
The mission of the project is to give everyone the freedom to share, monetize, and listen to any
audio.
The Audius Protocol [repository](https://github.com/AudiusProject/apps) is a
mono-repository that has all the pieces that make and support the protocol including smart
contracts, services, and other supporting libraries.
If you are interested in operating a service, see the [`running a node`](https://docs.openaudio.org)
section. If you're interested in contributing to the Audius protocol, explore the code below!
```mermaid
flowchart LR
A(((Artists)))--->|Publish Content|B([Audius Content Ledger])
B--->|Index Content Metadata|D[(Discovery Node)];
D--->|Discover Content|F;
A(((Artists)))--->|Upload Content|C[(Content Node)];
C--->|Stream Content|F(((Fans)));
click C href "/learn/architecture/content-node"
click D href "/learn/architecture/discovery-node"
```
Audius consists of three demographics of users: Artists (content creators), Fans (content
consumers), and Node Operators. Some users check fall into all three demographics!
* **Artists** upload tracks, create albums, and share content to their following
* **Fans** stream tracks, create playlists, subscribe to & follow artists, and re-share content to
their following
* **Node Operators** serve app traffic, stream songs, and help secure the network
Node Operators can provide one or more of the following services by staking $AUDIO tokens and
registering their service:
* Discovery node (host an endpoint with SSL support and register endpoint with stake)
* Content node (host an endpoint with SSL support and register endpoint with stake)
In the above diagram, creators can either run a content node themselves or use one of the
network-registered content nodes.
For more details on the Audius architecture, see the
[Audius protocol whitepaper](/reference/whitepaper).
***
### Audius Services
| Service | Description | GitHub |
| :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------ |
| [Content Node](/learn/architecture/content-node) | Maintains the availability of users' content on IPFS including user metadata, images, and audio content | [Link](https://github.com/AudiusProject/audius-docker-compose/tree/main/creator-node) |
| [Discovery Node](/learn/architecture/discovery-node) | Indexes and stores the contents of the Audius contracts on the Ethereum blockchain for clients to query via an API | [Link](https://github.com/AudiusProject/audius-docker-compose/tree/main/discovery-provider) |
| Identity Service | Stores encrypted auth ciphertexts, does Twitter OAuth and relays transactions (pays gas) on behalf of users | [Link](https://github.com/AudiusProject/audius-docker-compose/tree/main/identity-service) |
***
### Audius Smart Contracts & Libs
| Lib | Description |
| :------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| [`libs`](https://github.com/AudiusProject/apps/tree/main/packages/libs) | An easy interface to the distributed web and Audius services: Identity Service, Discovery Node (discovery provider), Content Node (creator node) |
| [`contracts`](https://github.com/AudiusProject/apps/tree/main/contracts) | The smart contracts being developed for the Audius streaming protocol |
| [`eth-contracts`](https://github.com/AudiusProject/apps/tree/main/eth-contracts) | The Ethereum smart contracts being developed for the Audius streaming protocol |
***
### Node Operators Quickstart
A quick start guide to running Nodes on Audius can be found [here](https://docs.openaudio.org)
### What is Staking?
Built as a decentralized protocol on Ethereum, all the content, information and data on Audius is
stored and indexed by a growing network of third-party Node Operators.
To ensure this content can be trusted and maintained, Node Operators are required to provide
collateral or "stake" as a bond to service the protocol. This stake, denominated in $AUDIO, ensures
that Node Operators have tokens at risk that can be slashed, or taken, in the event of malicious or
poor behavior.
:::info[More Information]
Ready to learn more, check out the [Staking section](https://docs.openaudio.org) of the docs.
:::
***
### What is Delegating?
For users that either do not hold enough $AUDIO to self stake a Node, do not want to operate a Node,
or are just looking to get started, Delegation is a great place to get involved.
Delegating tokens earns rewards, and increases your ownership of the protocol while supporting Node
Operators, assisting in keeping them up and running, which in turns keeps the Audius protocol
healthy.
:::info[More Information]
Ready to learn more, check out the [Delegating section](https://docs.openaudio.org) of the
docs.
:::
### How it Works
Offering a native token to align all actors creates a parallel incentive unique to web3, and one
which allows our early adopters to share in the upside of Audius as we continue to grow.
The Audius platform token $AUDIO has three prongs of functionality within the Audius protocol
ecosystem:
```mermaid
flowchart LR
token((Audius Token Utility));
token--->P1[Security ]
token--->P2[Feature Access ]
token--->P3[Governance ]
```
$AUDIO is staked as collateral for a value-added service such as
[operating a Node](https://docs.openaudio.org) or participating in governance.
In exchange, Stakers earn ongoing issuance, governance weight, and access to exclusive features.
In the future, $AUDIO will govern a global fee pool from value transfers in the network.
***
### Security
$AUDIO is staked by Node Operators to secure the network. The larger the stake, the higher the
probability of their node being used by fans and artists.
Audius is entirely hosted and operated by the community, creating a permissionless ecosystem of Node
Operators securing content for the world’s unstoppable streaming protocol.
***
### Feature Access
$AUDIO serves as collateral to unlock additional artist tooling. Early examples incubated by the
community include artists tokens, badges, and earnings multipliers.
In the future, Fans may delegate $AUDIO to specific Artists and curators to share in their growth on
the platform.
***
### Governance
Staking $AUDIO gives users governance weight to influence the future of the protocol, with each
token staked equaling one vote.
Every aspect of Audius is governable, aiming to involve even passive fans to voice their opinion
over product updates and feature upgrades.
Ongoing issuance aligns power with the most active platform users, ensuring $AUDIO tokens are always
being funneled to value-added actors. This distribution method, using on-chain metrics, directs
issuance to active participants, rather than just the largest stakers.
An Audius Content Node is a service that stores and maintains the availability of all content across
the Audius network. Content types include user, track, and playlist metadata, images and artwork,
and audio content.
The Content Node source code is hosted on
[GitHub](https://github.com/AudiusProject/audius-docker-compose/tree/main/creator-node) and see the
[registered Content Nodes](https://dashboard.audius.org/#/services/content-node) on the Audius
Protocol Dashboard.
***
### Design Goals
1. Surface the Audius Storage Protocol for storing and serving images and audio
2. Keep data consistently replicated and available
3. Provide an interface to handle content upload, transcoding, and identification
4. Allow users to maintain agency over where and how their data is stored amongst Content Nodes
:::note[Legacy Terminology]
The "Content Node" may be referred to as the "Creator Node". These services are the same.
:::
***
### Web Server
The Content Node core service is a web server with an HTTP API to process incoming requests and
perform the following functions:
* user & track metadata upload
* user & track image upload
* user track file upload
* user & track data, metadata, and track file retrieval
The web server is a [NodeJS](https://nodejs.org) [Express app](https://expressjs.com/).
***
### Persistent Storage
It stores all data in a PostgreSQL database and all images and metadata objects on its file system.
Pointers to all content and metadata stored on disk are persisted in the Postgres DB.
Postgres is managed in the codebase using the [Sequelize ORM](https://sequelize.org/main/) which
includes migrations, models and validations
***
### Redis
A [Redis client](https://redis.io/) is used for resource locking, request rate limiting, and
limited caching and key storage.
Redis is managed in the codebase through the [ioredis npm package](https://github.com/luin/ioredis)
***
### Track Segmenting
As defined by the [Audius Whitepaper](/reference/whitepaper), the content node uses
[FFMPEG](https://ffmpeg.org/ffmpeg.html) to segment & transcode all uploaded track files before
storing/serving.
***
### Data Redundancy
As defined by the [Audius Whitepaper](/reference/whitepaper), all content is stored redundantly
across multiple Nodes to maximize availability. This is all done automatically - every Node monitors
every other Node in the network to ensure minimum redundancy of all data, transferring files as
required.
An Audius Discovery Node is a service that indexes the metadata and availability of data across the
protocol for Audius users to query. The indexed content includes user, track, and album/playlist
information along with social features. The data is stored for quick access, updated on a regular
interval, and made available for clients via a [RESTful API](/api).
The Discovery Node source code is hosted on
[GitHub](https://github.com/AudiusProject/audius-docker-compose/tree/main/discovery-provider) and
see the [registered Discovery Nodes](https://dashboard.audius.org/#/services/discovery-node) on the
Audius Protocol Dashboard.
***
### Design Goals
1. Expose queryable endpoints which listeners/creators can interact with
2. Reliably store relevant blockchain events
3. Continuously monitor the blockchain and ensure stored data is up to date with the network
:::note[Legacy Terminology]
The "Discovery Node" may be referred to as the "Discovery Provider". These services are the same.
:::
***
### Database
{/* TODO: many of these GitHub links are broken */}
The Discovery Node uses PostgreSQL. Our Postgres database is managed through
[SQLAlchemy](https://www.sqlalchemy.org/), an object relational mapper and
[Alembic](http://alembic.zzzcomputing.com/en/latest/index.html), a lightweight database migration
tool. The data models are defined in
[src/models](https://github.com/AudiusProject/apps/blob/main/packages/discovery-provider/src/models)
which is used by alembic to automatically generate the migrations under
[alembic/versions](https://github.com/AudiusProject/apps/tree/main/packages/discovery-provider/alembic/versions).
You can find the connection defined between alembic and the data models in
[alembic/env.py](https://github.com/AudiusProject/apps/tree/main/packages/discovery-provider/alembic/env.py)
***
### Flask
The Discovery Node web server serves as the entry point for reading data through the Audius
Protocol. All queries are returned as JSON objects parsed from SQLAlchemy query results, and can be
found in
[src/queries](https://github.com/AudiusProject/apps/tree/main/packages/discovery-provider/src/queries).
Some examples of queries include user-specific feeds, track data, playlist data, etc.
***
### Celery
Celery is simply a task queue - it allows us to define a set of single tasks repeated throughout the
lifetime of the Discovery Node.
Currently, a single task `(src/tasks/index.py:update_task()`) handles all database write operations.
The Flask application reads from the database and is unaware of data correctness.
Celery [worker](https://docs.celeryq.dev/en/stable/reference/celery.worker.html) and
[beat](https://docs.celeryq.dev/en/stable/reference/celery.beat.html) are the key underlying
concepts behind Celery usage in the Discovery Node.
#### Celery Worker
Celery worker is the component that actually runs tasks.
The primary driver of data availability on Audius is the 'index\_blocks' Celery task. What happens
when 'index\_blocks' is actually executed? The Celery task does the following operations:
1. Check whether the latest block is different than the last processed block in the ‘blocks’ table.
If so, an array of blocks is generated from the last blockhash present in the database up to the
latest block number specified by the block indexing window.
Block indexing window is equivalent to the maximum number of blocks to be processed in a single
indexing operation
2. Traverse over each block in the block array produced after the above step.
In each block, check if any transactions relevant to the Audius Smart Contracts are present. If
present, we retrieve specific event information from the associated transaction hash, examples
include `creator` and `track` metadata.
To do so, the Discovery Node *must* be aware of both the contract ABIs as well as each contract's
address - these are shipped with each Discovery Node image.
3. Given operations from Audius contracts in a given block, the task updates the corresponding table
in the database.
{/* TODO: Audius Storage Protocol link? */}
Certain index operations require a metadata fetch from decentralized storage (Audius Storage
Protocol). Metadata formats can be found
[here](https://github.com/AudiusProject/apps/blob/main/packages/discovery-provider/src/tasks/metadata.py).
:::info[Why index blocks instead of using event filters?]
This is a great question - the main reason chosen to index blocks in this manner is to handle cases
of false progress and rollback. Each indexing task opens a fresh database session, which means
database transactions can be reverted at a block level - while rollback handling for the Discovery
Node has yet to be implemented, block-level indexing will be immediately useful when it becomes
necessary.
:::
#### Celery Beat
Celery beat is responsible for periodically scheduling index tasks and is run as a separate
container from the worker. Details about periodic task scheduling can be found in the
[official documentation](http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html).
This is an identical container as the [Celery worker](#celery-worker) but is run as a 'beat
scheduler' to ensure indexing is run at a periodic interval. By default this interval is
`5 seconds`.
***
### Redis
A [Redis client](https://redis.io/) is used for several things in the Discovery Node.
1. Caching (internally and externally)
2. As a
[broker for Celery](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html)
3. As a mechanism for locking to ensure single execution contexts for Celery jobs
### Elastic Search
Elastic Search is used to denormalize data and supports certain queries (Feed, Search, Related
Artists, etc.). Elastic Search data is populated and kept up to date by database triggers that live
on the Postgres database.
ETL code for the Elastic Search layer is found in the
[es-indexer](https://github.com/AudiusProject/apps/tree/main/packages/discovery-provider/es-indexer).
{/* TODO: update es-indexer link */}
:::info[ERN Versioning]
The following is provided based on [ERN3.8](/distributors/specification/deal-types/recommended)
Please note that exact DDEX fields will depend on the specific ERN version.
:::
***
### General Metadata Mapping
#### Required Fields
The following metadata fields are required for content to be listed on Audius and are examined and
pulled from a DDEX delivery in cascading precedence.
##### imageFile
:::note
The `imageFile` maps to two DDEX Fields
:::
* `/ResourceList/Image/ImageDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalImageDetails/File/FilePath`
* `/ResourceList/Image/ImageDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalImageDetails/File/FileName`
##### releaseDate
:::note
The resource will not be published on the Audius platform until the following condition is met:
`current date ≥ max(releaseDate, validity start date from the corresponding deal)`
However the date displayed in the Audius interface will be this `releaseDate` value (determined by
the hierarchy below).
:::
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/ReleaseDate`
2. `/ReleaseList/Release/GlobalOriginalReleaseDate`
3. `/DealList/ReleaseDeal/Deal/ValidityPeriod/StartDate`
##### userId
:::note
Not technically an Audius SDK metadata field, but uploaded as part of the track/album
:::
1. checks each artist name (in order) against Audius database of OAuthed display names, and uses the
first match
***
#### Optional Fields
The following fields are optional for content to be listed on Audius and are examined and pulled
from a DDEX delivery in cascading precedence.
##### ddexReleaseIds
1. `/ReleaseList/Release/ReleaseId`
:::note
The following fields are parsed and preserved from this field:
`PartyId`, `CatalogNumber`, `ICPN`, `GRid`, `ISAN`, `ISBN`, `ISMN`, `ISRC`, `ISSN`, `ISTC`, `ISWC`,
`MWLI`, `SICI`, and `ProprietaryId`
:::
##### description
The DDEX standard includes a `MarketingComments` field that is rarely used, but is available.
***
#### Unused Fields
The following Audius SDK Fields are not used by DDEX and have no mapping.
##### child elements of `/ReleaseList/Release/`
The following child elements are parsed and stored in the separate DDEX server. Audius does not
store these child fields and they are not used.
* `ReferenceTitle/TitleText`
* `ReferenceTitle/SubTitle`
##### child elements of `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/`
The following child elements are parsed and stored in the separate DDEX server. Audius does not
store these child fields and they are not used.
* `Title[@TitleType='DisplayTitle']/TitleText` (used in albums/EPs but not single tracks)
* `Title[@TitleType='DisplayTitle']/SubTitle`
* `Title[@TitleType='FormalTitle']/TitleText`
* `Title[@TitleType='FormalTitle']/SubTitle`
##### child elements of `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/ResourceGroup/`
The following child elements are parsed and stored in the separate DDEX server. Audius does not
store these child fields and they are not used.
* `SequenceNumber`
* `ResourceGroupContentItem/ResourceType`
* `ResourceGroupContentItem/ReleaseResourceReference`
* `ResourceGroupContentItem/IsInstantGratificationResource`
***
### Track Metadata Mapping
#### Required Fields
The following fields are required for content to be listed on Audius and are examined and pulled
from a DDEX delivery in cascading precedence.
##### genre
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/SubGenre`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/GenreText`
3. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/SubGenre`
4. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/GenreText`
##### title
1. `/ResourceList/SoundRecording/ReferenceTitle/TitleText`
:::note
Subtitle is currently ignored/unused in the both the SoundRecording and release and are stored in
the separate DDEX server but not in the Audius network.
:::
##### audioFile
This is the actual audio file, not technically an Audius SDK metadata field, but uploaded in the
same SDK function. Note that this DDEX field is a relative path to the file within the delivery
:::note
The `audioFile` maps to two DDEX Fields
:::
* `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalSoundRecordingDetails/File/FilePath`
* `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalSoundRecordingDetails/File/FileName`
***
#### Optional Fields
The following fields are optional for content to be listed on Audius and are examined and pulled
from a DDEX delivery in cascading precedence.
##### artists
The `/PartyName/FullName` child element is used as the artist’s name and the `SequenceNumber`
attribute to preserve order
1. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/DisplayArtist`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/DisplayArtist`
##### copyrightLine
This is only used if both `year` *AND* `text` are non-empty. The child elements that are parsed are:
`Year` and `CLineText`
1. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/CLine`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/CLine`
3. `/ReleaseList/Release/CLine`
##### indirectResourceContributors
:::note
The following children elements are parsed and stored in Audius: `PartyName`/`FullName`,
`SequenceNumber`, and `IndirectResourceContributorRole`
:::
1. `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/IndirectResourceContributor`
##### isrc
1. `/ResourceList/SoundRecording/SoundRecordingId/ISRC`
##### iswc
1. `/ReleaseList/Release/ReleaseId/ISWC`
##### parentalWarningType
1. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/ParentalWarningType`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/ParentalWarningType`
##### previewStartSeconds
Preview length is 30 seconds starting at the `previewStartSeconds` into the track’s audio, even if a
longer or shorter duration is given. does not support using an external file for the preview
1. `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalSoundRecordingDetails/PreviewDetails/StartPoint`
2. only when
`/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/TechnicalSoundRecordingDetails/IsPreview`
is true
##### producerCopyrightLine
1. `/ResourceList/SoundRecording/SoundRecordingDetailsByTerritory[TerritoryCode="Worldwide"]/PLine`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/PLine`
3. `/ReleaseList/Release/PLine`
:::note
This is only used if both `year` *AND* `text` are non-empty. The child elements that are parsed are:
`Year` and `CLineText`
:::
##### resourceContributors
1. `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/ResourceContributor`
:::note
The following children elements are parsed and stored in Audius: `PartyName`/`FullName`,
`SequenceNumber`, and `ResourceContributorRole`
:::
##### rightsController
1. `/ResourceList/SoundRecording/SoundDetailsByTerritory[TerritoryCode="Worldwide"]/RightsController`
:::note
The following children elements are parsed and stored in Audius: `PartyName`/`FullName`,
`RightsShareUnknown`, and `RightsControllerRole`
:::
***
### Album Metadata Mapping
#### Required Fields
The following fields are required for content to be listed on Audius and are examined and pulled
from a DDEX delivery in cascading precedence.
##### albumName
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/Title[@TitleType='DisplayTitle']/TitleText`
:::note
Subtitle is currently ignored/unused in the both the SoundRecording and release and are stored in
the separate DDEX server but not in the Audius network.
:::
##### genre
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/SubGenre`
2. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/Genre/GenreText`
##### audioFiles
This is an array of audio files which the album is comprised of, not technically an Audius SDK
metadata field, but uploaded as part of the album.
1. each `/ReleaseList/Release/` except the release with the attribute `IsMainRelease="true"`
##### trackMetadatas
Metadata about each track in the album, not technically an Audius SDK metadata field, but uploaded
as part of the album
1. each `/ReleaseList/Release/` except the release with the attribute `IsMainRelease="true"`
***
#### Optional Fields
The following fields are optional for content to be listed on Audius and are examined and pulled
from a DDEX delivery in cascading precedence.
##### artists
The `/PartyName/FullName` child element is used as the artist’s name and the `SequenceNumber`
attribute to preserve order
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/DisplayArtist`
##### copyrightLine
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/CLine`
2. `/ReleaseList/Release/CLine`
:::note
This is only used if both `year` *AND* `text` are non-empty. The child elements that are parsed are:
`Year` and `CLineText`
:::
##### parentalWarningType
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/ParentalWarningType`
##### producerCopyrightLine
1. `/ReleaseList/Release/ReleaseDetailsByTerritory[TerritoryCode="Worldwide"]/PLine`
2. `/ReleaseList/Release/PLine`
:::note
This is only used if both `year` *AND* `text` are non-empty. The child elements that are parsed are:
`Year` and `PLineText`
:::
##### upc
1. `/ReleaseList/Release/ReleaseId/ICPN`
:::note
ICPN (or "International Code Product Number") has an `IsEAN` attribute which determines if it’s an
EAN (or "European Article Number") or UPC ("Universal Product Code" — only used in US and Canada).
Audius uses these interchangeably and just set it as UPC even if it’s an EAN.
:::
### General Guidance
:::info[Delivery processing]
[TikiLabs](https://tikilabs.com) is a facilitator of distributing DDEX directly to the Audius
Protocol.
For inquiries or support, reach out at
[support@audius.co](mailto\:support@audius.co?subject=DDEX%20Support).
:::
* ERN3 is the preferred DDEX specification for bulk ingestion into Audius. See the
[ERN3 details](/distributors/specification/deal-types/recommended) for more details around
submission choreography.
* Audius only accepts price information using absolute prices (e.g. via `WholesalePricePerUnit`).
Price codes will be ignored (e.g. `PriceType`).
* Audius also does not support `ValidityPeriod` `EndDate`s. This includes using `EndDate` to specify
multiple `ValidityPeriod`s with different prices. We can only parse 1 `ValidityPeriod`
`StartDate`.
* Audius currently only accepts the `Worldwide` `TerritoryCode`. Territory support for controlled
streaming is coming soon! 🌎
* `NonInteractiveStream` is not supported.
* A `ReleaseDeal` is required with a `DealReleaseReference` to each track on an album.
* A `ReleaseDeal` is not required with a `DealReleaseReference` to an album release. If an album
deal is not specified, the album defaults to being free to stream, but each of its tracks is
configured according to the track’s `ReleaseDeal` `DealTerms`.
* Audius supports updates via `NewReleaseMessage` and takedowns of content via `PurgeReleaseMessage`
through matching `ReleaseId` properties.
## Recommended Deal Structure
The following deal types are recommended for distribution to Audius. For complete
documentation on supported deal types and corresponding XML, please see
[Supported Deal Types](/distributors/specification/deal-types/supported-deal-types).
If deals are provided without pricing information, the following standard pricing options
are assumed:
| Release type | Retail Price | Wholesale Price |
| ------------ | ------------ | --------------- |
| Track | $1.00 | $0.90 |
| Album | $5.00 | $4.50 |
:::warning[Note]
Please note, advertisement and subscription model types are not supported.
:::
### 1. Paid Downloads with Full Length Streams
Provide fans with a full-length streaming experience and an option to pay for a download of the
work.
```xml
FreeOfChargeModel
OnDemandStream
Worldwide
2023-09-02
PayAsYouGoModel
PermanentDownload
Worldwide
0.9
2023-09-02
```
### 2. Paid Downloads with Previewed Streams
Provide fans with a previewed stream and a paid option to unlock the content. After purchasing, full-length
streaming and downloads are unlocked.
Default preview length is 30s of the track starting from 0s. If you would like to adjust the
preview, see [Metadata](/distributors/specification/metadata) to provide the specific DDEX choreography.
```xml
PayAsYouGoModel
OnDemandStream
PermanentDownload
Worldwide
0.9
2023-09-02
```
:::info[Further Reading]
Checkout the
[DDEX ERN3 Knowledge Base](https://kb.ddex.net/implementing-each-standard/electronic-release-notification-message-suite-\(ern\)/ern-3-explained/)
for more information.
:::
The following `Deal` types are supported for distribution to Audius. `Deal` types provided outside
of the provided list will be ignored.
If your use case extends beyond the supported `Deal` types outlined below, please contact
`support@audius.co`.
***
### Tracks
Audius accepts the following DDEX `Deal`s for **track** releases:
#### Free To Stream
1. `CommercialModelType`: `FreeOfChargeModel`
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
FreeOfChargeModel
OnDemandStream
Worldwide
2023-09-02
```
#### Pay Gated Stream
1. `CommercialModelType`: `PayAsYouGoModel`
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: any nonzero USD amount
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
PayAsYouGoModel
OnDemandStream
Worldwide
1.0
...
2023-09-02
```
#### Follow Gated Stream
1. `CommercialModelType`: `UserDefined` (`FollowGated`)
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
UserDefined
OnDemandStream
Worldwide
2023-09-02
```
#### NFT Gated Stream
1. `CommercialModelType`: `UserDefined` (`NFTGated`)
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
7. `Conditions` (custom XML specific to Audius)
1. For Ethereum NFTs:
```xml
eth
// The Ethereum address of the NFT contract
// The standard followed by the NFT - either "ERC-721" or "ERC-1155"
// The name of the 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".
// Optional: URL to the image representing the NFT
// Optional: URL to an external resource providing more details about the NFT
```
ii. For Solana NFTs:
```xml
sol
// The address of the NFT on the Solana blockchain
// The name of the NFT
// Optional: URL to the image representing the NFT
// Optional: URL to an external resource providing more details about the NFT
```
```xml
UserDefined
OnDemandStream
Worldwide
2023-09-02
eth
0xAbCdEfGhIjKlMnOpQrStUvWxYz
ERC-721
Example NFT
example-nft
https://www.example.com/nft-image.png
https://www.example.com/nft-details
```
#### $AUDIO Tip Gated Stream
1. `CommercialModelType`: `UserDefined` (`TipGated`)
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
UserDefined
OnDemandStream
Worldwide
2023-09-02
```
#### Free To Download
:::info[Downloadable content is streamable.]
If you can download it, you can stream it.
:::
1. `CommercialModelType`: `FreeOfChargeModel`
2. `UseType`: `Stream` or `OnDemandStream`, `PermanentDownload`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
FreeOfChargeModel
OnDemandStream
PermanentDownload
Worldwide
2023-09-02
```
#### Pay Gated Download
:::info[Downloadable content is streamable.]
If you can download it, you can stream it.
:::
1. `CommercialModelType`: `PayAsYouGoModel`
2. `UseType`: `Stream` or `OnDemandStream`, `PermanentDownload`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: any USD amount
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
PayAsYouGoModel
OnDemandStream
PermanentDownload
Worldwide
1.0
...
2023-09-02
```
#### Follow Gated Download
:::info[Downloadable content is streamable.]
If you can download it, you can stream it.
:::
1. `CommercialModelType`: `UserDefined` (`FollowGated`)
2. `UseType`: `Stream` or `OnDemandStream`, `PermanentDownload`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
UserDefined
OnDemandStream
PermanentDownload
Worldwide
2023-09-02
```
***
### Albums
Audius accepts the following DDEX `Deal`s for **album** releases
#### Free To Stream
1. `CommercialModelType`: `FreeOfChargeModel`
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
FreeOfChargeModel
OnDemandStream
Worldwide
2023-09-02
```
#### Pay Gated Stream
1. `CommercialModelType`: `PayAsYouGoModel`
2. `UseType`: `Stream` or `OnDemandStream`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: any nonzero USD amount
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
PayAsYouGoModel
OnDemandStream
Worldwide
1.0
...
2023-09-02
```
#### Free To Download
:::info[Downloadable content is streamable.]
If you can download it, you can stream it.
:::
1. `CommercialModelType`: `FreeOfChargeModel`
2. `UseType`: `Stream` or `OnDemandStream`, `PermanentDownload`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: N/A
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
FreeOfChargeModel
OnDemandStream
PermanentDownload
Worldwide
2023-09-02
```
#### Pay Gated Download
:::info[Downloadable content is streamable.]
If you can download it, you can stream it.
:::
1. `CommercialModelType`: `PayAsYouGoModel`
2. `UseType`: `Stream` or `OnDemandStream`, `PermanentDownload`
3. `PriceType`: not supported
4. `WholesalePricePerUnit`: any USD amount
5. `ValidityPeriod`
1. `StartDate`: any
6. `TerritoryCode`: `Worldwide`
```xml
PayAsYouGoModel
OnDemandStream
PermanentDownload
Worldwide
1.0
...
2023-09-02
```
***
### ERN4 Support
:::info[Coming Soon]
Support for ERN4 is coming coming soon.
Checkout the
[DDEX ERN4 Knowledge Base](https://kb.ddex.net/implementing-each-standard/electronic-release-notification-message-suite-\(ern\)/ern-4-explained/)
for information in the meantime.
:::
***
:::info
Content can be ingested into the Audius network by directly operating your own DDEX ingestion
server. More details soon.
:::
:::info[Coming Soon!]
Under construction. Please use as your own risk! 🚧
Check back soon for updates on how to run and operate own DDEX Ingestion Server.
:::
### Instructions
1. Provision a new machine following. We recommend:
* Ubuntu
* 8 CPUs
* 16 to 32GB Memory
2. Visit [audius-docker-compose](https://github.com/AudiusProject/audius-docker-compose) and run the
install script to set up your Audius environment on your provisioned machine.
3. Follow the instructions in the
[apps](https://github.com/AudiusProject/apps/tree/main/packages/ddex)
repository to set the appropriate environment variables
4. Start the DDEX Ingestion Server
```
audius-cli launch ddex
```
Your server will now be available at localhost:80 and is exposed via self-signed SSL on :443 if
`DDEX_URL` is set.
:::tip[Looking for how to accept deliveries from your label or distributor?]
Check the [Audius Support Page](https://support.audius.co) for more information.
:::
Audius supports bulk content ingestion via the DDEX standard.
Providers, labels & partners with their own delivery infrastructure should reference the following
documentation for guidance on how to send and administer content on Audius.
If you're looking to build your own ingestion tooling, get started with the [Audius SDK](/sdk).
### What is DDEX?
Digital Data Exchange (or "DDEX") is an international standards-setting organization that was formed
in 2006 to develop standards that enable companies to communicate information along the digital
supply chain more efficiently by:
* Developing standard message and file formats (XML or flat-file)
* Developing choreographies for specific business transactions
* Developing communication protocols (SFTP or based on web services)
* Working with industry bodies to create a more efficient supply chain
:::info[More Information]
- Learn more on the official DDEX Website here: [https://ddex.net/](https://ddex.net/)
- Looking for a deeper technical dive? Checkout the DDEX knowledge base here:
[https://kb.ddex.net/](https://kb.ddex.net/)
:::
***
### Open Source
All DDEX ingestion code & libraries are open source and available on
[GitHub](https://github.com/AudiusProject/apps/tree/main/packages/ddex).
You may clone and self-operate your own Audius compatible DDEX ingestion server, which provides a
web interface to deliver files, manage uploads, and track success. Under the hood, DDEX ingestion
uses the [Audius SDK](/sdk) to process and upload tracks and is available in a
[self-service](/distributors/self-serve/overview) manner.
**Example files**
Example DDEX XML files are available on
[GithHub](https://github.com/AudiusProject/apps/tree/main/packages/ddex/processor/fixtures).
* [Delivery](https://github.com/AudiusProject/apps/blob/main/packages/ddex/processor/fixtures/01_delivery.xml)
* [Update](https://github.com/AudiusProject/apps/blob/main/packages/ddex/processor/fixtures/02_update.xml)
* [Delete (Purge Message)](https://github.com/AudiusProject/apps/blob/main/packages/ddex/processor/fixtures/03_delete.xml)
***
### Partner Onboarding
When delivering content to Audius, there are two options:
1. Run open source DDEX ingestion tooling yourself, starting
[here](/distributors/self-serve/overview).
2. Work directly with a partner that will accept standard DDEX XML files via private delivery (S3,
SFTP) and upload releases to Audius
##### Tiki Labs, Inc.
Tiki Labs, Inc., one of the development teams behind the Audius protocol, offers a first-party
service to run DDEX ingestion tooling on behalf of partners.
In order to get started with Tiki Labs, Inc. directly, reach out to [ddex-support@audius.co](mailto\:ddex-support@audius.co) and
submit an [intake form](https://forms.gle/jCwLLWRJY7fCQM5ZA).
After on-boarding, you will deliver files directly to a provided S3 or SFTP bucket, which will be
pushed to Audius as release criteria are met.
* **DPID**: PA-DPIDA-202401120D-9
* **Party Name**: Tiki Labs, Inc.
{/* TODO: when ready, add `npx create-audius-app` code block for rapid start */}
### 🧰 Use the Javascript SDK
> Interact with music and accounts using the JavascriptSDK.
* [Getting Started](/sdk) - Start here to get up and building with Audius.
* [Create Apps Easily](/developers/guides/create-audius-app) - Run `npx create-audius-app` to get a
head start building.
* [Advanced Options](/sdk) - Ready to dig in more? check out the SDK docs for all available options.
***
### 📤 Use the REST API
> Get read-only access using the RESTful API.
* [Full API Reference](/api) - Query, stream, and search for tracks, users & playlists across the
network.
* [Explore the API with Postman](https://www.postman.com/samgutentag/workspace/audius-devs/collection/17755266-71da9172-77a7-427f-8ab5-1ce58f929ff5?action=share\&creator=17755266) -
Explore the Audius API with Postman.
***
### 🧑💻 Getting Help & Reporting Bugs
> Join the developer community, get direct support, and file issues.
* [Developer Discord](https://discord.com/invite/audius) - Join the community and head to the
**Developers** section
* [Get one on one support](https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1IRk54J0XtTCavF8PbFSIxbaHX5tnybxKFLqEPvAXWYMgqNN9D9a6iFa_p6zCfTXsPbK8zIkKU) -
Book a meeting with Developer Relations.
* [Report a bug or make a feature request](https://github.com/AudiusProject/apps/issues/new/choose) -
Better yet, see a bug you have a fix for? Open a PR!
***
### 🔐 Log in With Audius
* [Quickstart](/developers/guides/log-in-with-audius) - Log In with Audius to retrieve a user's
Audius profile information and optionally get permission to perform actions on their behalf.
* [Demo Apps](/developers/guides/log-in-with-audius#examples) - Log In examples using React or
vanilla JS, and a convenient button generator to get started.
***
### 🦔 Hedgehog
> Hedgehog is an open-source, client-side Ethereum wallet that aims to lower the barrier of entry to
> crypto projects by using a username and password.
* [Learn More](/developers/guides/hedgehog) - Learn more about the motivation for a lower barrier to
entry wallet in the crypto ecosystem.
* [Source Code](https://github.com/AudiusProject/hedgehog) - Get the source code on GitHub.
* [More Docs](https://audiusproject.github.io/hedgehog-docs/#installation) - Even more Hedgehog
specific documentation.
***
### ፨ Audius on The Graph
> Explore on-chain governance using
> [The Graph](https://thegraph.com/docs/about/introduction#what-the-graph-is).
Audius has a GraphQL API Endpoint hosted by The Graph called a subgraph for indexing and organizing
data from the Audius smart contracts.
* [The Graph Guide](/developers/guides/subgraph) - Explore on-chain governance data using the Audius subgraph.
:::warning[Work in progress]
This page will not be published to builds. it is available when run locally only.
:::
> content goes here
### Quick Start
```sh
npx create-audius-app
cd my-app
npm run dev
```
***
### Creating an App
The easiest way to start building a new application on top of the Audius Protocol is by using
`create-audius-app`. This CLI tool enables you to quickly start building a new Audius application,
with everything set up for you. You can create a new app using the default Audius react template, or
by using one of the [examples][example-repo].
:::info[Minimum Node Version]
You’ll need to have Node >= 18 on your local development machine. You can use [nvm][nvm-url]
(macOS/Linux) or [nvm-windows][nvm-windows-url] to switch Node versions between different projects.
:::
To create a new app run the following command:
```sh
npx create-audius-app
```
You will be asked for the name of your project, and all the necessary dependencies will be
installed.
{/* prettier-ignore */}
#### Non Interactive Mode
To bypass the prompt for an app name, append it to the create command like this:
```sh
npx create-audius-app my-first-audius-app
```
Explore other command line options by running `npx create-audius-app --help`
#### Output
Running `create-audius-app` will create a directory called `my-app` inside the current folder.
Inside that directory, it will generate the initial project structure and install the transitive
dependencies:
```sh
my-app
├── README.md
├── gitignore
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ ├── emotion.d.ts
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
```
No configuration or complicated folder structures, only the files you need to build your app. Once
the installation is done, you can open your project folder:
```sh
cd my-app
```
### Launch Your App
Once inside the `my-app` directory run the following command and take note of the local host port:
```sh
npm run dev
```
{/* prettier-ignore */}
This example uses port 5173 on localhost.
***
### Design
Applications started from `create-audius-app` leverage the [Harmony][harmony-docs] design system.
Harmony is all about collaboration, reusability, and scalability. It aims to harmonize code and
Figma, provides a shared language for designers and developers, and provide consistent, reusable
components for use across platforms.
Read more about Harmony and using it across your other projects at
[https://harmony.audius.co/][harmony-docs].
{/* prettier-ignore */}
[example-repo]: https://github.com/AudiusProject/apps/tree/main/packages/create-audius-app/examples
[nvm-url]: https://github.com/nvm-sh/nvm#installation
[nvm-windows-url]: https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows
[harmony-docs]: https://harmony.audius.co/
**Programmable distribution** lets you control who can stream a track. Instead of making every track publicly streamable, you designate one or more wallet addresses as **access authorities**. Only requests signed by those addresses are accepted by the protocol. Your server holds the key and decides who gets access.
### How It Works
When you create a track, you set `access_authorities` to the wallet address(es) that can authorize stream requests. Validator nodes enforce this: if a stream request is unsigned or signed by an address not in `access_authorities`, the node returns 401 and rejects it.
Your **access server** holds the private key for one of those addresses. When a user requests a stream, your server:
1. Verifies the user is allowed (e.g. logged in, in the right region, has paid, follows you)
2. Fetches the stream URL from the Audius API
3. Signs a short-lived signature in the gate-release-access format
4. Redirects the user to the stream URL with the signature attached
The node validates your signature, confirms the signer is in the track’s `access_authorities`, and serves the audio. Without your server’s signature, direct requests to the node fail.
### Access Authorities
`access_authorities` is an array of Ethereum addresses. Any one of them can sign to authorize a stream. Common patterns:
* **Single signer** — One address (e.g. your server’s wallet). Simplest and most common.
* **Multiple signers** — Several addresses for redundancy or delegated access.
* **Empty or omitted** — The track is public; no signature required.
Tracks with `access_authorities` are **gated**. Tracks without it are **public** and can be streamed by anyone with the URL.
### Example: Gated Upload
The [gated-upload example](https://github.com/AudiusProject/apps/tree/main/packages/web/examples/gated-upload) implements programmable distribution with geo-gating.
**Server**
* `POST /create-track` — Creates a track with `access_authorities: [signerAddress]`. The server’s wallet is the only authority.
* `GET /stream/:trackId` — Checks the client’s IP via ip-api.com. If the client is in `ALLOWED_COUNTRIES`, fetches the track from the SDK, signs the stream URL, and redirects. Otherwise returns 403.
* `GET /my-region` — Returns the client’s IP, country, city, and whether they’re allowed (for UI feedback).
**Client**
* OAuth login, upload via SDK (`uploadTrackFiles`, then `create-track` with the server), and streaming via `GET /stream/:trackId` (server redirects to signed URL).
Run the server from `packages/web/examples/gated-upload/server` with `AUDIUS_API_KEY`, `AUDIUS_BEARER_TOKEN`, and `SIGNER_PRIVATE_KEY` in `.env`. See the [README](https://github.com/AudiusProject/apps/blob/main/packages/web/examples/gated-upload/README.md) for full setup.
### What You Can Build
Programmable distribution supports many use cases where you want to gate streaming behind your own logic.
#### Geo-Gated Releases
Only allow streaming from certain countries. The gated-upload example uses ip-api.com to resolve IP → country and blocks requests outside `ALLOWED_COUNTRIES`. Useful for licensing, regional rollouts, or compliance.
#### Private Groups
Restrict streams to members of a private community. Your access server checks whether the user is logged in and in the group (e.g. Discord role, invite list, subscription). Only then does it sign the stream URL.
#### Frontend-Gated Releases
Limit streaming to users who arrive through your app or frontend. Your server can verify a session, referrer, or token before signing. Direct links from other sites fail without that check.
#### Paid / Premium Content
Require payment, subscription, or NFT ownership before signing. Your server verifies the purchase or membership and signs only for eligible users.
#### Time-Based or Schedule-Based Access
Release content at a specific time or after a countdown. Your server checks the current time or event state before signing.
### Summary
| Concept | Meaning |
| ----------------------- | ----------------------------------------------------------------- |
| **access\_authorities** | Wallet addresses that can sign to authorize stream access |
| **Gated track** | Has `access_authorities`; requires a valid signature to stream |
| **Public track** | No `access_authorities`; anyone can stream |
| **Access server** | Your backend that holds the signing key and enforces access logic |
| **gate-release-access** | Signature format the protocol expects on stream URLs |
For implementation details (signature format, canonical JSON, EIP-191 hashing), see the [Open Audio Protocol gate-release-access tutorial](https://github.com/AudiusProject/open-audio-docs/blob/main/docs/pages/tutorials/gate-release-access.mdx) and the [gated-upload server source](https://github.com/AudiusProject/apps/blob/main/packages/web/examples/gated-upload/server/server.js).
> Build DApps Like Apps
***
Hedgehog is an open-source, client-side Ethereum wallet that uses a username and password. It aims
to lower the barrier of entry to crypto projects for non tech-savvy users.
Allow users to interact with your DApp just like they would any other website, no extensions
required, without centralizing control of keys.
Hedgehog is an alternative to Metamask that manages a user's private key and wallet on the browser.
It exposes a simple API to allow you to create an authentication scheme to let users sign up and
login to their wallet across multiple browsers and devices.
***
### Not All Transactions Are Created Equal
Decentralized apps today require lots of technical knowledge to configure and use, limiting your
user base and reducing the potential for growth.
Currently available wallets treat every transaction as if it were moving around your life’s savings.
Hedgehog was built for use cases involving low-to-no financial value.
***
### Is Hedgehog Right for your DApp?
:::note
The primary improvement to end-user experience is gained by hiding wallet complexity and not forcing
users to constantly confirm transactions - The opposite of what you’d want when moving significant
money around.
:::
Hedgehog isn’t right for every DApp. Massive improvements in user experience are only possible
through tradeoffs. As a general rule Hedgehog should not be used for apps involving significant sums
of money. As a bridge, one could start users on Hedgehog and suggest migrating to a more secure
wallet if their stored value increases beyond a certain threshold; the Hedgehog paradigm is
interoperable with existing web3 providers too.
#### Good Use Cases
* **Signing data** - If you’re building decentralized applications that rely on user signed data
(eg. via EIP-712-esque signing schemes), Hedgehog could help simplify the experience if the stakes
are low enough.
* **Gaming DApp** - Nothing ruins fun as much as signing transactions. If you’re building a gaming
DApp that doesn’t use significant financial assets, improving UX is key.
* **Decentralized Music Player** - If you’re building consumer-facing DApps, Hedgehog will
dramatically improve user experience and significantly increase your potential userbase.
#### Not So Good Use Cases
If your DApp involves moving around significant sums of money, then the tradeoff in security is most
likely not worth it. Hedgehog’s primary improvement to end-user experience is by hiding the wallet
and not forcing users to confirm transactions - The opposite of what you’d want when moving money
around. We absolutely don’t recommend using Hedgehog in situations like these:
* Banking DApp
* Decentralized Lending
* Prediction Markets
***
### A Closer Look
Hedgehog is a package that lives in your front end application to create and manage a user's entropy
(from which a private key is derived). Hedgehog relies on a username and password to create auth
artifacts, so it's able to simulate a familiar authentication system that allows users to sign up or
login from multiple browsers or devices and retrieve their entropy. Those artifacts, through
hedgehog, are persisted to a backend of your choosing.
:::note
A private key is only computed and available client side and is never transmitted or stored anywhere
besides the user's browser.
:::
```javascript
// Provide getFn, setAuthFn, setUserFn as requests to your database/backend service (more details in docs).
const hedgehog = new Hedgehog(getFn, setAuthFn, setUserFn)
let wallet
if (hedgehog.isLoggedIn()) {
wallet = hedgehog.getWallet()
} else {
wallet = await hedgehog.login('username', 'password')
// or
wallet = await hedgehog.signUp('username', 'password')
}
```
After creating or retrieving a user's wallet, you can either **fund their wallet directly** to pay
transaction fees or **relay their transactions through a EIP-712 relayer**.
***
### Installation
```bash
npm i --save @audius/hedgehog
```
***
### Docs & Examples
For a quick browser-side demo, [look no further](https://codesandbox.io/embed/pp9zzv2n00). For a
full end-to-end auth demonstration, see our
[demo repo](https://github.com/AudiusProject/audius-hedgehog-demo).
Ready to learn more? [Take a deeper dive into the docs](/api) and find the source code on
[Github](https://github.com/AudiusProject/hedgehog).
Images (artwork, profile pictures, cover photos) served by Audius are replicated across validator
nodes. When an image fails to load—due to node unavailability or network issues—your app should
retry using alternate mirror hosts. Without mirror fallback, image loading is unreliable.
### API Response Structure
Artwork and profile image objects in API responses include size variants **and** a `mirrors` array:
```json
{
"artwork": {
"150x150": "https://audius-content-7.cultur3stake.com/content/Qmd9Z9BS6NAGASFWcTdk1bhSaiJR84czJXeKNgLcL7hH4L/150x150.jpg",
"480x480": "https://audius-content-7.cultur3stake.com/content/Qmd9Z9BS6NAGASFWcTdk1bhSaiJR84czJXeKNgLcL7hH4L/480x480.jpg",
"1000x1000": "https://audius-content-7.cultur3stake.com/content/Qmd9Z9BS6NAGASFWcTdk1bhSaiJR84czJXeKNgLcL7hH4L/1000x1000.jpg",
"mirrors": [
"https://audius-creator-6.theblueprint.xyz",
"https://cn0.mainnet.audiusindex.org",
"https://creatornode2.audius.co"
]
}
}
```
* **Size variants** (`150x150`, `480x480`, `1000x1000`): Use the variant closest to the displayed
size for performance.
* **mirrors**: Alternate validator node host roots. Mirror order is arbitrary; try each until one
succeeds.
Profile objects use the same pattern for `profile_picture` and `cover_photo` (with `_150x150`,
`_480x480`, `_1000x1000` and `mirrors` in some adapters).
### Mirror Fallback Strategy
When an image URL fails to load:
1. Take the current URL (e.g. from a size variant).
2. Replace the host (scheme + authority) with each mirror root, in order.
3. Try each resulting URL until one loads successfully or all are exhausted.
4. Optionally fall back to a placeholder or `onError` handler afterward.
Example host-swap logic (conceptual):
```js
// Given: originalUrl, mirrors = ["https://cn0.mainnet.audiusindex.org", ...]
function buildMirrorUrl(originalUrl, mirrorHost) {
const url = new URL(originalUrl)
url.host = new URL(mirrorHost).host
return url.toString()
}
```
### Best Practices
#### 1. Preserve mirrors in normalization
Do not reduce artwork or profile objects to a single URL string during normalization. If you do,
mirrors are lost and cannot be used for retries. Keep `mirrors` attached to the image metadata that
your image component receives.
#### 2. Use a shared image component with mirror retry
Avoid raw ` ` for Audius content. Centralize mirror-aware loading in one component
(e.g. `RetryImage`, `ArtworkImage`) that:
1. Tries the primary URL.
2. On `onError`, retries with each mirror (by swapping host).
3. Falls back to `fallbackSrc` or `onError` only after all mirrors fail.
#### 3. Apply consistently everywhere
Mirror retry must be used for **all** Audius images—track art, playlist art, profile pictures, cover
photos, etc. Partial adoption (e.g. only where `RetryImage` is used) leaves gaps and broken images
in other views.
#### 4. Use size-aware variant selection
Pick the size variant closest to the displayed dimensions (and device pixel ratio) to avoid loading
oversized images. Register or pass all variants plus mirrors so the component can retry with the
same size on different hosts.
### Common Pitfalls
| Pitfall | Consequence |
| --------------------------------------------- | ---------------------------------------------------------- |
| `getArtworkUrl()` returning only a single URL | Mirrors are dropped; no retry possible. |
| Raw ` ` instead of mirror-aware component | No retry on failure. |
| Mirror logic only in some components | Inconsistent behavior; images fail in non-covered screens. |
| Ignoring mirrors in API responses | Same as above; no fallback hosts available. |
### Reference Implementation
The Audius embed player uses mirror fallback in `getArtworkUrl`. See
[getArtworkUrl.js](https://github.com/AudiusProject/apps/blob/main/packages/embed/src/util/getArtworkUrl.js)
in the apps repo.: it preloads the primary URL, and on failure, swaps the host with each mirror and
retries before returning.
For React apps, implement or use a shared component that accepts the full artwork/profile object
(with variants and mirrors) and performs the same retry logic on load failure.
> Help other users identify you by connecting your [Audius][audius-co] account to the [Audius
> Protocol Dashboard][protocol-dashboard].
Once you've linked your Audius account, your Profile Picture and Display Name will be visible to
users throughout the protocol dashboard.
### Connect to Protocol Dashboard
1. Navigate to the [Audius Protocol Dashboard][protocol-dashboard]
2. Click the "Connect Wallet" button on the upper right
{/* prettier-ignore */}
Wallet Connect Button
3. Select your web3 wallet in the wallet selection modal and sign in.
{/* prettier-ignore */}
Wallet Selection Modal
4. By default, a Gravatar style icon will be used to represent the wallet across the Dashboard.
{/* prettier-ignore */}
Protocol Dashboard default profile icon
***
### Connect to Audius Profile
To connect your Audius profile to the Dashboard,
1. Click the "Connect Audius Profile" button in the upper right corner.
{/* prettier-ignore */}
Protocol Dashboard "Connect Audius Profile" button
2. In the modal, confirm your understanding and proceed by clicking the "Connect Profile" button.
{/* prettier-ignore */}
Review and confirm the next step by clicking "Connect Profile" in the modal.
3. In the pop over, enter the credentials of the Audius account you want to connect to your Protocol
Dashboard account and click "Sign In & Authorize App"
Sign in with your Audius account to continue
:::tip[Already signed in?]
If you are already signed in to an Audius account it will be chosen as the default. If you would
like to use a different Audius account, be sure to sign out and sign in with the correct account
before clicking "Authorize App".
If you are not currently signed in to an Audius account, you will be prompted to do so.
:::
4. Your wallet app will present a signature request to confirm the account connection. Sign this
message to complete the link.
{/* prettier-ignore */}
Using MetaMask as an example, sign the request.
5. Complete! Now your Audius account profile image will be shown on the Audius Protocol Dashboard!
{/* prettier-ignore */}
Account icon when an Audius account is connected to the Protocol Dashboard.
{/* prettier-ignore */}
[audius-co]: https://audius.co/
[protocol-dashboard]: https://dashboard.audius.org/
UI for read and read/write authorization flows
Log In with Audius lets you retrieve a user's Audius profile information and optionally get
permission to perform actions on their behalf, without making the user give you their Audius
password.
### Quickstart
```html title="index.html"
```
```js title="script.js" showLineNumbers
import Web3 from 'web3'
import { sdk } from '@audius/sdk'
window.Web3 = Web3
const audiusSdk = sdk({ apiKey: 'Your API key goes here' })
audiusSdk.oauth.init({
successCallback: (userInfo) => {
console.log('Log in success!', userInfo)
},
})
audiusSdk.oauth.renderButton({
element: document.getElementById('audiusLogInButton'),
scope: 'read', // use "write" instead if you'd like to request read/write access to user's account
})
```
:::tip
Don't have an API key? Get one by creating a developer app on the Audius
[Settings page](https://audius.co/settings).
:::
### Examples
* Demo with React - [Demo app](https://nffqd5.csb.app/) |
[Code](https://codesandbox.io/s/log-in-with-audius-demo-723-nffqd5?file=/package.json:170-183)
* Demo with vanilla JS - [Demo app](https://f68xgn.csb.app/) |
[Code](https://codesandbox.io/s/log-in-with-audius-demo-vanilla-js-723-f68xgn?file=/index.html)
* [Log In Button Generator](https://9ncjui.csb.app/)
### Full Reference
#### 1. Install the JavaScript SDK
:::note
If you are not able to use the JavaScript SDK (for example, if you are developing a mobile app),
skip to [Manual Implementation](#manual-implementation).
:::
In your terminal, run the following:
```bash
npm install web3 @audius/sdk
```
Then, initialize the SDK:
```js
import Web3 from 'web3'
import { sdk } from '@audius/sdk'
window.Web3 = Web3
const audiusSdk = sdk({ apiKey: 'Your API Key goes here' })
```
:::tip
See complete instructions [here](/sdk#install-the-sdk) to install and initialize the JavaScript SDK.
:::
#### 2. Initialize the SDK `oauth` feature
```js showLineNumbers
audiusSdk.oauth.init({
successCallback: (res) => {
// This will run if the user logged in successfully.
console.log('Log in success!', res)
/**
`res` will contain the following user information:
{
userId: number; // unique Audius user identifier
email: string;
name: string; // user's display name
handle: string;
verified: boolean; // whether the user has the Audius "verified" checkmark
profilePicture: {"150x150": string, "480x480": string, "1000x1000": string } | null // URLs for the user's profile picture
apiKey: string | null // the API key for your application if specified
sub: number; // alias for userId
iat: string; // timestamp for when auth was performed
}
**/
},
errorCallback: (err) => {
// This will run if there was an error during the auth flow.
console.log('Error :(', err)
// `err` will contain the error message
},
})
```
#### 3. Render the Log In button
```js title="In your JS"
audiusSdk.oauth.renderButton({
element: document.getElementById('audiusLogInButton'),
scope: 'read', // Change to "write" if you need write access to users' accounts
buttonOptions: {
size: 'large',
corners: 'pill',
customText: 'Continue with Audius',
},
})
```
```html title="In your HTML"
```
[`renderButton`](/sdk/oauth#renderbutton) replaces the given `element` with the Log In
with Audius button.
If `scope` is set to `"write"`, the user will be prompted to grant your app read/write access to
their account (allowing your app to perform actions like uploading a track on the user's behalf). If
`scope` is set to `"read"`, the user will be prompted to grant your app read-only access to their
account.
:::note
The `write` scope grants your app permission to perform most actions on the user's behalf, but it
does NOT allow any access to DMs or wallets.
:::
You can also pass in `buttonOptions`, an optional object with customization settings for the button.
:::tip
Use [this playground](https://9ncjui.csb.app/) to explore the different button options.
:::
If you don't want to use `renderButton`, you can implement a login button yourself and invoke the
login popup with [`audiusSdk.oauth.login`](/sdk/oauth#login).
Optional: Show loader until the button is ready
The button may take up to a couple of seconds to load. You may want to show a loading indicator
until the button has loaded for an optimal user experience.
```html title="In your HTML" showLineNumbers
{/* Surround your element that will be replaced with the Log In with Audius button with a parent, e.g.: */}
```
```javascript title="In your JS" showLineNumbers
const observer = new MutationObserver(function (mutations_list) {
mutations_list.forEach(function (mutation) {
mutation.addedNodes.forEach(function (added_node) {
if (added_node.id == 'audius-login-button') {
// Login button has rendered
document.querySelector('#loading').remove()
observer.disconnect()
}
})
})
})
observer.observe(document.querySelector('#parent'), {
subtree: false,
childList: true,
})
```
The log in button will be rendered with an id of `audius-login-button`. As shown above, you can
detect when the element has been added using a MutationObserver.
#### 5. Optional: Write on behalf of the Audius user
Once a user has authorized your app with the `write` scope, you can easily perform actions on their
behalf using the SDK.
Simply initialize the SDK with your API Key and Secret and begin using the various write methods.
```js title="Server-side JS"
const audiusSdk = sdk({
apiKey: 'Your API Key goes here',
apiSecret: 'Your API Secret goes here',
})
const track = await audiusSdk.tracks.favoriteTrack({
trackId: 'D7KyD',
userId: 'Audius user ID of user who gave your app write access',
})
```
:::warning
Be careful to not expose API secrets on your frontend!
:::
#### 6. Done!
* [See examples](#examples)
* [Read full SDK `oauth` docs](/sdk/oauth)
* [Explore the API docs](/sdk/tracks)
::::note
#### Retrieving email addresses
Once you know your user's Audius user id, you can retrieve their Audius information at any time
using our SDK or web APIs. However, the one piece of profile information that is not available
outside of the Log In with Audius response is the user's email address. If you do not initially
store the user's email address, you can only re-retrieve the email through having the user
re-complete the Log In with Audius flow. :::
### Example use cases
##### Write scope
* Upload tracks to your users' Audius accounts
* Save tracks to your users' Audius libraries
##### Read-only scope
* Provide a convenient way for users to sign up and/or log in to your app without having to set a
password or fill in a profile form
* Associate a user to their Audius account so that you can retrieve their Audius data (e.g. retrieve
their tracks)
* Confirm if a user is a "Verified" Audius artist
However, note that this flow **CANNOT**:
* Manage the user's login session on your app
### Workflow
The "Log In with Audius" flow looks like this:
1. You provide a button on your app or website to begin the authentication flow
2. When the user clicks the button, it opens the Log In with Audius page
3. Once the user successfully authorizes your app, Audius provides your app/website with the user
profile using a JSON Web Token (JWT)
4. Your app verifies and decodes the JWT
The JWT payload contains the following information about the user:
* Unique identifier (Audius user id)
* Email
* Display name
* Audius handle
* Whether the user is a verified artist (i.e. has a purple checkmark)
* Profile picture URL
### Manual Implementation
If you are not able to use the Audius JavaScript SDK, you may implement Log In with Audius manually
by following the steps below.
#### 1. Open the Log In with Audius prompt page
Create a "Log In with Audius" button on your app.
:::tip
If using HTML (or HTML-like markup) and CSS, check out [this playground](https://j2jx6f.csb.app/) to
easily customize and generate code for an Audius-branded login button.
:::
::::
Clicking your log in button should direct the user to the Log In with Audius prompt page.
On a native app, the log in button should open a secure web browser within the app that loads the
Audius login page. A web app should open the Audius login page in a popup or redirect to it.
The Log In with Audius prompt page is located at the following URL:
`https://audius.co/oauth/auth?scope={read|write}&api_key={Your API Key}&redirect_uri={Your Redirect URI}&origin={Your App Origin}&state={Your State Value}&response_mode={query|fragment}`
You must open this page with the required URL parameters, described below.
**Params**
* scope `"read" | "write"` - the scope of the authentication request. Use `"write"` if your app will
request read/write access to the user's account; otherwise, use `"read"` if your app only needs
read access.
* api\_key `string` - your app's Audius API Key. If you don't have one, you can create one on the
Audius [Settings page](https://audius.co/settings).
* redirect\_uri `string` - the location that the Audius login page should redirect to once the user
successfully authenticates. A URL protocol of http or https is required. You can use the special
value `postmessage` here if you would like the login page to send the response back to its opener
using `window.postMessage` instead of using a redirect. Otherwise, the following validation rules
apply:
* Hosts cannot be raw IP addresses UNLESS they are localhost IP addresses
* Cannot contain the fragment component (`#`)
* Cannot contain the `userinfo` component
* Cannot contain a path traversal (contain `/..` or `\..`)
* Must contain valid characters and URI format
* origin *optional* `string` - only applicable and required if `redirect_uri` is set to
`postmessage`. If so, this value should be set to the
[origin](https://developer.mozilla.org/en-US/docs/Web/API/URL/origin) of the window that opened
the Log In with Audius popup.
* state *optional but highly recommended* `string` - any string. When the user is redirected back to
your app, the exact `state` value you provide here will be included in the redirect (in the
`state` URI fragment parameter). **This field should be leveraged as a CSRF protection mechanism**
(read more [here](https://auth0.com/docs/secure/attack-protection/state-parameters) or
[here](https://security.stackexchange.com/questions/20187/oauth2-cross-site-request-forgery-and-state-parameter)),
and may also be used as a way to persist any useful data for your app between where the `state`
value is generated and where the redirect goes.
* `response_mode` *optional, not recommended when possible* `"fragment" | "query"` - specifies
whether the auth flow response parameters will be encoded in the query string or the fragment
component of the redirect\_uri when redirecting back to your app. Default behavior is equivalent to
"fragment". We recommend NOT changing this if possible.
* `display` *optional* `"popup" | "fullScreen"` - determines whether the auth flow expects to be
rendered in a popup or a full screen view. Defaulted to `"popup"` if unspecified.
**Example**
```html noInline
Click me to log in with Audius!
```
:::tip[Remember to handle early exiting (i.e. failure) of the authentication flow]
If the user exits the authentication flow before completing it--e.g. by closing the window--your app
should detect this and have the UI respond accordingly.
:::
#### 2. Receive the response
##### **If you used a redirect URI**:
When the user has successfully authenticated, the Log In with Audius page will redirect to the
redirect URI that you specified, **with 1) the JWT containing the user profile, and 2) the original
state value you provided (if any) included in the URI fragment** (or query string, if
`response_mode` was set to `query`). To illustrate, going off the example above where we opened the
login page with the following URL:
```noInline
https://audius.co/oauth/auth?scope=read&app_name=My%20Demo%20App&redirect_uri=https://mydemoapp.com/oauth/receive-token&state=a4e0761e-8c21-4e20-819d-5a4daeab4ea9
```
when the user successfully authenticates, the login page would redirect to:
`https://mydemoapp.com/oauth/receive-token#state=a4e0761e-8c21-4e20-819d-5a4daeab4ea9&token={JWT}`
where `{JWT}` is a [JSON web token](https://jwt.io/introduction) containing the user's encoded
profile information and a signature.
:::info[If you specified `response_mode=query` when opening the login page, the login page would]
instead redirect to...:
`https://mydemoapp.com/oauth/receive-token?state=a4e0761e-8c21-4e20-819d-5a4daeab4ea9&token={JWT}`
:::
Skip to [**Handling the response**](#3-verify-the-response) below for what to do next.
##### **If you used `redirectURI=postmessage`**:
When the user has successfully authenticated, the Log In with Audius page will send a message via
`window.postMessage` to the window that opened it. The message will contain a JWT containing the
user profile as well as whatever `state` value you originally specified in the corresponding URL
param, if any.
For instance, if your app opened the login page using the following URL:
```noInline
https://audius.co/oauth/auth?scope=read&app_name=My%20Demo%20App&redirect_uri=https://mydemoapp.com/oauth/receive-token&state=a4e0761e-8c21-4e20-819d-5a4daeab4ea9
```
the message would look like this:
```
{
token: ,
state: 'a4e0761e-8c21-4e20-819d-5a4daeab4ea9'
}
```
where `` is a [JSON web token](https://jwt.io/introduction) containing the user's encoded
profile information and a signature.
:::warning
Make sure that your `postMessage` event listener validates that the origin of the incoming event is
`https://audius.co`!
:::
#### 3. Verify the response
Extract the JWT (`token`) from the URI fragment or query string (if you used a redirect) or the
event message (if you used `postmessage`).
Once you have the token, you must send it to the Audius `verify_token` endpoint in order to verify
that:
* the signature was signed by the Audius user who completed the authentication
* the content of the token hasn't been tampered with
Upon verifying the validity of the JWT, the endpoint will return the authenticated user's decoded
profile information.
GET /v1/users/verify\_token?token=\[JWT]
**Params**
* token `string` - the JWT from the authentication flow that you would like to verify
**Sending the request**
To use the API, you first select an API endpoint from the list of endpoints returned by:
`GET https://api.audius.co`
Once you've selected a host, all API requests can be sent directly to it. For instance, if you
picked the host at `https://audius-dp.singapore.creatorseed.com`, you would send the GET request to
`https://audius-dp.singapore.creatorseed.com/v1/users/verify_token?token=`, where `` is
replaced with the JWT you retrieved in the auth flow.
We recommend selecting a host each time your application starts up as availability may change over
time.
**Success response**
* Code: 200 OK
* Content: The decoded JWT payload, which contains the user's profile information:
```ts
{
userId: number, // unique Audius user identifier
email: string,
name: string, // user's display name
handle: string,
verified: boolean, // whether the user has the Audius "verified" checkmark
profilePicture: {"150x150": string, "480x480": string, "1000x1000": string } | null // URLs for the user's profile picture
apiKey: string | null // the API key for your application if specified
sub: number, // alias for userId
iat: string, // timestamp for when auth was performed
}
```
**Error responses**
Invalid signature :
* Code: `404` Not Found
* Content: Error message describing the issue that occurred, e.g. "The JWT signature is invalid -
wallet could not be recovered."
Problem with `token` input :
* Code: `400` Bad Request
* Content: Error message, e.g. "the JWT signature could not be decoded."
##### 3. Done!
Once you've verified the JWT, the authentication flow is complete and you now have your user's
Audius profile information.
If you used the `write` scope, you can use the Audius SDK to perform actions on behalf of the user
who authorized your app:
```js title="Server-side JS
const audiusSdk = sdk({
apiKey: 'Your API Key goes here',
apiSecret: 'Your API Secret goes here',
})
const track = await audiusSdk.tracks.favoriteTrack({
trackId: 'D7KyD',
userId: 'Audius user ID of user who gave your app write access',
})
```
See [Getting Started](/sdk) with the SDK or [the SDK methods reference](/sdk/tracks) for
further reading.
##### [A quick note on email](#retrieving-email-addresses)
Audius has a GraphQL API Endpoint hosted by
[The Graph](https://thegraph.com/docs/about/introduction#what-the-graph-is) called a subgraph for
indexing and organizing data from the mainnet Ethereum contracts.
This subgraph is can be used to query Audius data.
Subgraph information is serviced by a decentralized group of server operators called Indexers.
***
### Ethereum Mainnet
[Creating an API Key Video Tutorial](https://www.youtube.com/watch?v=UrfIpm-Vlgs)
* [Explorer Page](https://thegraph.com/explorer/subgraphs/F8TjrYuTLohz64J8uuDke9htSR1aY9TGCuEjJVVjUJaD?view=Query\&chain=arbitrum-one)
* Graphql Endpoint:
`https://gateway.thegraph.com/api/[api-key]/subgraphs/id/F8TjrYuTLohz64J8uuDke9htSR1aY9TGCuEjJVVjUJaD`
* [Code Repo](https://github.com/AudiusProject/audius-subgraph)
***
### Helpful Links
* [Querying from an Application](https://thegraph.com/docs/en/subgraphs/querying/introduction/)
* [Managing your API Key & Setting your indexer preferences](https://thegraph.com/docs/en/studio/managing-api-keys/)
### Sample Queries
Below are some sample queries you can use to gather information from the Audius contracts.
You can build your own queries using a [GraphQL Explorer](https://graphiql-online.com/graphiql) and
enter your endpoint to limit the data to exactly what you need.
***
#### User
Description: Get users balance of claimable stake and delegation information.
```graphql
{
user(id: "0x8c860adb28ca8a33db5571536bfcf7d6522181e5") {
balance
totalClaimableAmount
claimableStakeAmount
claimableDelegationSentAmount
claimableDelegationReceivedAmount
stakeAmount
delegationSentAmount
delegationReceivedAmount
deployerCut
delegateTo(orderBy: claimableAmount, orderDirection: desc) {
amount
claimableAmount
toUser {
id
}
}
delegateFrom(orderBy: claimableAmount, orderDirection: desc) {
amount
claimableAmount
fromUser {
id
}
}
}
}
```
***
#### Audius Network
Description: Find minimum stake and maximum on delegation
```graphql
{
audiusNetworks(first: 5) {
id
audiusTokenAddress
claimsManagerAddress
delegateManagerAddress
}
serviceTypes(first: 5) {
id
isValid
minStake
maxStake
}
}
```
### Entities
Description:
| Field | Type | Description |
| --------------------------------- | ------ | ------------------------------------------------------------------------------------------------- |
| `id` | ID | ID is set to 1 |
| `audiusTokenAddress` | Bytes | audiusToken address |
| `claimsManagerAddress` | Bytes | claimsManager address |
| `delegateManagerAddress` | Bytes | delegateManager address |
| `governanceAddress` | Bytes | governance address |
| `registry` | Bytes | registry address |
| `serviceProviderFactoryAddress` | Bytes | serviceProviderFactory address |
| `serviceTypeManagerAddress` | Bytes | serviceTypeManager address |
| `stakingAddress` | Bytes | staking address |
| `registryAddress` | Bytes | registry address |
| `totalSupply` | BigInt | Total supply of $AUDIO |
| `totalAUDIOMinted` | BigInt | Total amount of $AUDIO minted |
| `totalAUDIOBurned` | BigInt | Total amount of $AUDIO burned |
| `totalTokensStaked` | BigInt | Total amount of $AUDIO staked |
| `totalTokensClaimable` | BigInt | Total tokens that are settled and claimable |
| `totalTokensLocked` | BigInt | Total tokens that are currently locked or withdrawable in the network from unstaking/undelegating |
| `totalTokensDelegated` | BigInt | Total delegated tokens in the protocol |
| `maxDelegators` | BigInt | The max number of delegators per service provider |
| `inDelegationAmount` | BigInt | The minimum amount needed to delegate |
| `undelegateLockupDuration` | BigInt | The minimum number of blocks the user must wait from requesting undelegation to evaluating |
| `removeDelegatorLockupDuration` | BigInt | The minimum number of blocks the user must wait from requesting remove delegator to evaluating |
| `removeDelegatorEvalDuration` | BigInt | Evaluation period for a remove delegator request |
| `decreaseStakeLockupDuration` | BigInt | Number of blocks a decrease stake request is in lockup before evaluation is allowed |
| `updateDeployerCutLockupDuration` | BigInt | Number of blocks an update deployer cut request is in lockup before evaluation is allowed |
| `fundingRoundBlockDiff` | BigInt | |
| `fundingAmount` | BigInt | |
| `recurringCommunityFundingAmount` | BigInt | |
| `communityPoolAddress` | Bytes | address |
| `votingQuorumPercent` | BigInt | |
| `votingPeriod` | BigInt | |
| `executionDelay` | BigInt | |
| `maxInProgressProposals` | Int | |
| `guardianAddress` | Bytes | |
| `requestCount` | BigInt | |
| `totalStaked` | BigInt | |
***
### ClaimEvent
Description:
| Field | Type | Description |
| ------------- | ------ | ----------- |
| `id` | ID | |
| `claimer` | User | |
| `rewards` | BigInt | |
| `newTotal` | BigInt | |
| `blockNumber` | BigInt | |
***
### ClaimProcessedEvent
Description:
| Field | Type | Description |
| ------------- | ------ | ----------- |
| `id` | ID | |
| `rewards` | BigInt | |
| `claimer` | User | |
| `oldTotal` | BigInt | |
| `newTotal` | BigInt | |
| `blockNumber` | BigInt | |
***
### ClaimRound
Description:
| Field | Type | Description |
| ------------- | ------ | ---------------- |
| `id` | ID | The round number |
| `fundAmount` | BigInt | |
| `blockNumber` | BigInt | |
***
### DecreaseStakeEvent
Description:
| Field | Type | Description |
| -------------------- | ------------ | ----------- |
| `id` | ID | |
| `status` | LockupStatus | |
| `owner` | User | |
| `expiryBlock` | BigInt | |
| `createdBlockNumber` | BigInt | |
| `endedBlockNumber` | BigInt | |
| `decreaseAmount` | BigInt | |
| `newStakeAmount` | BigInt | |
***
### Delegate
Description:
| Field | Type | Description |
| ----------------- | ------ | ---------------------------------------------------------------- |
| `id` | ID | ID - generated w/ the service provider's & delegator's addresses |
| `id` | ID | ID - generated w/ the service provider's & delegator's addresses |
| `claimableAmount` | BigInt | The amount delegated minus the pending decrease delegation |
| `amount` | BigInt | The amount delegated |
| `fromUser` | User | Reference to the user sending/delegating tokens |
| `toUser` | User | Reference to the user receiving delegation |
***
### DeregisterProviderServicerEvent
Description:
| Field | Type | Description |
| --------------- | ----------- | ----------- |
| `id` | ID | |
| `type` | ServiceType | |
| `spId` | BigInt | |
| `node` | ServiceNode | |
| `owner` | User | |
| `endpoint` | String | |
| `unstakeAmount` | BigInt | |
| `blockNumber` | BigInt | |
***
### GuardianTransactionExecutedEvent
Description:
| Field | Type | Description |
| ----------------------- | ------ | ----------- |
| `id` | ID | |
| `targetContractAddress` | Bytes | |
| `callValue` | BigInt | |
| `functionSignature` | String | |
| `callData` | Bytes | |
| `returnData` | Bytes | |
| `blockNumber` | BigInt | |
***
### IncreasedDelegatedStakeEvent
Description:
| Field | Type | Description |
| ----------------- | ------ | ----------- |
| `id` | ID | |
| `delegator` | User | |
| `serviceProvider` | User | |
| `increaseAmount` | BigInt | |
| `blockNumber` | BigInt | |
***
### IncreasedStakeEvent
Description:
| Field | Type | Description |
| ---------------- | ------ | ----------- |
| `id` | ID | |
| `owner` | User | |
| `newStakeAmount` | BigInt | |
| `increaseAmount` | BigInt | |
| `blockNumber` | BigInt | |
***
### Proposal
Description:
| Field | Type | Description |
| --------------------------- | ------------- | ------------------------------------------------------------------------------ |
| `id` | ID | Proposal ID from the event (auto-incrementing) |
| `name` | String | Proposal name |
| `description` | String | Proposal description |
| `proposer` | User | Reference to the user submitting the proposal |
| `submissionBlockNumber` | BigInt | |
| `targetContractRegistryKey` | Bytes | |
| `targetContractAddress` | Bytes | |
| `callValue` | BigInt | |
| `functionSignature` | String | |
| `callData` | Bytes | |
| `outcome` | Outcome | TODO: convert int to enum - Outcome |
| `voteMagnitudeYes` | BigInt | Total vote weight for 'Yes' |
| `voteMagnitudeNo` | BigInt | Total vote weight for 'No' |
| `numVotes` | BigInt | Number of votes |
| `votes` | [Vote](#vote) | Derived from `field: "proposal"` - Reference to the votes - user & vote weight |
***
### ProposalOutcomeEvaluatedEvent
Description:
| Field | Type | Description |
| ----------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `outcome` | Outcome | |
| `voteMagnitueYes` | BigInt | |
| `voteMagnitudeNo` | BigInt | |
| `numVotes` | BigInt | |
| `blockNumber` | BigInt | |
***
### ProposalSubmittedEvent
Description:
| Field | Type | Description |
| ------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `proposer` | User | |
| `name` | String | |
| `description` | String | |
***
### ProposalTransactionExecutedEvent
Description:
| Field | Type | Description |
| ------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `success` | Boolean | |
| `returnData` | Bytes | |
| `blockNumber` | BigInt | |
***
### ProposalVoteSubmittedEvent
Description:
| Field | Type | Description |
| ------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `voter` | User | |
| `vote` | Vote | |
| `currentVote` | VoteType | |
| `voterStake` | BigInt | |
| `blockNumber` | BigInt | |
***
### ProposalVoteUpdatedEvent
Description:
| Field | Type | Description |
| -------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `voter` | User | |
| `vote` | Vote | |
| `voterStake` | BigInt | |
| `currentVote` | VoteType | |
| `previousVote` | VoteType | |
| `blockNumber` | BigInt | |
***
### ProposalVetoedEvent
Description:
| Field | Type | Description |
| ------------- | -------- | ----------- |
| `id` | ID | |
| `proposal` | Proposal | |
| `blockNumber` | BigInt | |
***
### RegisterProviderServicerEvent
Description:
| Field | Type | Description |
| ------------- | ----------- | ----------- |
| `id` | ID | |
| `type` | ServiceType | |
| `spId` | BigInt | |
| `node` | ServiceNode | |
| `owner` | User | |
| `endpoint` | String | |
| `stakeAmount` | BigInt | |
| `blockNumber` | BigInt | |
***
### RemoveDelegatorEvent
Description:
| Field | Type | Description |
| -------------------- | ------------ | ----------- |
| `id` | ID | |
| `status` | LockupStatus | |
| `owner` | User | |
| `expiryBlock` | BigInt | |
| `createdBlockNumber` | BigInt | |
| `endedBlockNumber` | BigInt | |
| `updatedCut` | BigInt | |
| `delegator` | User | |
***
### ServiceNode
Description:
| Field | Type | Description |
| --------------------- | ----------- | --------------------------------------------------------------------------- |
| `id` | ID | ID - generated from service-type and spID |
| `spId` | BigInt | Service provider ID - autoincrementing id created for each new service node |
| `owner` | User | Reference to user that registered this service |
| `type` | ServiceType | Reference to the service type |
| `endpoint` | String | URI to access the service node |
| `delegateOwnerWallet` | Bytes | Address used to confirm the ownership of the service node |
| `createdAt` | Int | When the service node was created |
| `isRegistered` | Boolean | Boolean if th service is registered/deregistered |
***
### ServiceType
Description:
| Field | Type | Description |
| ---------- | ----------------------------------------- | ---------------------------------------- |
| `id` | ID | The type of the service ie. creator-node |
| `isValid` | Boolean | If the service is removed of not |
| `minStake` | BigInt | Minimum Token Stake to run the service |
| `maxStake` | BigInt | Max Token Stake to run the service |
| `versions` | [ServiceTypeVersion](#servicetypeversion) | Derived from `field: "serviceType"` |
***
### ServiceTypeVersion
Description:
| Field | Type | Description |
| ---------------- | ----------- | ----------- |
| `id` | ID | |
| `serviceType` | ServiceType | |
| `serviceVersion` | String | |
| `blockNumber` | BigInt | |
***
### SlashEvent
Description:
| Field | Type | Description |
| ------------- | ------ | ----------- |
| `id` | ID | |
| `target` | User | |
| `amount` | BigInt | |
| `newTotal` | BigInt | |
| `blockNumber` | BigInt | |
***
### UndelegateStakeEvent
Description:
| Field | Type | Description |
| -------------------- | ------------ | ----------- |
| `id` | ID | |
| `status` | LockupStatus | |
| `owner` | User | |
| `expiryBlock` | BigInt | |
| `createdBlockNumber` | BigInt | |
| `endedBlockNumber` | BigInt | |
| `serviceProvider` | User | |
| `amount` | BigInt | |
***
### UpdateDeployerCutEvent
Description: implements LockupEvent
| Field | Type | Description |
| -------------------- | ------------ | ----------- |
| `id` | ID | |
| `status` | LockupStatus | |
| `owner` | User | |
| `expiryBlock` | BigInt | |
| `createdBlockNumber` | BigInt | |
| `endedBlockNumber` | BigInt | |
| `updatedCut` | BigInt | |
***
### User
Description:
| Field | Type | Description |
| ----------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------ |
| `id` | ID | Eth address of User |
| `balance` | BigInt | Token balance |
| `totalClaimableAmount` | BigInt | The total staked/delegated minus pending decrease stake/delegation |
| `claimableStakeAmount` | BigInt | The total staked minus pending decrease stake |
| `claimableDelegationReceivedAmount` | BigInt | The total delegation received from other users minus their pending decrease delegation |
| `claimableDelegationSentAmount` | BigInt | The total delegation sent to other users minus my own pending decrease delegation |
| `stakeAmount` | BigInt | The total staked |
| `delegationReceivedAmount` | BigInt | The total delegated |
| `delegationSentAmount` | BigInt | The total delegation sent |
| `hasStakeOrDelegation` | Boolean | Boolean set to true if the user has stake or delegation |
| `validBounds` | Boolean | If the user's stake is between the min/max stake |
| `deployerCut` | BigInt | The percentage of the claim from the delegator that the deployer takes |
| `services` | [ServiceNode](#servicenode) | Derived from `field: "owner"` - List of services operated by the user |
| `minAccountStake` | BigInt | Max stake of the user as determined by number of services and service types |
| `maxAccountStake` | BigInt | Min stake of the user as determined by number of services and service types |
| `delegateTo` | [Delegate](#delegate) | Derived from `field: "fromUser"` - Reference to delegations (user & amount) sent by user |
| `delegateFrom` | [Delegate](#delegate) | Derived from `field: "toUser"` - Reference to delegations (user & amount) received by user |
| `pendingDecreaseStake` | DecreaseStakeEvent | Reference to request to pending decrease stake |
| `pendingRemoveDelegator` | RemoveDelegatorEvent | DEPRECATED: Use event with service operator and delegator id |
| `pendingUpdateDeployerCut` | UpdateDeployerCutEvent | Reference to request to update deployer cut |
| `pendingUndelegateStake` | UndelegateStakeEvent | Reference to request to update undelegate stake |
| `votes` | [Vote](#vote) | Derived from `field: "voter"` - Reference to votes by the user |
| `createdAt` | BigInt | |
***
### Vote
Description:
| Field | Type | Description |
| -------------------- | -------- | ------------------------------------------------ |
| `id` | ID | ID - generated from proposal id and user address |
| `proposal` | Proposal | Reference to the proposal |
| `vote` | VoteType | TODO: update to enum - the voter's vote |
| `magnitude` | BigInt | The vote weight - the voter's claimable stake |
| `voter` | User | Reference the the user submitting the voter |
| `createdBlockNumber` | BigInt | The block number the vote was created |
| `updatedBlockNumber` | BigInt | The block number the vote was updated |
***
import ApiReference from '../../components/ApiReference.jsx'