Skip to main content

Overview

The Royalti.io API provides comprehensive functionality for delivering music products to Digital Service Providers (DSPs) like Spotify, Apple Music, YouTube Music, and more. This guide covers the complete delivery workflow, from preparation to monitoring.

Key Features

  • Multi-Provider Delivery - Deliver to multiple DSPs simultaneously
  • Real-Time Validation - Pre-flight checks before delivery
  • Batch Operations - Process up to 100 products at once
  • Status Monitoring - Track delivery progress and logs
  • Automatic Retry - Built-in retry logic for failed deliveries
  • Legacy Support - Backward-compatible endpoints maintained

Delivery Methods

The API provides two sets of delivery endpoints: Modern, REST-compliant endpoints for product delivery with full CRUD operations:
  • /product/delivery-providers - Get available providers
  • /product/{id}/deliveries - Manage deliveries
  • /product/batch-delivery - Batch operations

Legacy API (Deprecated)

Maintained for backward compatibility:
  • /product/{id}/delivery - Trigger delivery
  • /product/{id}/delivery/status - Get status
New integrations should use the RESTful API (/deliveries). Legacy endpoints will be deprecated in a future release.

Prerequisites

Before delivering a product, ensure:
  1. Product has a valid UPC - Required for all deliveries
  2. Assets have ISRCs - Required for track-level metadata
  3. Metadata is complete - Provider-specific requirements vary
  4. Provider is configured - Tenant must have access to the provider

Quick Start

1

Check Available Providers

Retrieve the list of providers available to your workspace:
curl -X GET https://api.royalti.io/product/delivery-providers \
  -H "Authorization: Bearer YOUR_API_TOKEN"
Note the requiredFields for each provider - you’ll need these for validation.
2

Validate Your Product

Before delivering, validate your product meets provider requirements:
curl -X POST https://api.royalti.io/product/prod-123/deliveries/validate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "providers": ["spotify-ddex-sftp", "apple-ddex-api"]
  }'
Validation is a dry-run - it checks requirements without actually delivering. Fix any errors before proceeding.
3

Initiate Delivery

Once validation passes, initiate delivery to one or more DSPs:
curl -X POST https://api.royalti.io/product/prod-123/deliveries \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "providers": ["spotify-ddex-sftp", "apple-ddex-api"],
    "autoDeliver": true,
    "settings": {
      "releaseType": "Album"
    }
  }'
Parameters:
  • providers (required): Array of provider IDs or single provider string
  • autoDeliver (optional): Set to false for validation only (default: true)
  • settings (optional): Custom DDEX settings to override product defaults
4

Monitor Delivery Progress

Track delivery status and progress:
curl -X GET "https://api.royalti.io/product/prod-123/deliveries?status=processing" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
Query Parameters:
  • provider - Filter by specific provider
  • status - Filter by delivery status
  • deliveryId - Get specific delivery

Understanding Delivery Providers

Each provider has specific requirements and capabilities. Use the /product/delivery-providers endpoint to discover what’s available. Response Example:
{
  "status": "success",
  "message": "Available delivery providers retrieved from database",
  "data": [
    {
      "id": "spotify-ddex-sftp",
      "name": "Spotify (DDEX ERN)",
      "messageType": "ERN",
      "deliveryMethod": "SFTP",
      "requiredFields": {
        "product": ["title", "upc", "displayArtist", "releaseDate"],
        "asset": ["title", "isrc", "displayArtist"]
      },
      "requiredAssets": {
        "minimum": 1,
        "fields": []
      }
    }
  ]
}

Provider Information

  • id: Unique identifier for API calls
  • name: Human-readable display name
  • messageType: Format used (ERN, MEAD, CSV)
  • deliveryMethod: Transport protocol (SFTP, FTP, API, HTTP)
  • requiredFields: Mandatory metadata fields
  • requiredAssets: Minimum asset requirements

Delivery Status Lifecycle

Deliveries progress through the following statuses: Status Definitions:
  • pending - Queued, not yet started
  • processing - Actively being delivered
  • delivered - Successfully completed
  • failed - Delivery failed, can be retried
  • error - System error occurred
  • rejected - Provider rejected the delivery
  • cancelled - Manually cancelled
  • retry - Scheduled for retry

Monitoring Delivery Status

Track delivery progress with detailed logs and status information:
# Get all deliveries for a product
curl -X GET https://api.royalti.io/product/prod-123/deliveries \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# Get specific delivery details
curl -X GET https://api.royalti.io/product/prod-123/deliveries/delivery-456 \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Handling Failed Deliveries

If a delivery fails, you can retry it with the retry endpoint:
curl -X PUT https://api.royalti.io/product/prod-123/deliveries/delivery-456/retry \
  -H "Authorization: Bearer YOUR_API_TOKEN"
Retry Requirements:
  • Delivery must be in retryable status (failed, error, rejected, cancelled)
  • Cannot exceed maximum retry attempts (default: 3)

Cancelling Deliveries

Cancel a pending or in-progress delivery:
curl -X DELETE https://api.royalti.io/product/prod-123/deliveries/delivery-456 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Product metadata needs updating"}'
Cancellable Statuses:
  • pending - Not yet started
  • retry - Scheduled for retry
  • processing - Currently in progress
Cancelling a delivery that’s already completed on the provider side may require manual intervention with the DSP.

Batch Delivery

Deliver multiple products to the same provider efficiently:
curl -X POST https://api.royalti.io/product/batch-delivery \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "productIds": ["prod-123", "prod-456", "prod-789"],
    "provider": "spotify-ddex-sftp",
    "priority": "normal",
    "settings": {
      "releaseType": "Album"
    }
  }'
Limitations:
  • Maximum 100 products per batch
  • All products must have UPC codes
  • All products delivered to same provider
Response:
{
  "status": "success",
  "message": "Batch delivery initiated successfully",
  "data": {
    "batchId": "batch_1234567890",
    "totalProducts": 3,
    "foundProducts": 3,
    "successfulDeliveries": 3,
    "failedDeliveries": 0,
    "provider": "spotify-ddex-sftp",
    "deliveries": [
      {
        "productId": "prod-123",
        "upc": "123456789012",
        "status": "queued",
        "deliveryId": "delivery-001"
      }
    ]
  }
}

Common Use Cases

Single Product, Multiple Providers

Deliver one product to multiple DSPs:
POST /product/prod-123/deliveries
{
  "providers": [
    "spotify-ddex-sftp",
    "apple-ddex-api",
    "youtube-ddex-api"
  ]
}

Validation Only (Dry-Run)

Check if product is ready without delivering:
POST /product/prod-123/deliveries
{
  "providers": ["spotify-ddex-sftp"],
  "autoDeliver": false
}

Custom DDEX Settings

Override default settings for specific delivery:
POST /product/prod-123/deliveries
{
  "providers": ["spotify-ddex-sftp"],
  "settings": {
    "releaseType": "Single",
    "recordLabel": "Custom Label",
    "copyrightYear": 2024
  }
}

Troubleshooting

Error: 400 Bad Request - Product must have a UPC to be deliveredCause: The product doesn’t have a UPC assigned, which is required for all DSP deliveries.Solution: Add a UPC to your product before attempting delivery:
curl -X PUT https://api.royalti.io/product/prod-123 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"upc": "123456789012"}'
Error: Provider 'xyz-provider' not available for tenantCause: The provider you’re trying to use isn’t configured for your workspace, or the provider ID is incorrect.Solution:
  1. Check available providers for your workspace:
    GET /product/delivery-providers
    
  2. Verify you’re using the correct provider ID from the response
  3. Contact support if you need access to a specific provider
Error: Validation fails with missing field messagesExample:
{
  "provider": "spotify-ddex-sftp",
  "isValid": false,
  "errors": ["Missing required field: releaseDate"],
  "suggestions": {
    "releaseDate": "2024-09-01"
  }
}
Solution: The validation response includes specific field suggestions. Update your product with the missing data:
curl -X PUT https://api.royalti.io/product/prod-123 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"releaseDate": "2024-09-01"}'
Error: 404 Not Found - Product not foundCause: The product ID doesn’t exist or doesn’t belong to your workspace.Solution:
  1. Verify the product ID is correct
  2. Check the product exists in your workspace:
    GET /product/prod-123
    
  3. Ensure you’re using the correct API token for your workspace
Issue: Delivery status remains “processing” for extended periodCause: Large files, slow provider connections, or provider-side delays.Solution:
  1. Check delivery logs for detailed progress:
    GET /product/prod-123/deliveries/delivery-456
    
  2. Typical delivery times:
    • Small releases (1-5 tracks): 5-15 minutes
    • Albums (6-20 tracks): 15-45 minutes
    • Large catalogs: 1-3 hours
  3. If stuck for more than expected time, contact support with the delivery ID
Error: 400 Bad Request - Delivery has reached maximum retry attempts (3)Cause: The delivery has failed 3 times and can’t be retried automatically.Solution:
  1. Review delivery logs to understand why it’s failing:
    GET /product/prod-123/deliveries/delivery-456
    
  2. Fix the underlying issue (usually metadata or file problems)
  3. Create a new delivery instead of retrying:
    POST /product/prod-123/deliveries
    

Best Practices

Always Validate First

Run validation before delivery to catch issues early and avoid failed deliveries. This saves time and API quota.

Monitor Progress

Poll delivery status regularly to track progress and handle failures promptly. Set up webhooks for real-time notifications.

Use Batch Delivery

For multiple products, use batch delivery to reduce API calls and improve efficiency. Process up to 100 products at once.

Handle Retries

Implement automatic retry logic for transient failures with exponential backoff. Don’t retry immediately on failure.

Complete Integration Example

Here’s a full workflow example with error handling and monitoring:
const axios = require('axios');

async function deliverProduct(productId, providers) {
  const baseURL = 'https://api.royalti.io';
  const headers = {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  };

  try {
    // Step 1: Validate
    console.log('Validating product...');
    const validation = await axios.post(
      `${baseURL}/product/${productId}/deliveries/validate`,
      { providers },
      { headers }
    );

    if (!validation.data.data.validations.every(v => v.isValid)) {
      console.error('Validation failed:', validation.data);
      return;
    }

    // Step 2: Deliver
    console.log('Initiating delivery...');
    const delivery = await axios.post(
      `${baseURL}/product/${productId}/deliveries`,
      { providers, autoDeliver: true },
      { headers }
    );

    const deliveryIds = delivery.data.data.deliveries.map(d => d.deliveryId);
    console.log('Deliveries initiated:', deliveryIds);

    // Step 3: Monitor
    console.log('Monitoring delivery status...');
    for (const deliveryId of deliveryIds) {
      await monitorDelivery(productId, deliveryId);
    }

  } catch (error) {
    console.error('Delivery error:', error.response?.data || error.message);
  }
}

async function monitorDelivery(productId, deliveryId) {
  const baseURL = 'https://api.royalti.io';
  const headers = { 'Authorization': `Bearer ${API_TOKEN}` };

  let attempts = 0;
  const maxAttempts = 60; // 5 minutes with 5-second intervals

  while (attempts < maxAttempts) {
    const response = await axios.get(
      `${baseURL}/product/${productId}/deliveries/${deliveryId}`,
      { headers }
    );

    const { status, progress } = response.data.data;
    console.log(`Delivery ${deliveryId}: ${status} (${progress}%)`);

    if (status === 'delivered') {
      console.log('Delivery completed successfully!');
      return;
    }

    if (['failed', 'error', 'rejected'].includes(status)) {
      console.error('Delivery failed:', response.data.data);
      return;
    }

    await new Promise(resolve => setTimeout(resolve, 5000));
    attempts++;
  }

  console.warn('Monitoring timeout reached');
}

// Usage
deliverProduct('prod-123', ['spotify-ddex-sftp', 'apple-ddex-api']);

Support

Need help with product delivery?