Documentation Index Fetch the complete documentation index at: https://apidocs.royalti.io/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Learn practical workflows for common tasks like onboarding collaborators, managing split changes, processing royalties, and handling payments. These examples show how different API endpoints work together to accomplish real-world goals.
What You’ll Learn
Onboarding collaborators - Create users, assign splits, verify earnings
Managing split changes - Update revenue shares and recalculate accounting
Processing royalties - Upload files, check status, distribute earnings
Payment workflows - Generate reports, create payments, update balances
Territory-specific deals - Handle different splits by region
Collaborative projects - Manage multi-contributor revenue sharing
Product delivery - Prepare and deliver releases to DSPs
Asset DDEX management - Manage DDEX metadata for distribution
Workflow 1: Onboarding a New Collaborator
Add a new user to your workspace and configure their revenue share.
Create User Account
Create a user with invitation email. const userResponse = await fetch ( 'https://api.royalti.io/user/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
firstName: 'Sarah' ,
lastName: 'Producer' ,
nickName: 'sarahpro' ,
email: 'sarah@example.com' ,
role: 'user' ,
userType: [ 'producer' , 'artist' ],
sendEmail: true ,
message: 'Welcome to our label workspace!'
})
});
const userData = await userResponse . json ();
const userId = userData . id ;
console . log ( 'User created:' , userId );
user_response = requests.post(
'https://api.royalti.io/user/' ,
headers = headers,
json = {
'firstName' : 'Sarah' ,
'lastName' : 'Producer' ,
'nickName' : 'sarahpro' ,
'email' : 'sarah@example.com' ,
'role' : 'user' ,
'userType' : [ 'producer' , 'artist' ],
'sendEmail' : True ,
'message' : 'Welcome to our label workspace!'
}
)
user_data = user_response.json()
user_id = user_data[ 'id' ]
print ( f 'User created: { user_id } ' )
Assign to Existing Splits
Add the user to existing asset splits. const splitResponse = await fetch (
`https://api.royalti.io/split/ ${ existingSplitId } ` ,
{
method: 'PUT' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
split: [
{
TenantUserId: userId ,
split: 25.0 // 25% share
},
{
TenantUserId: labelUserId ,
split: 75.0 // Remaining 75%
}
]
})
}
);
console . log ( 'Split updated successfully' );
Create Future Release Splits
Set up default splits for upcoming projects. const newSplitResponse = await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
AssetId: newAssetId ,
split: [
{
TenantUserId: userId ,
split: 30.0
},
{
TenantUserId: labelUserId ,
split: 70.0
}
],
startDate: new Date (). toISOString (),
endDate: null // Open-ended
})
});
console . log ( 'Future split configured' );
Verify Earnings Calculation
Check that earnings are calculated correctly for the user. const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats?forceRefresh=true` ,
{
headers: {
'Authorization' : `Bearer ${ token } `
}
}
);
const earnings = await statsResponse . json ();
console . log ( 'User Earnings:' , {
gross: earnings . Royalty_Share ,
paid: earnings . paid ,
due: earnings . due
});
Set Payment Preferences (Optional)
Configure payment settings for the user. await fetch ( 'https://api.royalti.io/payment-setting/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
userId: userId ,
minimumPayment: 50.00 ,
currency: 'USD' ,
paymentMethod: 'bank_transfer' ,
paymentDetails: {
bankName: 'Chase Bank' ,
accountHolderName: 'Sarah Producer'
}
})
});
console . log ( 'Payment settings configured' );
Workflow 2: Managing Revenue Distribution Changes
Handle changes to split agreements while maintaining historical accuracy.
Scenario: Split Terms Changing Mid-Year
When split agreements change, choose the appropriate update method:
Option A: Update Existing Split (Simple Change)
For simple percentage adjustments without time-based conditions:
// Update split percentages (applies to all historical data)
const response = await fetch ( `https://api.royalti.io/split/ ${ splitId } ` , {
method: 'PUT' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
split: [
{
TenantUserId: artistId ,
split: 65.0 // Changed from 60%
},
{
TenantUserId: labelId ,
split: 35.0 // Changed from 40%
}
]
})
});
console . log ( 'Split updated' );
Updating an existing split applies the new percentages to ALL historical data. Use temporal splits (Option B) for changes that should only apply going forward.
Option B: Create Temporal Split (Time-Based Change)
For changes that apply from a specific date forward:
// Create new split effective from specific date
const newSplitResponse = await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
AssetId: assetId ,
split: [
{
TenantUserId: artistId ,
split: 70.0 // New terms starting July 1
},
{
TenantUserId: labelId ,
split: 30.0
}
],
startDate: '2025-07-01' , // Effective date
endDate: null // Open-ended
})
});
console . log ( 'Temporal split created' );
# Create temporal split
new_split_response = requests.post(
'https://api.royalti.io/split/' ,
headers = headers,
json = {
'AssetId' : asset_id,
'split' : [
{ 'TenantUserId' : artist_id, 'split' : 70.0 },
{ 'TenantUserId' : label_id, 'split' : 30.0 }
],
'startDate' : '2025-07-01' ,
'endDate' : None
}
)
print ( 'Temporal split created' )
Refresh Earnings After Changes
After making split changes, refresh accounting data:
// Request recalculation with latest split data
const refreshResponse = await fetch (
`https://api.royalti.io/accounting/users/ ${ userId } /recalculate?forceRefresh=true` ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } `
}
}
);
const { jobId } = await refreshResponse . json ();
// Check updated earnings
const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats` ,
{
headers: {
'Authorization' : `Bearer ${ token } `
}
}
);
const earnings = await statsResponse . json ();
console . log ( 'Updated earnings:' , earnings . due );
Notify Affected Users
After split changes, notify collaborators:
const affectedUserIds = [ artistId , producerId , labelId ];
for ( const userId of affectedUserIds ) {
await fetch ( 'https://api.royalti.io/notifications/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
recipientIds: [ userId ],
type: 'split_updated' ,
message: 'Your revenue split terms have been updated' ,
metadata: {
assetId: assetId ,
effectiveDate: '2025-07-01'
}
})
});
}
console . log ( 'Notifications sent' );
Workflow 3: Processing and Distributing Royalties
Complete workflow from file upload to user earnings distribution.
Upload Royalty File
Upload CSV or ZIP file from streaming platform. // Step 1: Request upload URL
const urlResponse = await fetch ( 'https://api.royalti.io/file/upload-url' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
royaltySource: 'spotify' ,
accountingPeriod: '2024-03-01' ,
salePeriod: '2024-03-01' ,
fileMetaData: {
fileName: 'spotify-march-2024.csv' ,
fileSize: 1024000 ,
mimeType: 'text/csv'
}
})
});
const { uploadUrl , fileId } = await urlResponse . json ();
// Step 2: Upload file
const fileBuffer = fs . readFileSync ( 'spotify-march-2024.csv' );
await fetch ( uploadUrl , {
method: 'PUT' ,
body: fileBuffer
});
// Step 3: Confirm upload
await fetch ( `https://api.royalti.io/file/ ${ fileId } /confirm` , {
method: 'POST' ,
headers: { 'Authorization' : `Bearer ${ token } ` }
});
console . log ( 'File uploaded:' , fileId );
The system automatically recognizes the file format and reporting period from the file contents and metadata.
Monitor Processing
Check file processing status. const statusResponse = await fetch ( `https://api.royalti.io/file/ ${ fileId } ` , {
headers: { 'Authorization' : `Bearer ${ token } ` }
});
const fileStatus = await statusResponse . json ();
console . log ( 'Status:' , fileStatus . status );
console . log ( 'Rows processed:' , fileStatus . rowsProcessed );
// Status: uploaded → processing → completed
Large files may take several minutes to process. Poll the status endpoint every 30-60 seconds.
Verify Earnings Updated
Check that user balances were updated. // Get current balances for all users
const dueResponse = await fetch (
'https://api.royalti.io/accounting/getcurrentdue?sort=due&order=DESC' ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const { Users } = await dueResponse . json ();
// Filter users meeting payment threshold
const eligible = Users . filter ( user => user . Due >= 50 );
console . log ( ` ${ eligible . length } users eligible for payment` );
Review Updated Earnings
Check detailed earnings for specific users. const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats?include=royalty` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const stats = await statsResponse . json ();
console . log ( 'Gross earnings:' , stats . Royalty_Share );
console . log ( 'Total streams:' , stats . royaltySummary . Streams );
console . log ( 'Stream royalty:' , stats . royaltySummary . Streams_Royalty );
console . log ( 'Amount due:' , stats . due );
Workflow 4: Payment Cycle Management
Monthly payment workflow from eligibility check to payment execution.
Generate Current Due Report
Get all users with outstanding balances. const reportResponse = await fetch (
'https://api.royalti.io/accounting/getcurrentdue?size=100' ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const { Users , totalUsers } = await reportResponse . json ();
console . log ( `Total users with balances: ${ totalUsers } ` );
report_response = requests.get(
'https://api.royalti.io/accounting/getcurrentdue?size=100' ,
headers = headers
)
data = report_response.json()
users = data[ 'Users' ]
total_users = data[ 'totalUsers' ]
print ( f 'Total users with balances: { total_users } ' )
Filter by Payment Settings
Filter users who meet minimum threshold and have payment settings. const eligibleUsers = Users . filter ( user => {
const hasMinimum = user . Due >= ( user . paymentSettings ?. minimumPayment || 50 );
const hasSettings = user . paymentSettings !== null ;
const notRequested = ! user . paymentRequested ;
return hasMinimum && hasSettings && notRequested ;
});
console . log ( ` ${ eligibleUsers . length } users eligible for payment` );
Create Payment Batch
Create payment records for eligible users. const payments = await Promise . all (
eligibleUsers . map ( user =>
fetch ( 'https://api.royalti.io/payment/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
user: user . TenantUserId ,
title: 'March 2024 Royalty Payment' ,
transactionDate: new Date (). toISOString (),
currency: 'USD' ,
amount: user . Due ,
amountUSD: user . Due ,
conversionRate: 1.0 ,
memo: 'Monthly royalty distribution'
})
})
)
);
console . log ( ` ${ payments . length } payment records created` );
Process External Payments
After processing payments through your payment provider, update records. for ( const paymentResponse of payments ) {
const payment = await paymentResponse . json ();
// Process through Stripe, PayPal, etc.
// const externalPayment = await stripeClient.transfers.create({...});
// Update payment record with external transaction ID
await fetch ( `https://api.royalti.io/payment/ ${ payment . data . id } ` , {
method: 'PUT' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
memo: `Paid via Stripe: ${ externalTransactionId } `
})
});
}
console . log ( 'Payments processed and recorded' );
Notify Users
Send payment confirmation notifications. await fetch ( 'https://api.royalti.io/notifications/bulk' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
recipientIds: eligibleUsers . map ( u => u . id ),
type: 'payment_completed' ,
message: 'Your royalty payment has been processed'
})
});
console . log ( 'Payment notifications sent' );
Common Use Cases
Use Case 1: Label with Multiple Artists
Set up a label workspace with default splits for all artists.
// Step 1: Configure label-level defaults (70% artist, 30% label)
await fetch ( 'https://api.royalti.io/default-split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
TenantId: tenantId ,
split: [
{ role: 'artist' , split: 70.0 },
{ role: 'label' , split: 30.0 }
]
})
});
// Step 2: Add artists (they'll inherit default splits)
const artistResponse = await fetch ( 'https://api.royalti.io/user/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
firstName: 'Artist' ,
lastName: 'Name' ,
email: 'artist@example.com' ,
userType: 'artist' ,
role: 'user'
})
});
// Step 3: Upload catalog → defaults apply automatically
// Step 4: Process royalties → accounting calculated per artist
Use Case 2: Collaborative Project
Manage splits for a multi-contributor project.
// Step 1: Create project-specific users
const usersResponse = await fetch ( 'https://api.royalti.io/user/bulk' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
users: [
{ firstName: 'Producer' , lastName: 'One' , email: 'producer@example.com' , userType: 'producer' },
{ firstName: 'Engineer' , lastName: 'Two' , email: 'engineer@example.com' , userType: 'engineer' },
{ firstName: 'Artist' , lastName: 'Three' , email: 'artist@example.com' , userType: 'artist' }
],
sendEmail: true
})
});
const users = await usersResponse . json ();
const [ producer , engineer , artist ] = users . data ;
// Step 2: Assign custom splits for this project
await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
ProductId: projectProductId ,
split: [
{ TenantUserId: producer . id , split: 40.0 },
{ TenantUserId: engineer . id , split: 10.0 },
{ TenantUserId: artist . id , split: 50.0 }
]
})
});
// Step 3: All royalties automatically distributed per split
// Step 4: Each user sees their share in accounting
Use Case 3: Territory-Specific Deals
Handle different split agreements by territory.
// Step 1: Create default worldwide split
await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
AssetId: assetId ,
split: [
{ TenantUserId: artistId , split: 70.0 },
{ TenantUserId: labelId , split: 30.0 }
]
})
});
// Step 2: Create territory-specific override for Japan
await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
AssetId: assetId ,
split: [
{ TenantUserId: artistId , split: 50.0 },
{ TenantUserId: labelId , split: 30.0 },
{ TenantUserId: distributorId , split: 20.0 }
],
territory: 'JP' , // Only applies to Japan
startDate: '2025-01-01'
})
});
// The system automatically applies the correct split based on territory
Integration Sequences
Sequence 1: User → Splits → Accounting
Complete flow from user creation to earnings calculation:
// Step 1: Create user
const userResponse = await fetch ( 'https://api.royalti.io/user/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
firstName: 'John' ,
lastName: 'Doe' ,
email: 'john@example.com'
})
});
const { id : userId } = await userResponse . json ();
// Step 2: Assign to splits
await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
AssetId: assetId ,
split: [
{ TenantUserId: userId , split: 50.0 },
{ TenantUserId: labelId , split: 50.0 }
]
})
});
// Step 3: Process royalties (file upload - see Workflow 3)
// Step 4: View earnings
const earningsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const earnings = await earningsResponse . json ();
console . log ( 'User earnings:' , earnings . due );
Sequence 2: Splits → Accounting Update
Update splits and verify accounting recalculation:
// Step 1: Update split
await fetch ( `https://api.royalti.io/split/ ${ splitId } ` , {
method: 'PUT' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
split: [
{ TenantUserId: userId , split: 60.0 }, // Increased from 50%
{ TenantUserId: labelId , split: 40.0 }
]
})
});
// Step 2: Request fresh calculation
const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats?forceRefresh=true` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const updatedStats = await statsResponse . json ();
console . log ( 'Updated earnings:' , updatedStats . due );
Sequence 3: Payment → Balance Update
Create payment and verify balance adjustment:
// Step 1: Create payment
const paymentResponse = await fetch ( 'https://api.royalti.io/payment/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
user: userId ,
title: 'Monthly Payment' ,
transactionDate: new Date (). toISOString (),
currency: 'USD' ,
amount: 500.00 ,
amountUSD: 500.00 ,
conversionRate: 1.0
})
});
// Step 2: Check updated balance
const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ userId } /stats` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const stats = await statsResponse . json ();
console . log ( 'Paid amount increased:' , stats . paid );
console . log ( 'Due amount decreased:' , stats . due );
Best Practices
Data Consistency
Create users before splits - Users must exist before being assigned to splits
Validate splits before activation - Ensure splits add up to 100% and have valid date ranges
Use temporal splits for term changes - Preserves historical accuracy
Verify data after changes - Always check results after updates
Workflow Efficiency
Batch user creation - Use bulk endpoints for multiple users
Set default splits - Apply workspace-level defaults for consistency
Monitor file processing - Poll status endpoints for upload completion
Check eligibility first - Filter users before creating payments
Communication
Notify users of changes - Keep collaborators informed via notifications
Document split terms - Store agreements in metadata fields
Regular reporting - Send periodic earning statements
Payment confirmations - Notify users when payments are processed
Troubleshooting
Earnings not updating after split change
Solution:
Verify split was saved successfully (check API response)
Request fresh calculation: GET /accounting/{id}/stats?forceRefresh=true
Verify split date ranges include current period
Check that royalty data exists for the asset
User missing from accounting
Checklist:
User has at least one active split
Split date ranges include current period
Asset/Product has royalty data uploaded
Split percentages are valid (0-100)
Verify: // Check user splits
const splits = await fetch ( `https://api.royalti.io/split?userId= ${ userId } ` , {
headers: { 'Authorization' : `Bearer ${ token } ` }
});
// Check user stats
const stats = await fetch ( `https://api.royalti.io/accounting/ ${ userId } /stats` , {
headers: { 'Authorization' : `Bearer ${ token } ` }
});
Payment not deducted from due amount
Solution:
Verify payment status is successfully created
Check payment amount matches expected value
Request fresh accounting data: GET /accounting/{id}/stats?forceRefresh=true
Review transaction history to confirm payment was recorded
Split validation error: total exceeds 100%
Solution:
Review all split shares for the asset
Ensure percentages add up to exactly 100%
Check for overlapping temporal splits
Update split shares before saving:
// Validate splits add to 100%
const total = splits . reduce (( sum , s ) => sum + s . split , 0 );
if ( Math . abs ( total - 100 ) > 0.01 ) {
console . error ( `Splits total ${ total } %, must equal 100%` );
}
File upload stuck in processing
Check status: const status = await fetch ( `https://api.royalti.io/file/ ${ fileId } ` , {
headers: { 'Authorization' : `Bearer ${ token } ` }
});
const fileData = await status . json ();
console . log ( 'Status:' , fileData . status );
console . log ( 'Error:' , fileData . error );
Common causes:
Large file (may take 10-30 minutes)
Invalid file format
Missing required columns
File encoding issues
Solutions:
Wait 30 minutes for large files
Check error details in file status
Verify file format matches source
Re-upload with correct royaltySource specified
API Reference
Related Guides:
Key Endpoints:
Workflow 5: Product Delivery to DSPs
Complete workflow for delivering music releases to Digital Service Providers.
Prepare Asset DDEX Metadata
Ensure all assets have complete DDEX metadata before creating products. // Update asset DDEX metadata
await fetch ( `https://api.royalti.io/asset/ ${ assetId } /ddex-metadata` , {
method: 'PUT' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
ddexMetadata: {
resourceReference: 'A123456789' ,
title: 'Track Title' ,
displayArtist: 'Artist Name' ,
duration: 'PT3M45S' ,
languageOfPerformance: 'en' ,
parentalWarningType: 'NotExplicit'
},
technicalResourceDetails: {
fileFormat: 'WAV' ,
sampleRate: 44100 ,
bitDepth: 16 ,
numberOfChannels: 2 ,
audioCodec: 'PCM'
},
soundRecordingDetails: {
isrc: 'USRC17607839' ,
pLine: {
year: 2024 ,
pLineText: '2024 Record Label'
},
genre: 'Pop' ,
subGenre: 'Indie Pop'
}
})
});
console . log ( 'Asset DDEX metadata updated' );
Verify Asset Readiness
Check that assets meet DDEX requirements for delivery. const readinessResponse = await fetch (
`https://api.royalti.io/asset/ ${ assetId } /ddex-readiness` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const { isReady , readinessScore , missingFields } = await readinessResponse . json ();
if ( ! isReady ) {
console . log ( `Asset not ready ( ${ readinessScore } %)` );
console . log ( 'Missing fields:' , missingFields );
// Fix missing fields before proceeding
return ;
}
console . log ( '✓ Asset is ready for delivery' );
Get Available Delivery Providers
Check which DSPs are available for delivery. const providersResponse = await fetch (
'https://api.royalti.io/product/delivery-providers' ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const { providers } = await providersResponse . json ();
console . log ( ` ${ providers . length } providers available` );
// Example: Spotify, Apple Music, Tidal, etc.
Validate Product for Delivery
Ensure product meets all provider requirements. const validationResponse = await fetch (
`https://api.royalti.io/product/ ${ productId } /deliveries/validate` ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
providers: [ 'spotify' , 'apple_music' , 'tidal' ]
})
}
);
const { validations } = await validationResponse . json ();
// Check each provider
for ( const validation of validations ) {
if ( ! validation . isValid ) {
console . log ( ` ${ validation . provider } : ${ validation . errors . join ( ', ' ) } ` );
}
}
Create Delivery
Deliver the product to selected DSPs. const deliveryResponse = await fetch (
`https://api.royalti.io/product/ ${ productId } /deliveries` ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
providers: [ 'spotify' , 'apple_music' ],
releaseDate: '2025-03-01' ,
preorderDate: '2025-02-15' ,
metadata: {
releaseType: 'single' ,
genre: 'Pop'
}
})
}
);
const { deliveries } = await deliveryResponse . json ();
console . log ( `Created ${ deliveries . length } deliveries` );
Monitor Delivery Status
Track delivery progress for each provider. // Poll delivery status
const statusResponse = await fetch (
`https://api.royalti.io/product/ ${ productId } /deliveries` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const { deliveries } = await statusResponse . json ();
for ( const delivery of deliveries ) {
console . log ( ` ${ delivery . provider } : ${ delivery . status } ` );
// Status: pending → processing → delivered → live
if ( delivery . status === 'failed' ) {
console . log ( 'Error:' , delivery . error );
}
}
Workflow 6: Complete Release Lifecycle
End-to-end workflow from asset upload to DSP delivery and royalty collection.
Upload and Configure Assets
Upload audio files and configure DDEX metadata. // Upload audio file
const uploadResponse = await fetch ( 'https://api.royalti.io/file/upload-url' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
fileType: 'audio' ,
fileMetaData: {
fileName: 'track.wav' ,
fileSize: 50000000 ,
mimeType: 'audio/wav'
}
})
});
const { uploadUrl , fileId } = await uploadResponse . json ();
// Upload to GCS
const fileBuffer = fs . readFileSync ( 'track.wav' );
await fetch ( uploadUrl , {
method: 'PUT' ,
body: fileBuffer
});
// Confirm upload and create asset
const confirmResponse = await fetch (
`https://api.royalti.io/file/ ${ fileId } /confirm` ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
assetMetadata: {
title: 'Track Title' ,
artist: 'Artist Name' ,
isrc: 'USRC17607839'
}
})
}
);
const { assetId } = await confirmResponse . json ();
console . log ( 'Asset created:' , assetId );
Create Product (Release)
Group assets into a product/release. const productResponse = await fetch ( 'https://api.royalti.io/product/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
title: 'Single Title' ,
artist: 'Artist Name' ,
upc: '123456789012' ,
releaseDate: '2025-03-01' ,
releaseType: 'single' ,
assets: [ assetId ],
metadata: {
label: 'Record Label' ,
genre: 'Pop' ,
cLine: '2024 Record Label'
}
})
});
const { id : productId } = await productResponse . json ();
console . log ( 'Product created:' , productId );
Configure Splits
Set up revenue sharing for the release. await fetch ( 'https://api.royalti.io/split/' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
ProductId: productId ,
split: [
{ TenantUserId: artistId , split: 70.0 },
{ TenantUserId: labelId , split: 30.0 }
],
startDate: '2025-03-01'
})
});
console . log ( 'Revenue splits configured' );
Deliver to DSPs
Send the release to streaming platforms (see Workflow 5). // Validate and deliver
const deliveryResponse = await fetch (
`https://api.royalti.io/product/ ${ productId } /deliveries` ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
providers: [ 'spotify' , 'apple_music' , 'tidal' ],
releaseDate: '2025-03-01'
})
}
);
console . log ( 'Delivery initiated' );
Monitor and Collect Royalties
Process royalty reports as they arrive (see Workflow 3). // Upload royalty reports from DSPs
// Process revenue distribution via splits
// Generate user accounting records
const statsResponse = await fetch (
`https://api.royalti.io/accounting/ ${ artistId } /stats` ,
{
headers: { 'Authorization' : `Bearer ${ token } ` }
}
);
const earnings = await statsResponse . json ();
console . log ( 'Artist earnings:' , earnings . due );
Process Payments
Distribute earnings to collaborators (see Workflow 4). // Create payments for eligible users
// Track payment completion
// Notify users
Summary
These workflows demonstrate how to combine user management, splits, accounting, payments, and distribution for common scenarios:
Onboarding - Create users, assign splits, verify earnings
Split changes - Update terms, recalculate accounting, notify users
Royalty processing - Upload files, monitor progress, distribute earnings
Payments - Generate reports, create payments, update balances
Product delivery - Prepare assets, validate metadata, deliver to DSPs
Release lifecycle - Complete flow from upload to distribution and royalty collection
Follow these patterns to build efficient royalty management workflows that scale with your business.