> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.royalti.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrated Workflows

> Complete end-to-end workflows combining user management, splits, accounting, and payments for common scenarios

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

<Steps>
  <Step title="Create User Account">
    Create a user with invitation email.

    ```javascript Node.js theme={null}
    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 Python theme={null}
    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}')
    ```
  </Step>

  <Step title="Assign to Existing Splits">
    Add the user to existing asset splits.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>

  <Step title="Create Future Release Splits">
    Set up default splits for upcoming projects.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>

  <Step title="Verify Earnings Calculation">
    Check that earnings are calculated correctly for the user.

    ```javascript Node.js theme={null}
    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
    });
    ```
  </Step>

  <Step title="Set Payment Preferences (Optional)">
    Configure payment settings for the user.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>
</Steps>

## 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:

```javascript Node.js theme={null}
// 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');
```

<Warning>
  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.
</Warning>

#### Option B: Create Temporal Split (Time-Based Change)

For changes that apply from a specific date forward:

```javascript Node.js theme={null}
// 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 Python theme={null}
# 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:

```javascript Node.js theme={null}
// 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:

```javascript Node.js theme={null}
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.

<Steps>
  <Step title="Upload Royalty File">
    Upload CSV or ZIP file from streaming platform.

    ```javascript Node.js theme={null}
    // 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);
    ```

    <Info>
      The system automatically recognizes the file format and reporting period from the file contents and metadata.
    </Info>
  </Step>

  <Step title="Monitor Processing">
    Check file processing status.

    ```javascript Node.js theme={null}
    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
    ```

    <Note>
      Large files may take several minutes to process. Poll the status endpoint every 30-60 seconds.
    </Note>
  </Step>

  <Step title="Verify Earnings Updated">
    Check that user balances were updated.

    ```javascript Node.js theme={null}
    // 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`);
    ```
  </Step>

  <Step title="Review Updated Earnings">
    Check detailed earnings for specific users.

    ```javascript Node.js theme={null}
    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);
    ```
  </Step>
</Steps>

## Workflow 4: Payment Cycle Management

Monthly payment workflow from eligibility check to payment execution.

<Steps>
  <Step title="Generate Current Due Report">
    Get all users with outstanding balances.

    ```javascript Node.js theme={null}
    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 Python theme={null}
    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}')
    ```
  </Step>

  <Step title="Filter by Payment Settings">
    Filter users who meet minimum threshold and have payment settings.

    ```javascript Node.js theme={null}
    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`);
    ```
  </Step>

  <Step title="Create Payment Batch">
    Create payment records for eligible users.

    ```javascript Node.js theme={null}
    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`);
    ```
  </Step>

  <Step title="Process External Payments">
    After processing payments through your payment provider, update records.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>

  <Step title="Notify Users">
    Send payment confirmation notifications.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>
</Steps>

## Common Use Cases

### Use Case 1: Label with Multiple Artists

Set up a label workspace with default splits for all artists.

```javascript Node.js theme={null}
// 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.

```javascript Node.js theme={null}
// 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.

```javascript Node.js theme={null}
// 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:

```javascript Node.js theme={null}
// 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:

```javascript Node.js theme={null}
// 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:

```javascript Node.js theme={null}
// 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

<AccordionGroup>
  <Accordion title="Earnings not updating after split change">
    **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
  </Accordion>

  <Accordion title="User missing from accounting">
    **Checklist:**

    * User has at least one active split
    * Split date ranges include current period
    * Asset/Product has royalty data uploaded
    * Split percentages are valid (0-100)

    **Verify:**

    ```javascript theme={null}
    // 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}` }
    });
    ```
  </Accordion>

  <Accordion title="Payment not deducted from due amount">
    **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
  </Accordion>

  <Accordion title="Split validation error: total exceeds 100%">
    **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:

    ```javascript theme={null}
    // 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%`);
    }
    ```
  </Accordion>

  <Accordion title="File upload stuck in processing">
    **Check status:**

    ```javascript theme={null}
    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
  </Accordion>
</AccordionGroup>

## API Reference

**Related Guides:**

* [User Management](/guides/user-management) - User creation and management
* [Splits Management](/guides/splits-management) - Revenue distribution
* [Financial Data & Management](/guides/financial-data-management) - Earnings calculation
* [Payment Processing](/guides/payment-processing) - Payment workflows
* [Royalty Management](/guides/royalty-management) - File uploads and analytics
* [Product Delivery](/guides/product-delivery) - DSP delivery workflows
* [Asset DDEX Management](/guides/asset-ddex-management) - DDEX metadata management

**Key Endpoints:**

* **User**: [POST /user/](/api-reference/user/post-user), [GET /user/{id}](/api-reference/user/get-user-id)
* **Splits**: [POST /split/](/api-reference/splits/post-split), [PUT /split/{id}](/api-reference/splits/put-split-id)
* **Accounting**: [GET /accounting/{id}/stats](/api-reference/accounting/get-accounting-id-stats)
* **Payments**: [POST /payment/](/api-reference/payments/post-payment)
* **Files**: [POST /file/upload-url](/api-reference/file/post-file-upload-url)
* **Product Delivery**: [POST /product/{id}/deliveries](/api-reference/product/post-product-id-deliveries), [GET /product/delivery-providers](/api-reference/product/get-product-delivery-providers)
* **Asset DDEX**: [PUT /asset/{id}/ddex-metadata](/api-reference/asset/put-asset-id-ddex-metadata), [GET /asset/{id}/ddex-readiness](/api-reference/asset/get-asset-id-ddex-readiness)

## Workflow 5: Product Delivery to DSPs

Complete workflow for delivering music releases to Digital Service Providers.

<Steps>
  <Step title="Prepare Asset DDEX Metadata">
    Ensure all assets have complete DDEX metadata before creating products.

    ```javascript Node.js theme={null}
    // 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');
    ```

    <Info>
      See the [Asset DDEX Management Guide](/guides/asset-ddex-management) for complete DDEX metadata requirements.
    </Info>
  </Step>

  <Step title="Verify Asset Readiness">
    Check that assets meet DDEX requirements for delivery.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>

  <Step title="Get Available Delivery Providers">
    Check which DSPs are available for delivery.

    ```javascript Node.js theme={null}
    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.
    ```
  </Step>

  <Step title="Validate Product for Delivery">
    Ensure product meets all provider requirements.

    ```javascript Node.js theme={null}
    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(', ')}`);
      }
    }
    ```
  </Step>

  <Step title="Create Delivery">
    Deliver the product to selected DSPs.

    ```javascript Node.js theme={null}
    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`);
    ```

    <Note>
      See the [Product Delivery Guide](/guides/product-delivery) for complete delivery workflows.
    </Note>
  </Step>

  <Step title="Monitor Delivery Status">
    Track delivery progress for each provider.

    ```javascript Node.js theme={null}
    // 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);
      }
    }
    ```
  </Step>
</Steps>

## Workflow 6: Complete Release Lifecycle

End-to-end workflow from asset upload to DSP delivery and royalty collection.

<Steps>
  <Step title="Upload and Configure Assets">
    Upload audio files and configure DDEX metadata.

    ```javascript Node.js theme={null}
    // 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);
    ```
  </Step>

  <Step title="Create Product (Release)">
    Group assets into a product/release.

    ```javascript Node.js theme={null}
    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);
    ```
  </Step>

  <Step title="Configure Splits">
    Set up revenue sharing for the release.

    ```javascript Node.js theme={null}
    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');
    ```
  </Step>

  <Step title="Deliver to DSPs">
    Send the release to streaming platforms (see Workflow 5).

    ```javascript Node.js theme={null}
    // 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');
    ```
  </Step>

  <Step title="Monitor and Collect Royalties">
    Process royalty reports as they arrive (see Workflow 3).

    ```javascript Node.js theme={null}
    // 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);
    ```
  </Step>

  <Step title="Process Payments">
    Distribute earnings to collaborators (see Workflow 4).

    ```javascript Node.js theme={null}
    // Create payments for eligible users
    // Track payment completion
    // Notify users
    ```
  </Step>
</Steps>

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