Skip to main content

Overview

Access real-time earnings data, payment tracking, and comprehensive financial reporting through the Royalti.io API. The accounting system provides detailed breakdowns of user earnings, transaction history, and outstanding balances with automatic calculations based on royalty data and configured splits.

Key Features

  • Real-time earnings - View current earnings and payment status for all users
  • Automatic calculations - Earnings updated automatically when royalties are uploaded or splits change
  • Transaction tracking - Complete history of payments, revenues, and expenses
  • Flexible reporting - Monthly breakdowns, summaries, and custom date ranges
  • Balance management - Track paid amounts, outstanding balances, and gross earnings

Prerequisites

Required Data

Before accessing accounting data, ensure you have:
  • Configured splits - Users assigned to splits with percentages
  • Royalty data uploaded - Revenue data processed from DSPs/distributors
  • Payment records - Payment transactions tracked in the system

Authentication

All accounting endpoints require authentication with a Bearer token.
Node.js
const headers = {
  'Authorization': `Bearer ${token}`,
  'Content-Type': 'application/json'
};
Python
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

Quick Start: Getting User Earnings

1

Get User Earnings Statistics

Retrieve earnings breakdown for a specific user.
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/${userId}/stats`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const stats = await response.json();

console.log('Gross earnings:', stats.Royalty_Share);
console.log('Amount paid:', stats.paid);
console.log('Amount due:', stats.due);
Python
response = requests.get(
    f'https://api.royalti.io/accounting/{user_id}/stats',
    headers=headers
)

stats = response.json()

print(f"Gross earnings: ${stats['Royalty_Share']}")
print(f"Amount paid: ${stats['paid']}")
print(f"Amount due: ${stats['due']}")
2

Get Latest Data with Refresh

Request fresh calculations to ensure latest data.
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/${userId}/stats?forceRefresh=true`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const freshStats = await response.json();
Use forceRefresh=true after making split changes or when you need guaranteed up-to-date calculations.
3

Get Detailed Breakdown

Include royalty summary for complete earnings breakdown.
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/${userId}/stats?include=royalty`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const detailedStats = await response.json();

console.log('Royalty summary:', detailedStats.royaltySummary);
// Includes streams, downloads, rates, and more

User Earnings & Statistics

Get User Accounting Statistics

Retrieve comprehensive earnings data for a specific user:
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/${userId}/stats`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const stats = await response.json();
Response Fields:
  • Royalty_Share - Gross earnings from all splits
  • paid - Total amount paid to user
  • due - Outstanding balance (Royalty_Share - paid)
Query Parameters:
  • include=royalty - Include detailed royalty breakdown
  • forceRefresh=true - Recalculate from source data
Example Response:
{
  "Royalty_Share": 1135.28,
  "paid": 100.00,
  "due": 1035.28
}

Get Detailed Royalty Breakdown

Include royalty summary for streams, downloads, and rates:
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/${userId}/stats?include=royalty`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const stats = await response.json();

console.log('Total streams:', stats.royaltySummary.Streams);
console.log('Stream royalty:', stats.royaltySummary.Streams_Royalty);
console.log('Total downloads:', stats.royaltySummary.Downloads);
console.log('Download royalty:', stats.royaltySummary.Downloads_Royalty);
console.log('Rate per 1K:', stats.royaltySummary.RatePer1K);

All Users Summary

Current Balances for All Users

Get list of all users with their outstanding balances:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/getcurrentdue?sort=due&order=DESC&page=1&size=20',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const { Users, totalUsers, page, totalPages } = await response.json();

Users.forEach(user => {
  console.log(`${user.fullName}: $${user.Due} due`);
});
Python
response = requests.get(
    'https://api.royalti.io/accounting/getcurrentdue?sort=due&order=DESC',
    headers=headers
)

data = response.json()

for user in data['Users']:
    print(f"{user['fullName']}: ${user['Due']} due")
Query Parameters:
  • q - Search by user name
  • page - Page number (default: 1)
  • size - Page size (default: 10, max: 100)
  • sort - Sort field: gross, net, paid, due (default: due)
  • order - Sort order: ASC or DESC (default: DESC)
Response Fields per User:
{
  "id": "user-id",
  "firstName": "John",
  "lastName": "Doe",
  "fullName": "John Doe",
  "email": "john@example.com",
  "TenantUserId": "tenant-user-id",
  "Gross": 2500.75,
  "Net": 2250.68,
  "Paid": 1500.00,
  "Due": 750.68,
  "paymentRequested": false,
  "paymentRequestId": null
}

Total Outstanding for Workspace

Get aggregate due amount across all users:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/gettotaldue',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const total = await response.json();

console.log('Total gross:', total.totalGross);
console.log('Total paid:', total.totalPaid);
console.log('Total due:', total.totalDue);
console.log('User count:', total.userCount);
Response Example:
{
  "message": "success",
  "totalGross": 150000.50,
  "totalPaid": 100000.00,
  "totalDue": 50000.50,
  "userCount": 125
}

Transaction History

List All Transactions

Get paginated list of all financial transactions:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/transactions?page=1&size=20',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const { transaction, currentPage, totalPages, totalItem } = await response.json();

transaction.forEach(txn => {
  console.log(`${txn.type}: $${txn.amount} - ${txn.TenantUser.fullName}`);
});
Query Parameters:
  • page - Page number (default: 1)
  • size - Items per page (default: 10, max: 100)
  • start - Start date filter (ISO format: YYYY-MM-DD)
  • end - End date filter (ISO format: YYYY-MM-DD)
Transaction Types:
  • payment - Payment made to user
  • revenue - Revenue received
  • expense - Expense recorded
  • paymentRequest - Payment request created
Filter by Date Range:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/transactions?start=2024-01-01&end=2024-12-31',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

Transaction Summary

Get aggregated totals across all transaction types:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/transactions/summary',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const { summary } = await response.json();

console.log('Total payments:', summary.totalPayments);
console.log('Total revenues:', summary.totalRevenues);
console.log('Total expenses:', summary.totalExpenses);
console.log('Net amount:', summary.netAmount);
Response Example:
{
  "message": "success",
  "summary": {
    "totalPayments": 50000.00,
    "totalRevenues": 125000.50,
    "totalExpenses": 15000.25,
    "netAmount": 60000.25
  }
}
The summary provides all-time totals for the workspace. Use the monthly breakdown endpoint for time-series analysis.

Monthly Transaction Breakdown

Analyze transactions by month with type breakdown:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/transactions/monthly?start=2024-01-01&stop=2024-12-31',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const monthlyData = await response.json();

monthlyData.forEach(month => {
  console.log(`${month.month}:`);
  console.log(`  Payments: $${month.transactions.payment}`);
  console.log(`  Revenue: $${month.transactions.revenue}`);
  console.log(`  Expenses: $${month.transactions.expense}`);
  console.log(`  Net: $${month.transactions.net}`);
});
Query Parameters:
  • start - Start date (ISO format: YYYY-MM-DD)
  • stop - End date (ISO format: YYYY-MM-DD)
Response Format:
[
  {
    "month": "2024-08-01",
    "transactions": {
      "payment": 5000.00,
      "revenue": 12500.50,
      "expense": 1500.25,
      "net": 6000.25
    }
  }
]

Refreshing Accounting Data

When to Refresh

Accounting data is automatically calculated when:
  • Royalty files are uploaded and processed
  • Splits are created or modified
  • Payments are recorded
You may want to manually refresh when:
  • You’ve made multiple split changes
  • You need guaranteed latest data
  • You suspect calculations are out of sync

Refresh for Specific User

Request updated calculations for a single user:
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/users/${userId}/recalculate?forceRefresh=true`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const result = await response.json();

console.log('Status:', result.message);
// "User accounting update queued successfully"
Python
response = requests.post(
    f'https://api.royalti.io/accounting/users/{user_id}/recalculate?forceRefresh=true',
    headers=headers
)

result = response.json()
print(f"Status: {result['message']}")
Query Parameters:
  • forceRefresh=true - Force recalculation even if data is recent
Response:
{
  "message": "User accounting update queued successfully",
  "jobId": "job-uuid-123",
  "userId": "tenant-user-id",
  "status": "queued"
}

Refresh All Users in Workspace

Request recalculation for all users:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/tenant/recalculate?batchSize=100',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const result = await response.json();

console.log('Status:', result.message);
console.log('Job ID:', result.jobId);
Query Parameters:
  • batchSize - Users per batch (default: 100, min: 1, max: 500)
Response:
{
  "message": "Tenant accounting update queued successfully",
  "jobId": "job-uuid-456",
  "tenantId": 123,
  "status": "queued",
  "info": {
    "batchSize": 100,
    "estimatedTime": "~5 minutes for 500 users"
  }
}
For workspaces with many users (100+), recalculation is processed in batches to ensure reliability. Check the status endpoint to monitor progress.

Immediate Refresh (Small Workspaces)

For small workspaces with fewer than 100 users, you can use the immediate refresh:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/refresh',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const result = await response.json();

console.log('Processed:', result.processedCount);
console.log('Total users:', result.totalUsers);
Response:
{
  "message": "success",
  "processedCount": 125,
  "totalUsers": 125
}
This endpoint processes all users immediately. For workspaces with 100+ users, use the batched recalculate endpoint instead to avoid timeouts.

Check Recalculation Status

Monitor the progress of recalculation operations:
Node.js
const response = await fetch(
  `https://api.royalti.io/accounting/queue/status?jobId=${jobId}`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const status = await response.json();

console.log('Job state:', status.state);
console.log('Progress:', status.progress + '%');
Job States:
  • waiting - Job queued, not yet started
  • active - Currently processing
  • completed - Successfully finished
  • failed - Encountered an error
  • delayed - Temporarily delayed, will retry
Response Example:
{
  "jobId": "job-uuid-123",
  "state": "active",
  "progress": 45.5,
  "data": {
    "triggeredBy": "manual",
    "timestamp": "2024-08-11T10:30:00Z",
    "userCount": 100
  },
  "timestamps": {
    "created": 1723370000000,
    "processed": 1723370050000,
    "finished": null
  }
}

Queue Overview

View overall queue status without specifying a job ID:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/queue/status',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const overview = await response.json();

console.log('Waiting jobs:', overview.queue.waiting);
console.log('Active jobs:', overview.queue.active);
console.log('Completed:', overview.queue.completed);
console.log('Failed:', overview.queue.failed);

Statistics Pipeline

Refresh all statistics and accounting data in the correct order:
Node.js
const response = await fetch(
  'https://api.royalti.io/accounting/refreshstats',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const result = await response.json();

console.log('Status:', result.message);
console.log('Flow ID:', result.flowId);
Response:
{
  "message": "Stats and accounting refresh queued successfully",
  "flowId": "flow-uuid-789",
  "tenantId": 123,
  "status": "queued",
  "info": {
    "triggeredBy": "manual",
    "executionOrder": "Product Stats → Asset Stats → Artist Stats → Accounting"
  }
}
This endpoint ensures all statistics are recalculated in the correct dependency order. Use this after bulk data uploads or major catalog changes.

Best Practices

Data Accuracy

  1. Trust automatic calculations - The system updates accounting when royalties upload or splits change
  2. Use forceRefresh sparingly - Only when you need guaranteed fresh data
  3. Validate splits first - Ensure splits total 100% before expecting accurate earnings
  4. Regular reconciliation - Compare accounting data with actual payments monthly

Performance Optimization

  1. Use default cached data - Faster response times for most queries
  2. Batch recalculations - For large workspaces, use batched refresh endpoints
  3. Monitor queue status - Track long-running operations
  4. Paginate results - Use reasonable page sizes (10-50 items)

Reporting Workflows

  1. Use date filters - Filter transactions by specific periods for reports
  2. Export for analysis - Use transaction endpoints for external reporting tools
  3. Monitor monthly trends - Track revenue and payment patterns over time
  4. Check user balances - Regularly review who is owed payment

Error Handling

Always handle refresh operations properly:
Node.js
async function refreshUserAccounting(userId, forceRefresh = false) {
  try {
    const url = `https://api.royalti.io/accounting/users/${userId}/recalculate?forceRefresh=${forceRefresh}`;

    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Refresh failed: ${error.message}`);
    }

    const result = await response.json();

    // Monitor job status
    const jobId = result.jobId;
    const statusUrl = `https://api.royalti.io/accounting/queue/status?jobId=${jobId}`;

    let completed = false;
    while (!completed) {
      await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds

      const statusResponse = await fetch(statusUrl, {
        headers: { 'Authorization': `Bearer ${token}` }
      });

      const status = await statusResponse.json();

      if (status.state === 'completed') {
        completed = true;
        console.log('Refresh completed successfully');
      } else if (status.state === 'failed') {
        throw new Error('Refresh job failed');
      }

      console.log(`Progress: ${status.progress}%`);
    }

    return true;

  } catch (error) {
    console.error('Accounting refresh failed:', error.message);
    throw error;
  }
}

Troubleshooting

Checklist:
  • Verify split configurations are active
  • Check split date ranges include relevant periods
  • Review conditional split logic (territory, DSP-specific splits)
  • Ensure royalty data uploaded successfully
Solutions:
  1. Check user stats: GET /accounting/{userId}/stats
  2. Force refresh: GET /accounting/{userId}/stats?forceRefresh=true
  3. Verify splits: GET /split?userId={userId}
  4. Check royalty data: GET /royalty?userId={userId}
Possible causes:
  • Recent split changes not yet reflected
  • Royalty file just uploaded
  • Manual refresh needed
Solutions:
  1. Use forceRefresh=true parameter
  2. Check recent uploads: GET /file?status=completed
  3. Verify split changes saved: GET /split/{splitId}
  4. Request recalculation: POST /accounting/users/{userId}/recalculate
Check job status:
const status = await fetch(
  `https://api.royalti.io/accounting/queue/status?jobId=${jobId}`,
  { headers: { 'Authorization': `Bearer ${token}` } }
);
Common causes:
  • Invalid split data (doesn’t total 100%)
  • Missing royalty data
  • Database connection issues
Solutions:
  1. Check error details in job status
  2. Verify split configurations
  3. Retry with smaller batch size
  4. Contact support if error persists
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:
  1. Check user splits: GET /split?userId={userId}
  2. Check user stats: GET /accounting/{userId}/stats
  3. Verify user exists: GET /user/{userId}
  4. Force recalculation if needed
Reconciliation steps:
// Get transaction summary
const summary = await fetch(
  'https://api.royalti.io/accounting/transactions/summary',
  { headers: { 'Authorization': `Bearer ${token}` } }
);

// Get current due total
const due = await fetch(
  'https://api.royalti.io/accounting/gettotaldue',
  { headers: { 'Authorization': `Bearer ${token}` } }
);

// Compare values
const summaryData = await summary.json();
const dueData = await due.json();

console.log('Summary total paid:', summaryData.summary.totalPayments);
console.log('Accounting total paid:', dueData.totalPaid);
If mismatched:
  1. Check for deleted payments
  2. Verify all payments recorded correctly
  3. Force full workspace refresh

API Reference

For complete endpoint documentation, see: User Earnings: Workspace Totals: Transactions: Refresh Operations: Related Guides: