# Audius Developer Documentation
> Audius is a fully decentralized music platform. Build on the largest open music catalog on the internet.
> Source: https://docs.audius.co
---
## Audius API Reference
Source: https://docs.audius.co/api
---
## Getting Started
Source: https://docs.audius.co/developers/guides/create-audius-app
# Getting Started
## 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 Open Audio 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].
> **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/
---
## Gate Release Access
Source: https://docs.audius.co/developers/guides/gate-release-access
# Gate Release Access
**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).
---
## Hedgehog
Source: https://docs.audius.co/developers/guides/hedgehog
# Hedgehog
> 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).
---
## Image Loading & Mirrors
Source: https://docs.audius.co/developers/guides/image-mirrors
# Image Loading & Mirrors
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.
---
## Link Audius Account to Protocol Dashboard
Source: https://docs.audius.co/developers/guides/link-audius-account-to-protocol-dashboard
# Link Audius Account to Protocol Dashboard
> Help other users identify you by connecting your [Audius][audius-co] account to the [Open Audio
> 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 [Open Audio 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
> **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 Open Audio 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/
---
## Log In With Audius
Source: https://docs.audius.co/developers/guides/log-in-with-audius
# Log In with Audius
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.
The SDK implements the **OAuth 2.0 Authorization Code Flow with PKCE** — no backend server or
client secret is required.
## 1. Get an API Key
Create a developer app and obtain an API key from either:
- **In-app settings** — [audius.co/settings](https://audius.co/settings) → Developer Apps
- **Developer portal** — [api.audius.co/plans](https://api.audius.co/plans)
## 2. Register a Redirect URI
On the same page, register the **redirect URI(s)** your app will use. Audius validates the redirect
URI in every authorization request against this list.
- **Web popup / full-page redirect**: register your callback page URL (e.g. `https://yourapp.com/callback`)
- **Mobile (React Native)**: register a custom URL scheme (e.g. `myapp://oauth/callback`)
- **Local development**: register `http://localhost:PORT` (or the specific path you use)
## 3. Initialize the SDK
```ts
const audiusSdk = sdk({
appName: 'My App',
apiKey: 'YOUR_API_KEY',
redirectUri: 'https://yourapp.com/callback',
})
```
On **React Native / Expo** (requires React Native 0.71+ / Expo SDK 48+), import from `@audius/sdk`
as normal — the native entry point is resolved automatically and configures `AsyncStorage`-backed
token persistence and `expo-web-browser` for the OAuth browser session out of the box. Make sure
both peer dependencies are installed:
```sh
npx expo install expo-web-browser @react-native-async-storage/async-storage
```
## 4. Log the User In
Call `login()` with a registered `redirectUri`. The SDK runs the full PKCE exchange and stores the
resulting access and refresh tokens automatically.
```ts
await audiusSdk.oauth.login({ scope: 'write' })
const user = await audiusSdk.oauth.getUser()
console.log('Signed in as', user.name)
```
> **Note**
The `write` scope grants permission to perform most actions on the user's behalf (upload, favorite,
etc.) but does **not** allow access to DMs or wallets. Use `'read'` if your app only needs profile
info.
## 5. Handle the Callback (web only)
On your callback page (the `redirectUri`), initialize the SDK and call `handleRedirect()`. The SDK
handles both flows automatically:
- **Popup**: Detects `window.opener`, forwards the authorization code to the parent window, and
closes the popup. The parent's `login()` promise resolves.
- **Full-page redirect**: Performs the PKCE token exchange and stores the tokens. Call `getUser()`
to retrieve the profile.
```ts
const audiusSdk = sdk({ appName: 'My App', apiKey: 'YOUR_API_KEY' })
await audiusSdk.oauth.handleRedirect()
// Popup: closes automatically, login() in parent resolves
// Full-page redirect: token exchange complete, call getUser() next
```
> **Tip**
On **mobile**, `handleRedirect()` is called automatically inside `login()` — your app does not
need to call it at all.
> **Tip**
For the **popup flow**, the popup window must load your callback page before `handleRedirect()` can
run. If your app has a large JavaScript bundle, users will see a spinner in the popup while it
loads. To avoid this, use a dedicated lightweight callback page at your `redirectUri` that only
initializes the SDK and calls `handleRedirect()` — rather than loading your full app.
## 6. Restore an Existing Session
On page/app load, check `isAuthenticated()` to avoid prompting the user to log in again.
```ts
if (await audiusSdk.oauth.isAuthenticated()) {
const user = await audiusSdk.oauth.getUser()
// restore UI
}
```
## Full Examples
See the
[web upload example](https://github.com/AudiusProject/audius-protocol/tree/main/packages/web/examples/upload)
and the
[React Native upload example](https://github.com/AudiusProject/audius-protocol/tree/main/packages/mobile/examples/upload)
for complete, runnable apps that sign in with OAuth and upload a track — no backend required.
## 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
Note that this flow **CANNOT**:
- Manage the user's login session on your app
## Manual Implementation
If you are not able to use the Audius JavaScript SDK, you may implement the OAuth 2.0 Authorization
Code Flow with PKCE manually. All OAuth endpoints are on the Audius API at
`https://api.audius.co/v1`.
### 1. Generate PKCE parameters
Before opening the consent screen, generate two values client-side:
- **`code_verifier`** — a cryptographically random URL-safe string, 43–128 characters
- **`code_challenge`** — `BASE64URL(SHA256(code_verifier))`
- **`state`** — a random string for CSRF protection; store it so you can verify it on the redirect
```js
// Example using the Web Crypto API
async function generatePkce() {
const array = new Uint8Array(32)
crypto.getRandomValues(array)
const codeVerifier = btoa(String.fromCharCode(...array))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
const encoded = new TextEncoder().encode(codeVerifier)
const digest = await crypto.subtle.digest('SHA-256', encoded)
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
return { codeVerifier, codeChallenge }
}
```
### 2. Open the consent screen
Direct the user to:
```noInline
https://api.audius.co/v1/oauth/authorize
?response_type=code
&scope=read
&api_key=YOUR_API_KEY
&redirect_uri=https://mydemoapp.com/callback
&state=YOUR_STATE
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256
```
**Required params**
- `response_type` — always `code`
- `scope` — `"read"` or `"write"`
- `api_key` — your Audius API key (use `app_name` instead if you only need read scope and don't have an API key)
- `redirect_uri` — must match a URI registered in your developer app settings. Validation rules:
- Must use `http` or `https`
- Hosts cannot be raw IP addresses (localhost IPs are allowed)
- Cannot contain `#`, `userinfo`, or path traversal (`/..`, `\..`)
- `state` — your CSRF token; Audius returns this unchanged in the redirect
- `code_challenge` — BASE64URL(SHA256(code_verifier))
- `code_challenge_method` — always `S256`
**Optional params**
- `response_mode` — `"fragment"` (default) or `"query"` — how params are returned in the redirect URL
- `display` — `"popup"` (default) or `"fullScreen"`
> **Remember to handle early exiting**
If the user closes the window before completing the flow, your app should detect this and update the
UI accordingly.
### 3. Receive the authorization code
After the user approves, Audius redirects to your `redirect_uri` with `code` and `state` as URI
fragment params (or query params if you set `response_mode=query`):
```noInline
https://mydemoapp.com/callback
#code=AUTH_CODE
&state=YOUR_STATE
```
**Verify that the `state` value matches what you sent.** If it doesn't, abort — this may indicate a
CSRF attack.
### 4. Exchange the code for tokens
POST to `https://api.audius.co/v1/oauth/token` with the authorization code and your PKCE verifier:
```http
POST https://api.audius.co/v1/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "AUTH_CODE",
"code_verifier": "YOUR_CODE_VERIFIER",
"client_id": "YOUR_API_KEY",
"redirect_uri": "https://mydemoapp.com/callback"
}
```
**Success response (200)**
```json
{
"access_token": "...",
"refresh_token": "..."
}
```
Store both tokens. The access token is a short-lived Bearer token; the refresh token is used to
obtain new access tokens without re-prompting the user.
**Error response**
Non-2xx responses include an `error` and `error_description` field in the JSON body.
### 5. Get the user's profile
```http
GET https://api.audius.co/v1/me
Authorization: Bearer ACCESS_TOKEN
```
**Success response (200)**
```ts
{
userId: number // unique Audius user identifier
name: string // display name
handle: string
verified: boolean // Audius verified checkmark
profilePicture?: {
'150x150': string
'480x480': string
'1000x1000': string
mirrors: string[]
}
}
```
### 6. Refresh the access token
When the access token expires, exchange the refresh token for a new one:
```http
POST https://api.audius.co/v1/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_API_KEY"
}
```
Returns the same `{ access_token, refresh_token }` shape as step 4.
### 7. Revoke the token (logout)
To log the user out, revoke the refresh token server-side then discard both tokens from your storage:
```http
POST https://api.audius.co/v1/oauth/revoke
Content-Type: application/json
{
"token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_API_KEY"
}
```
Per [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009), revocation errors are non-fatal — if the request fails, discard the tokens locally regardless.
## API Reference
For full SDK method documentation (`login`, `getUser`, `handleRedirect`, `isAuthenticated`,
`logout`, and more), see the [OAuth API reference](/sdk/oauth).
---
## Subgraph
Source: https://docs.audius.co/developers/guides/subgraph
# Subgraph
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 |
---
---
## Getting Started
Source: https://docs.audius.co/developers/introduction/overview
{/* 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.
## 📤 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.
- [Examples](/developers/guides/log-in-with-audius#full-examples) - Complete, runnable apps that
sign in with OAuth and upload a track.
## 🦔 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.
---
## Developer Resources
Source: https://docs.audius.co/developers/introduction/resources
> **Work in progress**
This page will not be published to builds. it is available when run locally only.
> content goes here
---
## Using ddex.audius.co
Source: https://docs.audius.co/distributors/hosted/using-ddex-audius-co
## Overview
[ddex.audius.co](https://ddex.audius.co) is the hosted DDEX ingestion tool operated by Tiki Labs on
behalf of the Open Audio Protocol. Once you've been onboarded as a distributor, you can log in to view,
manage, and track all of your releases.
To get started, reach out to **ddex-support@audius.co** or submit an
[intake form](https://forms.gle/jCwLLWRJY7fCQM5ZA).
## How It Works
After onboarding, you deliver DDEX XML files and associated assets (audio, artwork) to a provided S3
or SFTP bucket. The hosted tool automatically parses your deliveries, validates them against the
Audius DDEX specification, and queues them for publishing.
From the web interface at [ddex.audius.co](https://ddex.audius.co) you can:
- View all parsed releases and their current status (pending, published, failed)
- Inspect validation errors and fix delivery issues
- Manually trigger or retry publishing
- Manage artist account associations
- View sales reports and delivery history
## Publishing Modes
There are two modes for how releases get published to artist accounts on Audius:
### 1. Artist-Authorized (via distro.audius.co)
In this mode, artists individually authorize your distributor to publish on their behalf by visiting
[distro.audius.co](https://distro.audius.co) and granting access to their Audius account via OAuth.
Once an artist has authorized your distributor:
- Their Audius account is linked to the artist name in your DDEX deliveries
- Releases are automatically published to the artist's existing Audius account
- The artist retains full control of their account and can revoke access at any time
This mode is best when artists already have Audius accounts and want to maintain direct control over
their profiles.
### 2. Auto-Publish with Magic Link
In this mode, the tool automatically publishes releases without requiring prior artist authorization.
When a release is delivered:
- A new claimable account is created on Audius for the artist (if one doesn't already exist)
- The release is published immediately to that account
- The distributor receives a **magic link** that can be passed to the artist
- The artist uses the magic link to claim ownership of their Audius account and manage their profile
going forward
This mode is best for distributors who want to streamline onboarding — releases go live immediately,
and artists can claim their accounts at their convenience.
> **Choosing a Mode**
The publishing mode is configured per-source during onboarding. You can use artist-authorized mode
for some catalogs and auto-publish for others. Talk to the Tiki Labs team about which mode is best
for your workflow.
---
## Distributing Content to Audius
Source: https://docs.audius.co/distributors/introduction/overview
> **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
> **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/ddex-processor).
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
[GitHub](https://github.com/AudiusProject/ddex-processor/tree/main/fixtures).
- [Delivery](https://github.com/AudiusProject/ddex-processor/blob/main/fixtures/01_delivery.xml)
- [Update](https://github.com/AudiusProject/ddex-processor/blob/main/fixtures/02_update.xml)
- [Delete (Purge Message)](https://github.com/AudiusProject/ddex-processor/blob/main/fixtures/03_delete.xml)
## Partner Onboarding
When delivering content to Audius, there are three options:
1. Use the hosted [ddex.audius.co](https://ddex.audius.co) tool after onboarding with Tiki Labs —
see [Using ddex.audius.co](/distributors/hosted/using-ddex-audius-co) for details.
2. Run the open source [ddex-processor](https://github.com/AudiusProject/ddex-processor) yourself —
see [Self Serve](/distributors/self-serve/overview) for details.
3. 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. facilitates distributing DDEX content directly to the Open Audio Protocol on behalf
of partners.
In order to get started with Tiki Labs, Inc. directly, reach out to 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.
---
## Self-Serve DDEX Ingestion
Source: https://docs.audius.co/distributors/self-serve/overview
## Overview
The backend powering [ddex.audius.co](https://ddex.audius.co) is fully open source and published as
the [ddex-processor](https://github.com/AudiusProject/ddex-processor) — a Node.js application that
parses DDEX XML deliveries and publishes music to Audius via the [Audius SDK](/sdk). You can run your
own instance to manage DDEX ingestion independently.
It includes:
- **S3 polling** — automatically pulls new deliveries from configured S3 buckets
- **DDEX XML parsing** — supports ERN 3.8 and ERN 4.0 message formats
- **Web UI** — browser-based interface for managing releases, users, and publishing status
- **CLI** — command-line tools for parsing, publishing, and debugging
- **Auto-publish** — optionally publish to claimable accounts without prior artist authorization
- **OAuth artist linking** — artists authorize your app to publish on their behalf
- **Reporting** — sales reports and content lifecycle management (CLM) reports
To get started, see [Running the DDEX Processor](/distributors/self-serve/run-a-ddex-server).
---
## Running the DDEX Processor
Source: https://docs.audius.co/distributors/self-serve/run-a-ddex-server
## Prerequisites
- **Docker**
- An S3-compatible bucket containing your DDEX deliveries
- AWS credentials (key + secret) with read access to the bucket
- An Audius SDK API key and secret (obtain from the
[Audius Developer Portal](https://audius.org/developer-apps))
- A PostgreSQL database
## Quick Start
The ddex-processor is published as a Docker image at `audius/ddex:latest`. You can run it directly:
```bash
docker run -d \
-e DDEX_DATABASE_URL=postgres://postgres:postgres@host.docker.internal:5432/ddex \
-e DDEX_PORT=8989 \
-e COOKIE_SECRET=your-random-secret \
-e ADMIN_HANDLES=your-audius-handle \
-p 8989:8989 \
-v $(pwd)/sources.json:/app/sources.json \
audius/ddex:latest
```
Configure your `sources.json` before starting (see [Source Configuration](#source-configuration)
below).
The web UI will be available at `http://localhost:8989`.
## Source Configuration
Sources define where DDEX deliveries come from and how they should be published. Create a
`sources.json` file in the project root:
```json
{
"sources": [
{
"env": "production",
"name": "my-label",
"ddexKey": "your-audius-app-api-key",
"ddexSecret": "your-audius-app-api-secret",
"autoPublish": false,
"awsKey": "your-aws-access-key",
"awsSecret": "your-aws-secret-key",
"awsRegion": "us-east-1",
"awsBucket": "my-ddex-deliveries"
}
]
}
```
### Source Fields
| Field | Required | Description |
|-------|----------|-------------|
| `env` | Yes | Audius environment: `production`, `staging`, or `development` |
| `name` | Yes | Unique identifier for this source |
| `ddexKey` | Yes | Your Audius app API key (used for OAuth and SDK publishing) |
| `ddexSecret` | Yes | Your Audius app API secret |
| `autoPublish` | No | When `true`, automatically publishes to claimable accounts without artist authorization. Default: `false` |
| `awsKey` | Yes | AWS access key for S3 bucket |
| `awsSecret` | Yes | AWS secret key for S3 bucket |
| `awsRegion` | Yes | AWS region of the S3 bucket |
| `awsBucket` | Yes | S3 bucket name containing DDEX deliveries |
| `payoutUserId` | No | Encoded Audius user ID to receive payouts for paid releases |
| `labelUserIds` | No | Object mapping label names to encoded Audius user IDs for payout splits |
### Publishing Modes
**Artist-Authorized (`autoPublish: false`)**
Artists authorize your app via OAuth at `distro.audius.co`. The processor polls for authorized users
and matches them to artist names in DDEX deliveries. Releases are only published once an artist has
granted permission.
**Auto-Publish (`autoPublish: true`)**
Releases are published immediately to claimable accounts. A magic link is generated that the
distributor can pass to the artist, allowing them to claim their Audius account.
## Architecture
The ddex-processor runs as two main processes — a **worker** and a **server** — backed by PostgreSQL.
### Worker
The worker runs on a 5-minute loop and handles:
1. **S3 Polling** — scans configured S3 buckets for new DDEX XML files and assets using incremental
marker-based pagination
2. **XML Parsing** — parses `NewReleaseMessage` (deliveries/updates) and `PurgeReleaseMessage`
(takedowns) in ERN 3.8 or ERN 4.0 format
3. **User Polling** — checks for newly authorized artists via the Audius API
4. **Publishing** — publishes pending releases via the Audius SDK when all preconditions are met
(artist authorized or auto-publish enabled, release date reached, assets available)
5. **Reporting** — generates CLM reports and processes LSR files
### Server
The Hono-based web server provides:
- A management UI for viewing and administering releases
- OAuth login for artists to authorize publishing
- Manual publish/retry controls
- Sales and delivery reporting
- Admin access controlled by the `ADMIN_HANDLES` environment variable
## CLI Commands
The processor includes a CLI for debugging and manual operations:
```bash
# Parse a DDEX XML file and print the result
npx ts-node cli.ts parse
# Publish a specific release to a user
npx ts-node cli.ts publish-to-user
# Publish to a claimable account (auto-publish)
npx ts-node cli.ts publish-to-claimable-account
# Poll S3 for new deliveries
npx ts-node cli.ts poll-s3
# Delete a published release
npx ts-node cli.ts delete
# Start the server only (no background worker)
npx ts-node cli.ts server
# Start the worker only (no web UI)
npx ts-node cli.ts worker
# Start both server and worker
npx ts-node cli.ts start
```
## Delivery Format
The processor expects DDEX deliveries in your S3 bucket as either:
- **ZIP files** containing a DDEX XML file and associated assets (audio files, artwork)
- **Directories** with XML files and assets organized per the DDEX standard
Supported message types:
- `NewReleaseMessage` — deliver new releases or update existing ones
- `PurgeReleaseMessage` — take down (delete) previously published releases
See the [Specification](/distributors/specification/overview) section for details on supported
metadata fields, deal types, and best practices.
## Machine Recommendations
For production deployments, we recommend:
- **OS**: Ubuntu or any Linux distribution with Docker support
- **CPU**: 4+ cores
- **Memory**: 8-16 GB
- **Storage**: Depends on delivery volume — assets are cached locally during processing
## More Information
- **Source code**: [github.com/AudiusProject/ddex-processor](https://github.com/AudiusProject/ddex-processor)
- **Developer README**: See the
[README_DEV.md](https://github.com/AudiusProject/ddex-processor/blob/main/README_DEV.md) for
local development setup without Docker
- **Issues & support**: [github.com/AudiusProject/ddex-processor/issues](https://github.com/AudiusProject/ddex-processor/issues)
---
## Artist Profile Updates
Source: https://docs.audius.co/distributors/specification/artist-profile-updates
Audius supports basic artist profile updates from DDEX MEAD messages. Distributors can send MEAD to
update an artist's Audius profile display name, bio, profile picture, and cover photo without
redelivering a release.
MEAD profile updates are processed separately from ERN release deliveries. They do not create,
update, or takedown tracks or albums.
## Requirements
- The distributor must already be configured as an Audius DDEX source.
- The artist must have authorized the distributor's Audius app before the profile update can be
published.
- The MEAD message must identify the artist by one of:
- `ProprietaryId` with `Namespace="AudiusUserId"`
- `ProprietaryId` with `Namespace="AudiusHandle"`
- a `PartyName` or official `Pseudonym` matching an authorized Audius user for the source app
- `AudiusUserId` values must be Audius API/SDK hash IDs, such as `7eP5n`, not numeric database IDs
or handles.
- Profile and cover images must be delivered as package-relative files. Remote image URLs are not
fetched by the DDEX processor.
- Bio text must be 256 characters or fewer.
If the processor cannot match the MEAD party to an authorized Audius user, the update is stored but
blocked until the artist grants access to the distributor app.
## Supported Fields
| Audius profile field | MEAD source |
| --- | --- |
| Display name | `DisplayName`, `DisplayArtistName`, official `Pseudonym/Name`, or `PartyName` |
| Bio | `Biography/Text` |
| Profile picture | `Image` with `ImageType` such as `ProfilePicture`, `ArtistPhoto`, `Avatar`, or `Headshot` |
| Cover photo | `Image` with `ImageType` such as `ProfileBanner`, `CoverPhoto`, `Header`, or `Background` |
The image `File/URI` should be relative to the XML file or package root, matching the delivery style
used for ERN audio and artwork assets.
## Standalone MEAD Message
This example targets an artist by Audius user ID and updates the display name, bio, and profile
picture.
```xml
artist-profile-update-0012026-06-22T13:00:00ZP-ARTIST-17eP5nArtist OneArtist One DisplaytrueShort artist bio from a standalone MEAD update.IMG-PROFILEimages/profile.jpg
```
## Feed Entry Message
This example uses a MEAD feed entry, targets the artist by authorized Audius handle, and includes
both a profile picture and cover photo.
```xml
artist-profile-feed-0012026-06-22T14:00:00ZP-ARTIST-2artisttwoArtist TwoArtist Two DeluxeBio update sent as an entry in a larger MEAD feed.images/artist-two-profile.pngimages/artist-two-banner.jpg
```
## Delivery Notes
- Send MEAD XML through the same configured delivery bucket or ingestion path used for DDEX.
- Use a fresh `MessageId` and `MessageCreatedDateTime` for each update so redeliveries are not
treated as stale.
- Include profile image files alongside the MEAD XML. For example, if the XML references
`images/profile.jpg`, the delivered package should contain that file at `images/profile.jpg`.
- Do not send `https://` image URLs. The processor intentionally rejects remote image fetches.
---
## Recommended Deal Struture
Source: https://docs.audius.co/distributors/specification/deal-types/recommended
# 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 |
> **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
FreeOfChargeModelOnDemandStreamWorldwide2023-09-02PayAsYouGoModelPermanentDownloadWorldwide0.92023-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
PayAsYouGoModelOnDemandStreamPermanentDownloadWorldwide0.92023-09-02
```
---
## Supported Deal Types
Source: https://docs.audius.co/distributors/specification/deal-types/supported-deal-types
> **Further Reading**
Checkout the
[DDEX ERN3 Knowledge Base]()
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
`ddex-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
FreeOfChargeModelOnDemandStreamWorldwide2023-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
PayAsYouGoModelOnDemandStreamWorldwide1.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
UserDefinedOnDemandStreamWorldwide2023-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
UserDefinedOnDemandStreamWorldwide2023-09-02eth
0xAbCdEfGhIjKlMnOpQrStUvWxYz
ERC-721Example NFTexample-nfthttps://www.example.com/nft-image.pnghttps://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
UserDefinedOnDemandStreamWorldwide2023-09-02
```
### Free To Download
> **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
FreeOfChargeModelOnDemandStreamPermanentDownloadWorldwide2023-09-02
```
### Pay Gated Download
> **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
PayAsYouGoModelOnDemandStreamPermanentDownloadWorldwide1.0
...
2023-09-02
```
### Follow Gated Download
> **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
UserDefinedOnDemandStreamPermanentDownloadWorldwide2023-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
FreeOfChargeModelOnDemandStreamWorldwide2023-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
PayAsYouGoModelOnDemandStreamWorldwide1.0
...
2023-09-02
```
### Free To Download
> **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
FreeOfChargeModelOnDemandStreamPermanentDownloadWorldwide2023-09-02
```
### Pay Gated Download
> **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
PayAsYouGoModelOnDemandStreamPermanentDownloadWorldwide1.0
...
2023-09-02
```
## ERN4 Support
> **Coming Soon**
Support for ERN4 is coming coming soon.
Checkout the
[DDEX ERN4 Knowledge Base]()
for information in the meantime.
---
## Supported Metadata Mapping
Source: https://docs.audius.co/distributors/specification/metadata
> **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.
---
## DDEX Best Practices
Source: https://docs.audius.co/distributors/specification/overview
## General Guidance
> **Delivery processing**
[TikiLabs](https://tikilabs.com) is a facilitator of distributing DDEX directly to the Open Audio
Protocol.
For inquiries or support, reach out at
[ddex-support@audius.co](mailto:ddex-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.
- Audius supports basic artist profile updates via MEAD. See
[Artist Profile Updates](/distributors/specification/artist-profile-updates) for the supported
display name, bio, profile picture, and cover photo mapping.
---
## Getting Started
Source: https://docs.audius.co/index

## Welcome to the Audius Developer Docs! 🧑💻
Audius is a music streaming service built on top of the
[Open Audio Protocol](https://openaudio.org). These docs are your entry point for building
applications that tap directly into the largest open music catalog on the internet.
If you want to create apps that stream music from the catalog, extend the listening experience, or
invent entirely new audio-native products, you are in the right place. Think of it like building on
a free, open, Spotify-alternative API where the music is permissionless and the ecosystem is yours
to build on.
Explore [Audius Community](https://audius.community) to see a showcase of projects built on the API and SDK.
## Getting Started
- [REST API](/api) - Query, stream, and search for tracks, users & playlists across the network.
- [Javascript SDK](/sdk) - Use the Javascript SDK to build your first Audius app.
- [Quick Start](/developers/guides/create-audius-app) - Run `npx create-audius-app` to get a head
start building.
- [Log in with Audius](/developers/guides/log-in-with-audius) - let users log in to your app with
their Audius account.
Find more how-tos, guide and tutorials like this in the
[Developers](/developers/introduction/overview) section.
---
> **Info**
To read more about the Open Audio Protocol and build directly on the transaction, DDEX, and storage
layer, check out [docs.openaudio.org](https://docs.openaudio.org)
---
## Content Node
Source: https://docs.audius.co/learn/architecture/content-node
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 Open Audio
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
> **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.
---
## Discovery Node
Source: https://docs.audius.co/learn/architecture/discovery-node
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
Open Audio 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
> **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 Open Audio
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).
> **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 */}
---
## Protocol
Source: https://docs.audius.co/learn/concepts/protocol
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 Open Audio 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 Open Audio 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
[Open Audio 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)
---
## Untitled
Source: https://docs.audius.co/learn/concepts/staking-and-delegating
---
title: Staking and Delegating
description: Open Audio Protocol Documentation
## 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.
> **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 Open Audio Protocol
healthy.
> **More Information**
Ready to learn more, check out the [Delegating section](https://docs.openaudio.org) of the
docs.
---
## The $AUDIO Token
Source: https://docs.audius.co/learn/concepts/token
---
title: The $AUDIO Token
description: Open Audio Protocol Documentation
## 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 Open Audio 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.
---
## Governance
Source: https://docs.audius.co/learn/contributing/governance
---
title: Governance
description: Open Audio Protocol Documentation
## 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 Open Audio 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 Overview
Source: https://docs.audius.co/learn/contributing/overview
---
title: Contributing Overview
description: Open Audio Protocol Documentation
## 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.
---
## Ethereum Contracts
Source: https://docs.audius.co/reference/eth-contracts
## Overview
The Audius Ethereum contracts are meant to accomplish the following goals for the Open Audio 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 Open Audio 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 Open Audio 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 Open Audio 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 Open Audio 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 Open Audio Protocol
For every service provider address in Open Audio 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 Open Audio 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
---
## Developer Overview
Source: https://docs.audius.co/reference/overview
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)
---
## Link Audius Account to Protocol Dashboard
Source: https://docs.audius.co/reference/protocol-dashboard/link-profile
> 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 [Open Audio 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
> **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 Open Audio 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/
---
## Solana Programs
Source: https://docs.audius.co/reference/solana-programs
## Programs
> **Testnet on Mainnet?**
Please note that all Open Audio 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)
---
## Whitepaper
Source: https://docs.audius.co/reference/whitepaper
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/)
---
## Albums
Source: https://docs.audius.co/sdk/albums
# Albums
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](/sdk/playlists#createplaylistparams-requestinit) and
[updatePlaylist](/sdk/playlists#updateplaylistparams-requestinit) for details.
---
## Go SDK
Source: https://docs.audius.co/sdk/community-projects/go-sdk
# Go SDK
> **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
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
```
---
## Unreal Engine Plugin
Source: https://docs.audius.co/sdk/community-projects/unreal-engine-plugin
# Unreal Engine Plugin
> **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);
}
```
---
## Javascript SDK
Source: https://docs.audius.co/sdk
# Getting Started with the Audius SDK
## Overview
The Audius JavaScript (TypeScript) SDK allows you to easily interact with the Open Audio 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
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**.
- **API Key** — used in all contexts (frontend and backend). Safe to include in client-side code.
- **Bearer Token** — backend only. Grants your app the ability to act on behalf of users who have authorized it. **Never expose this in browser or mobile code.**
## 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
How you initialize the SDK depends on whether you are running in a **backend** (Node.js) or **frontend** (browser/mobile) context.
### Node.js (backend) example
Include your API key and bearer token. The bearer token enables your app to perform actions on behalf of authorized users.
```js title="In Node.js environment"
const audiusSdk = sdk({
apiKey: 'Your API Key goes here',
bearerToken: 'Your Bearer Token goes here',
})
```
### HTML + JS (frontend) example
In a browser or mobile context, initialize with your **API key only** — no bearer token. User authentication is handled via the [OAuth flow](#log-in-with-audius-oauth) described below.
```js title="In web page"
const audiusSdk = window.audiusSdk({
apiKey: 'Your API Key goes here',
})
```
> **Warning**
**Never include your bearer token in frontend code.** The bearer token allows your app to act on behalf of users who have authorized it. Exposing it in client-side code (browser or mobile) is a critical security risk — anyone who inspects your code could use it to impersonate your app.
For frontend apps, use the [OAuth flow](#log-in-with-audius-oauth) instead.
## 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
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
```
## Log In with Audius (OAuth)
For frontend apps, use the built-in OAuth 2.0 PKCE flow to authenticate users. This lets your users log in with their Audius account and authorize your app to act on their behalf — without exposing your app's bearer token in client-side code.
```js title="In web page"
const audiusSdk = window.audiusSdk({
apiKey: 'Your API Key goes here',
redirectUri: 'https://your-app.com/oauth/callback'
})
// Redirect the user to Audius to log in
audiusSdk.oauth.login()
// On your callback page, handle the redirect:
await audiusSdk.oauth.handleRedirectCallback()
```
After the user logs in, the SDK stores their access token automatically and includes it in subsequent API calls. See the [Log In with Audius guide](/developers/guides/log-in-with-audius) for the full flow.
## What's next?
- [Log in with Audius](/developers/guides/log-in-with-audius) — add OAuth authentication to your frontend app
- [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. The examples below use a bearer token and are intended for **backend/server-side use only** — do not use your bearer token in browser or mobile code.
**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
Source: https://docs.audius.co/sdk/oauth
# OAuth
Audius OAuth lets users authorize your app to perform actions on their behalf (write permissions) or
simply verify their identity and share their profile info (read-only). The SDK implements the
**OAuth 2.0 Authorization Code Flow with PKCE** — no backend server or client secret is required.
For a step-by-step setup guide, see [Log in with Audius](/developers/guides/log-in-with-audius).
## login(`params`)
Opens the Audius consent screen and runs the full PKCE authorization flow.
- **Popup (default)**: Opens a popup window. When the user approves, the popup redirects to
`redirectUri`. On that page, `handleRedirect()` forwards the code to the parent and closes the
popup. The `login()` promise resolves.
- **Full-page redirect**: Navigates the current page to Audius. After the user approves, Audius
redirects back to `redirectUri`. Call `handleRedirect()` there to complete the exchange.
- **Mobile**: Opens an in-app browser session (`ASWebAuthenticationSession` on iOS, Chrome Custom
Tab on Android). The redirect is captured and `handleRedirect()` is called automatically.
`login()` resolves when the token exchange is complete.
Throws if the flow fails or the user cancels.
Example (popup — recommended for web):
```ts
// redirectUri set once in sdk({ redirectUri: '...' })
try {
await audiusSdk.oauth.login({ scope: 'write' })
const user = await audiusSdk.oauth.getUser()
console.log('Signed in as', user.name)
} catch (err) {
console.error('Login failed or cancelled:', err.message)
}
```
Example (mobile):
```ts
// redirectUri set once in sdk({ redirectUri: 'myapp://oauth/callback' })
await audiusSdk.oauth.login({ scope: 'write', display: 'fullScreen' })
const user = await audiusSdk.oauth.getUser()
```
#### Params
| Parameter | Type | Default | Description |
| :----------- | :------------------------ | :----------- | :----------------------------------------------------------------------------------------------------------------------------- |
| scope | `'read' \| 'write'` | `'read'` | `'write'` grants your app permission to act on the user's behalf (upload, favorite, etc.). `'read'` only returns profile info. |
| redirectUri | `string` | SDK config | The registered redirect URI where Audius sends the user after consent. Falls back to `redirectUri` in the SDK config. |
| display | `'popup' \| 'fullScreen'` | `'popup'` | Whether to open the consent screen in a popup window or redirect the current page. |
| responseMode | `'fragment' \| 'query'` | `'fragment'` | How the authorization response params are delivered in the redirect URL. |
> **Note**
The `write` scope grants permission to perform most actions on the user's behalf but does **not**
allow access to DMs or wallets.
#### Returns
Returns `Promise`.
## getUser()
Fetches the authenticated user's profile from the server using the stored access token. Always
makes a network request so the result reflects current server-side state.
Throws `ResponseError` (4xx/5xx) or `FetchError` (network failure) if the request fails. A `401`
typically means no token is stored or the session has expired.
Example:
```ts
const user = await audiusSdk.oauth.getUser()
console.log(`@${user.handle}`)
```
#### Returns
Returns `Promise`.
```ts
type DecodedUserToken = {
userId: number // unique Audius user identifier
name: string // display name
handle: string
verified: boolean // Audius verified checkmark
profilePicture?: {
'150x150': string
'480x480': string
'1000x1000': string
mirrors: string[]
}
}
```
## handleRedirect(`url?`)
Completes an OAuth flow by processing the redirect URL. Call this on your callback page after
`login()` redirects there.
- **Popup**: Detects `window.opener`, forwards the authorization code back to the parent window via
`postMessage`, and closes the popup. The parent's `login()` promise resolves.
- **Full-page redirect / mobile**: Verifies the CSRF state, performs the PKCE token exchange, and
stores the tokens. Call `getUser()` afterwards to retrieve the profile.
Pass the redirect URL explicitly (e.g. a mobile deep link URL) or omit to use the current page URL
on web. Is a no-op when no redirect params are present. The result can only be consumed once.
The `redirectUri` used in the token exchange is resolved in this order:
1. The value stored in session state from the originating `login()` call
2. The `redirectUri` set in the top-level SDK config
3. The current page's origin + path (web only, fallback)
On **mobile**, this is called automatically inside `login()` — you do not need to call it.
Example:
```ts
// On your callback page:
const audiusSdk = sdk({ appName: 'My App', apiKey: 'YOUR_API_KEY' })
await audiusSdk.oauth.handleRedirect()
// Popup: closes and resolves login() in parent — nothing more needed here
// Full-page: token stored — call getUser() to get the profile
```
#### Params
| Name | Type | Description | Required? |
| :---- | :------- | :-------------------------------------------------------------------------------- | :--------- |
| `url` | `string` | The redirect URL to process. Defaults to the current page URL on web. | _Optional_ |
#### Returns
Returns `Promise`.
## isAuthenticated()
Returns `true` if an access token is currently stored. Tokens are persisted across sessions
(localStorage on web, AsyncStorage on React Native), so this will be `true` on page/app reload if
the user previously logged in.
Example:
```ts
if (await audiusSdk.oauth.isAuthenticated()) {
const user = await audiusSdk.oauth.getUser()
}
```
#### Returns
Returns `Promise`.
## hasRefreshToken()
Returns `true` if a refresh token is stored and a refresh exchange could be attempted.
Example:
```ts
if (await audiusSdk.oauth.hasRefreshToken()) {
await audiusSdk.oauth.refreshAccessToken()
}
```
#### Returns
Returns `Promise`.
## hasRedirectResult(`url?`)
Synchronous check for whether the given URL (or current page URL on web) contains OAuth redirect
params (`code` + `state`). No network requests or side effects. Useful for showing a loading state
before calling `handleRedirect()`.
Example:
```ts
if (audiusSdk.oauth.hasRedirectResult()) {
showLoadingSpinner()
await audiusSdk.oauth.handleRedirect()
hideLoadingSpinner()
}
```
#### Params
| Name | Type | Description | Required? |
| :---- | :------- | :----------------------------------------------------------------------------- | :--------- |
| `url` | `string` | The URL to check. Defaults to the current page URL on web. | _Optional_ |
#### Returns
Returns `boolean`.
## refreshAccessToken()
Refreshes the access token using the stored refresh token. The SDK calls this automatically when
API requests return `401`, but you can trigger it manually if needed.
Example:
```ts
const newToken = await audiusSdk.oauth.refreshAccessToken()
if (!newToken) {
// Prompt the user to log in again
}
```
#### Returns
Returns `Promise` — the new access token, or `null` if the refresh failed.
## logout()
Revokes the current refresh token server-side and clears all stored tokens and PKCE session state.
After calling this, all SDK API calls revert to unauthenticated.
Example:
```ts
await audiusSdk.oauth.logout()
```
#### Returns
Returns `Promise`.
---
## Playlists
Source: https://docs.audius.co/sdk/playlists
# Playlists
## getPlaylist(`params`, `requestInit?`)
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** |
| `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
[`PlaylistResponse`](#playlistresponse) object.
## getBulkPlaylists(`params`, `requestInit?`)
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_ |
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
[`PlaylistResponse`](#playlistresponse) objects.
## getPlaylistTracks(`params`, `requestInit?`)
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** |
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
The return type is the same as [`getBulkTracks`](tracks#getbulktracksparams-requestinit)
## getTrendingPlaylists(`params?`, `requestInit?`)
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_ |
| `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_ |
| `type` | `string` | The type of trending playlists to return | _Optional_ |
You can pass an optional
[`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) object as the first
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
[`PlaylistResponse`](#playlistresponse) objects.
## searchPlaylists(`params`, `requestInit?`)
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 | _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
[`PlaylistResponse`](#playlistresponse) objects.
## 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(`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(`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(`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(`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(`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(`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) |
---
## Resolve
Source: https://docs.audius.co/sdk/resolve
# Resolve
## resolve(`params`, `requestInit?`)
Resolve a provided Audius app URL to the API resource it represents.
Example:
```ts
const { data: track } = await audiusSdk.resolve