Skip to main content

Overview

Access comprehensive financial reporting and management tools through the Royalti.io API. View real-time earnings calculations, track expenses and revenue, manage payments, and maintain accurate accounting records across your music catalog.

Key Features

  • Real-time earnings - View current earnings and payment status for all users
  • Automatic calculations - Earnings updated automatically when royalties upload or splits change
  • Expense tracking - Record costs at any level (user, artist, product, asset, general)
  • Revenue management - Track additional income sources beyond streaming royalties
  • Transaction history - Complete audit trail of all financial activity
  • Flexible reporting - Monthly breakdowns, summaries, and custom date ranges
  • Multi-currency support - Handle international transactions with automatic USD conversion

Prerequisites

Required Data

Before accessing financial features, ensure you have:
  • User accounts - Collaborators with earnings
  • Configured splits - Revenue distribution defined with percentages
  • Royalty data uploaded - Revenue data processed from DSPs/distributors
  • Payment settings - Bank accounts or payment methods configured (for payments)

Authentication

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

Quick Start: Complete Financial Cycle

1

Check User Earnings

View earnings breakdown for a specific user:
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);
2

Track an Expense

Record costs associated with a product release:
const response = await fetch('https://api.royalti.io/expense/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Marketing Campaign',
    type: 'product',
    id: 'product-uuid',
    transactionDate: '2024-03-15',
    currency: 'USD',
    amount: 2500.00,
    amountUSD: 2500.00,
    memo: 'Social media advertising for album release'
  })
});

const expense = await response.json();
3

Record Additional Revenue

Track income beyond streaming royalties:
const response = await fetch('https://api.royalti.io/revenue/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Sync Licensing Deal',
    amount: 10000.00,
    currency: 'USD',
    amountUSD: 10000.00,
    transactionDate: '2024-03-20T10:00:00Z',
    ArtistId: 'artist-uuid',
    memo: 'TV commercial licensing'
  })
});

const revenue = await response.json();
4

Review Updated Balances

Verify accounting reflects all transactions:
const response = await fetch(
  'https://api.royalti.io/accounting/getcurrentdue?sort=due&order=DESC',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

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

Users.forEach(user => {
  console.log(`${user.fullName}: $${user.Due} due`);
});

Viewing Earnings & Accounting

Get User Accounting Statistics

Retrieve comprehensive earnings data for a specific user:
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:
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);

Current Balances for All Users

Get list of all users with their outstanding balances:
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`);
});
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": "[email protected]",
  "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:
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

Get paginated list of all financial transactions:
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:
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:
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:
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
    }
  }
]

Expense Management

Track costs associated with releases, artists, and operations. Expenses reduce user earnings in accounting calculations.

Flexible Expense Association

Expenses can be associated with different types of entities in your catalog:
TypeDescriptionUse Case
userAssociate with a specific userPersonal expenses, reimbursements
artistAssociate with an artistRecording sessions, promotional costs
productAssociate with an album/EPMarketing campaigns, manufacturing
assetAssociate with a single trackMastering, mixing, feature artist fees
generalNot associated with any entityOffice expenses, software subscriptions
How It Works:
  • Specify the type field to indicate the entity type
  • Provide the id field with the entity’s UUID
  • For general expenses, omit the id field
This flexible system allows you to:
  • Track costs at any level of granularity
  • Generate expense reports by artist, product, or asset
  • Allocate expenses to the appropriate earnings calculations

Create Product Expense

const response = await fetch('https://api.royalti.io/expense/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Marketing Campaign',
    type: 'product',
    id: 'product-uuid',
    transactionDate: '2024-03-15',
    currency: 'USD',
    amount: 2500.00,
    amountUSD: 2500.00,
    conversionRate: 1.0,
    memo: 'Social media advertising for album release',
    split: [
      { user: 'user-id-1', share: 60 },
      { user: 'user-id-2', share: 40 }
    ]
  })
});

const expense = await response.json();

Create Artist Expense

const response = await fetch('https://api.royalti.io/expense/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Studio Recording Session',
    type: 'artist',
    id: 'artist-uuid',
    transactionDate: '2024-02-20',
    currency: 'USD',
    amount: 5000.00,
    amountUSD: 5000.00,
    memo: 'Recording sessions for new album'
  })
});

Create Asset Expense

Track costs for individual tracks or recordings:
const response = await fetch('https://api.royalti.io/expense/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Mastering Services',
    type: 'asset',
    id: 'asset-uuid',
    transactionDate: '2024-03-10',
    currency: 'USD',
    amount: 800.00,
    amountUSD: 800.00,
    memo: 'Professional mastering for single track'
  })
});

Create General Expense

const response = await fetch('https://api.royalti.io/expense/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Legal Consultation',
    type: 'general',
    transactionDate: '2024-03-01',
    currency: 'USD',
    amount: 1500.00,
    amountUSD: 1500.00,
    memo: 'Contract review and negotiation'
  })
});

List Expenses

const response = await fetch(
  'https://api.royalti.io/expense/?include=Artist,Product,Asset&page=1&size=20',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

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

expenses.forEach(expense => {
  console.log(`${expense.title}: $${expense.amountUSD}`);
  if (expense.Artist) console.log(`  Artist: ${expense.Artist.name}`);
  if (expense.Product) console.log(`  Product: ${expense.Product.title}`);
});

Expense Splits

Distribute expenses among multiple users:
{
  split: [
    { user: 'user-uuid-1', share: 50 },  // 50% of expense
    { user: 'user-uuid-2', share: 30 },  // 30% of expense
    { user: 'user-uuid-3', share: 20 }   // 20% of expense
  ]
}
Expense splits reduce user earnings in accounting calculations. The total share should equal 100.

Bulk Expense Creation

Create multiple expenses in a single request:
const expenseData = [
  {
    title: 'Marketing - Product A',
    type: 'product',
    id: 'product-a-uuid',
    transactionDate: '2024-03-15',
    currency: 'USD',
    amount: 1000.00,
    amountUSD: 1000.00
  },
  {
    title: 'Marketing - Product B',
    type: 'product',
    id: 'product-b-uuid',
    transactionDate: '2024-03-15',
    currency: 'USD',
    amount: 1500.00,
    amountUSD: 1500.00
  }
];

const response = await fetch('https://api.royalti.io/expense/bulk', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ expenses: expenseData })
});

const result = await response.json();
console.log('Created:', result.data.created);
console.log('Failed:', result.data.failed);

Revenue Management

Track additional income sources beyond streaming royalties.

Create Sync Licensing Revenue

const response = await fetch('https://api.royalti.io/revenue/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Sync Licensing Deal',
    amount: 10000.00,
    currency: 'USD',
    amountUSD: 10000.00,
    transactionDate: '2024-03-20T10:00:00Z',
    ArtistId: 'artist-uuid',
    memo: 'TV commercial licensing',
    splits: [
      { user: 'user-id-1', share: 60 },
      { user: 'user-id-2', share: 40 }
    ]
  })
});

const revenue = await response.json();

Create Merchandise Revenue

const response = await fetch('https://api.royalti.io/revenue/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'Merchandise Sales - Q1',
    amount: 5000.00,
    currency: 'USD',
    amountUSD: 5000.00,
    transactionDate: '2024-03-31T23:59:59Z',
    memo: 'T-shirts and posters sales'
  })
});

List Revenue

const response = await fetch(
  'https://api.royalti.io/revenue/?include=Artist&page=1&size=20',
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

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

revenues.forEach(revenue => {
  console.log(`${revenue.title}: $${revenue.amountUSD}`);
  if (revenue.Artist) console.log(`  Artist: ${revenue.Artist.name}`);
});

Bulk Revenue Creation

Create multiple revenue records in a single request:
const revenueData = [
  {
    title: 'Sync Deal A',
    amount: 5000.00,
    currency: 'USD',
    amountUSD: 5000.00,
    transactionDate: '2024-03-01',
    ArtistId: 'artist-a-uuid'
  },
  {
    title: 'Sync Deal B',
    amount: 7500.00,
    currency: 'USD',
    amountUSD: 7500.00,
    transactionDate: '2024-03-15',
    ArtistId: 'artist-b-uuid'
  }
];

const response = await fetch('https://api.royalti.io/revenue/bulk', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ revenues: revenueData })
});

Accounting Operations

When to Refresh Accounting Data

Accounting data is automatically calculated when:
  • Royalty files are uploaded and processed
  • Splits are created or modified
  • Payments are recorded
  • Expenses or revenue entries are created
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:
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);
console.log('Job ID:', result.jobId);
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:
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:
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:
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:
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:
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.

Multi-Currency Handling

All financial records support multi-currency transactions with automatic USD conversion:
{
  currency: 'EUR',
  amount: 1000.00,        // Amount in EUR
  amountUSD: 1090.00,     // USD equivalent
  conversionRate: 1.09    // EUR to USD rate
}
Example: Euro Payment:
const eurAmount = 1000;
const eurToUsdRate = 1.09;  // 1 EUR = 1.09 USD
const usdAmount = eurAmount * eurToUsdRate;

const response = await fetch('https://api.royalti.io/payment/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    user: 'user-uuid',
    title: 'Euro Payment',
    transactionDate: '2024-03-15',
    currency: 'EUR',
    amount: eurAmount,      // 1000
    amountUSD: usdAmount,   // 1090
    conversionRate: eurToUsdRate  // 1.09
  })
});
The system uses amountUSD for all accounting calculations to ensure consistency across currencies.

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
  5. Verify amounts - Always double-check USD conversion rates
  6. Detailed memos - Include transaction context for audit trails

Performance Optimization

  1. Use default cached data - Faster response times for most queries
  2. Batch operations - Use bulk endpoints for multiple records
  3. Monitor queue status - Track long-running recalculation operations
  4. Paginate results - Use reasonable page sizes (10-50 items)
  5. Async processing - Don’t wait for recalculation to complete

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
  5. Weekly reviews - Check user balances and pending items
  6. Monthly cycles - Establish consistent payment schedules
  7. Quarterly audits - Reconcile all transactions against external records

Error Handling

Always implement robust error handling for financial operations:
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
  • Check for expenses reducing earnings
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}
  5. Review expenses: GET /expense?userId={userId}
Possible causes:
  • Recent split changes not yet reflected
  • Royalty file just uploaded
  • Manual refresh needed
  • Cache delay
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
  • Large dataset processing
Solutions:
  1. Check error details in job status
  2. Verify split configurations
  3. Retry with smaller batch size
  4. Contact support if error persists
Expected Processing Times:
  • Small workspaces (<100 users): 1-2 minutes
  • Medium workspaces (100-1000 users): 2-10 minutes
  • Large workspaces (1000+ users): 10-30 minutes
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)
  • User actually has positive balance
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. Review expenses and revenue entries
  4. Force full workspace refresh
Common Mistakes:
  • Wrong conversion direction (USD/EUR vs EUR/USD)
  • Missing conversionRate
  • amountUSD doesn’t match calculation
Correct Example:
const eurAmount = 1000;
const eurToUsdRate = 1.09;  // 1 EUR = 1.09 USD
const usdAmount = eurAmount * eurToUsdRate;

{
  currency: 'EUR',
  amount: eurAmount,      // 1000
  amountUSD: usdAmount,   // 1090
  conversionRate: eurToUsdRate  // 1.09
}
Checklist:
  • Expense split configured correctly
  • User IDs in split are valid
  • Share percentages total 100
  • Expense transaction date is within accounting period
Solutions:
  1. Verify expense: GET /expense/{id}
  2. Check split configuration in expense record
  3. Force user recalculation: POST /accounting/users/{userId}/recalculate
  4. Review accounting stats: GET /accounting/{userId}/stats?forceRefresh=true
Checklist:
  • Revenue splits configured correctly
  • User IDs in splits are valid
  • Share percentages total 100
  • Revenue amount is positive
Solutions:
  1. Verify revenue: GET /revenue/{id}
  2. Check split configuration
  3. Force user recalculation: POST /accounting/users/{userId}/recalculate
  4. Review accounting stats: GET /accounting/{userId}/stats?forceRefresh=true

API Reference

For complete endpoint documentation, see: Accounting & Earnings: Refresh Operations: Expenses: Revenue: Related Guides:

Support

Need help with financial management?