Skip to main content

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.
1

Create User Account

Create a user with invitation email.
Node.js
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: '[email protected]',
    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);
Python
user_response = requests.post(
    'https://api.royalti.io/user/',
    headers=headers,
    json={
        'firstName': 'Sarah',
        'lastName': 'Producer',
        'nickName': 'sarahpro',
        'email': '[email protected]',
        '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}')
2

Assign to Existing Splits

Add the user to existing asset splits.
Node.js
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');
3

Create Future Release Splits

Set up default splits for upcoming projects.
Node.js
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');
4

Verify Earnings Calculation

Check that earnings are calculated correctly for the user.
Node.js
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
});
5

Set Payment Preferences (Optional)

Configure payment settings for the user.
Node.js
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:
Node.js
// 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:
Node.js
// 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');
Python
# 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:
Node.js
// 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:
Node.js
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.
1

Upload Royalty File

Upload CSV or ZIP file from streaming platform.
Node.js
// 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.
2

Monitor Processing

Check file processing status.
Node.js
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.
3

Verify Earnings Updated

Check that user balances were updated.
Node.js
// 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`);
4

Review Updated Earnings

Check detailed earnings for specific users.
Node.js
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.
1

Generate Current Due Report

Get all users with outstanding balances.
Node.js
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}`);
Python
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}')
2

Filter by Payment Settings

Filter users who meet minimum threshold and have payment settings.
Node.js
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`);
3

Create Payment Batch

Create payment records for eligible users.
Node.js
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`);
4

Process External Payments

After processing payments through your payment provider, update records.
Node.js
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');
5

Notify Users

Send payment confirmation notifications.
Node.js
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.
Node.js
// 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: '[email protected]',
    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.
Node.js
// 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: '[email protected]', userType: 'producer' },
      { firstName: 'Engineer', lastName: 'Two', email: '[email protected]', userType: 'engineer' },
      { firstName: 'Artist', lastName: 'Three', email: '[email protected]', 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.
Node.js
// 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:
Node.js
// 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: '[email protected]'
  })
});

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:
Node.js
// 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:
Node.js
// 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

  1. Create users before splits - Users must exist before being assigned to splits
  2. Validate splits before activation - Ensure splits add up to 100% and have valid date ranges
  3. Use temporal splits for term changes - Preserves historical accuracy
  4. Verify data after changes - Always check results after updates

Workflow Efficiency

  1. Batch user creation - Use bulk endpoints for multiple users
  2. Set default splits - Apply workspace-level defaults for consistency
  3. Monitor file processing - Poll status endpoints for upload completion
  4. Check eligibility first - Filter users before creating payments

Communication

  1. Notify users of changes - Keep collaborators informed via notifications
  2. Document split terms - Store agreements in metadata fields
  3. Regular reporting - Send periodic earning statements
  4. Payment confirmations - Notify users when payments are processed

Troubleshooting

Solution:
  1. Verify split was saved successfully (check API response)
  2. Request fresh calculation: GET /accounting/{id}/stats?forceRefresh=true
  3. Verify split date ranges include current period
  4. Check that royalty data exists for the asset
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}` }
});
Solution:
  1. Verify payment status is successfully created
  2. Check payment amount matches expected value
  3. Request fresh accounting data: GET /accounting/{id}/stats?forceRefresh=true
  4. Review transaction history to confirm payment was recorded
Solution:
  1. Review all split shares for the asset
  2. Ensure percentages add up to exactly 100%
  3. Check for overlapping temporal splits
  4. 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%`);
}
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:
  1. Wait 30 minutes for large files
  2. Check error details in file status
  3. Verify file format matches source
  4. 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.
1

Prepare Asset DDEX Metadata

Ensure all assets have complete DDEX metadata before creating products.
Node.js
// 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');
See the Asset DDEX Management Guide for complete DDEX metadata requirements.
2

Verify Asset Readiness

Check that assets meet DDEX requirements for delivery.
Node.js
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');
3

Get Available Delivery Providers

Check which DSPs are available for delivery.
Node.js
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.
4

Validate Product for Delivery

Ensure product meets all provider requirements.
Node.js
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(', ')}`);
  }
}
5

Create Delivery

Deliver the product to selected DSPs.
Node.js
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`);
See the Product Delivery Guide for complete delivery workflows.
6

Monitor Delivery Status

Track delivery progress for each provider.
Node.js
// 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.
1

Upload and Configure Assets

Upload audio files and configure DDEX metadata.
Node.js
// 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);
2

Create Product (Release)

Group assets into a product/release.
Node.js
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);
3

Configure Splits

Set up revenue sharing for the release.
Node.js
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');
4

Deliver to DSPs

Send the release to streaming platforms (see Workflow 5).
Node.js
// 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');
5

Monitor and Collect Royalties

Process royalty reports as they arrive (see Workflow 3).
Node.js
// 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);
6

Process Payments

Distribute earnings to collaborators (see Workflow 4).
Node.js
// 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.