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

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: '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);
Python
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}')
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: '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.
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: '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.
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: '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:
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:

Summary

These workflows demonstrate how to combine user management, splits, accounting, and payments 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
Follow these patterns to build efficient royalty management workflows that scale with your business.