# Royalti API

> Royalti.io REST API v2.6 pattern reference for developers. Covers authentication, CRUD patterns, pagination, error handling, webhooks, WebSocket events, data models, DDEX distribution, AI chat, source creator, global search, checklist workflows, music publishing, billing, and more. Use when helping developers integrate with the Royalti API (api.royalti.io) or when writing integration code, API documentation, or troubleshooting API issues.

- Skill: `royalti-io/royalti-api` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add royalti-io/royalti-api`
- Raw SKILL.md: https://api.skillmd.com/api/skills/royalti-io/royalti-api/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: MIT
- Author: Royalti-io (https://skillmd.com/u/royalti-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/royalti-io/royalti-api

---


# Royalti API v2.6 — Developer Reference

Pattern reference for integrating with the Royalti.io REST API.

**Base URL:** `https://api.royalti.io`
**Current Version:** 2.6.4
**Architecture:** Multi-tenant (workspace-scoped)

---

## 1. Authentication

### Token Types

| Token | Prefix | Expiry | Use Case |
|-------|--------|--------|----------|
| JWT Access Token | — | 6 hours | All API requests |
| JWT Refresh Token | — | 1 day | Obtain new access tokens |
| Workspace API Key | `RWAK` | Never (revocable) | Programmatic workspace access |
| User API Key | `RUAK` | Never (revocable) | Programmatic user access |

### Two-Step JWT Login

```bash
# Step 1: Login — returns refresh token + workspace list
curl -X POST https://api.royalti.io/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com", "password": "secret" }'

# Response:
# {
#   "refresh_token": "eyJ...",
#   "workspaces": [{ "id": "ws_abc", "name": "My Label", ... }]
# }

# Step 2: Exchange refresh token for access token (scoped to a workspace)
curl https://api.royalti.io/auth/authtoken?currentWorkspace=ws_abc \
  -H "Authorization: Bearer <refresh_token>"

# Response:
# { "data": { "access_token": "eyJ..." } }
```

### Using the Access Token

All subsequent requests include the access token:

```bash
curl https://api.royalti.io/artist/ \
  -H "Authorization: Bearer <access_token>"
```

### API Key Authentication

API keys can be used instead of JWT tokens. Pass them in the `Authorization` header:

```bash
# Workspace API key
curl https://api.royalti.io/artist/ \
  -H "Authorization: Bearer RWAK_abc123..."

# User API key
curl https://api.royalti.io/asset \
  -H "Authorization: Bearer RUAK_def456..."
```

### API Key Auth Resolution (Internals)

Understanding how RWAK/RUAK keys resolve to a workspace context helps debug auth errors:

```
1. Token prefix detected as RWAK/RUAK (not JWT)
2. ApiKeys table lookup → finds TenantId
3. Tenants table lookup → finds workspace (must be status='active')
4. Tenants.user field → used to find the owner TenantUser via TenantUser.findByPk()
5. Owner TenantUser populates req.user for all downstream handlers
```

**Key detail:** `Tenants.user` must store a `TenantUser.id` (UUID), NOT a `User.id`. These are different tables with different PKs. If this field is wrong, the API returns `"Workspace user not found"` (HTTP 404) despite the API key being valid.

The `Tenants.user` field is also used by:
- **Accounting endpoints** — to exclude the workspace owner from payee/due calculations
- **Admin auth** — to resolve workspace context for admin JWT tokens

### Other Auth Methods

| Endpoint | Method | Purpose |
|----------|--------|---------|
| `POST /auth/loginlink` | POST | Magic link login |
| `POST /auth/forgotpassword` | POST | Request password reset |
| `PATCH /auth/resetpassword?code=CODE` | PATCH | Apply password reset |
| `GET /auth/google` | GET | Google OAuth |
| `GET /auth/linkedin` | GET | LinkedIn OAuth |
| `GET /auth/facebook` | GET | Facebook OAuth |

### Rate Limiting

- Login endpoint: **20 requests per 3 minutes** per IP
- AI endpoints: Rate limited per tenant subscription tier (billing-period aligned)
- Other endpoints: No documented limits (subject to fair use)

### RBAC Roles (ascending privilege)

```
guest < user < admin < owner < super_admin < main_super_admin
```

### Feature Gating

Certain API features require subscription-level feature flags:

| Feature Flag | Required For |
|-------------|-------------|
| `royaltyAccess` | Royalty file upload, source creator, analytics |
| `aiAgent` | AI chat conversations and messages |
| `addonsAccess` | DDEX, Merlin, Publisher addons |

Addon-specific checks: `ddex`, `publisher`, `merlin` — require the addon to be enabled on the tenant.

---

## 2. Request Patterns

### Standard Headers

```
Authorization: Bearer <access_token | API_KEY>
Content-Type: application/json
```

### Pagination (all list endpoints)

| Param | Default | Description |
|-------|---------|-------------|
| `page` | `1` | Page number |
| `size` | `10`–`20` | Items per page (max `100`) |
| `sort` | `updatedAt` | Sort field |
| `order` | `desc` | `asc` or `desc` |

```bash
GET /artist/?page=2&size=25&sort=artistName&order=asc
```

### Filtering (analytics endpoints)

| Param | Type | Description |
|-------|------|-------------|
| `start` | `YYYY-MM-DD` | Date range start |
| `end` | `YYYY-MM-DD` | Date range end |
| `dsp` | CSV string | Filter by DSP/platform |
| `country` | CSV string | Filter by territory (ISO 3166-1 alpha-2) |
| `artists` | CSV string | Filter by artist IDs |
| `upc` | CSV string | Filter by UPC |
| `isrc` | CSV string | Filter by ISRC |
| `aggregator` | CSV string | Filter by distributor |
| `type` | string | Filter by sale type |
| `periodFilterType` | `accounting` or `sale` | Date filtering mode |
| `includePreviousPeriod` | boolean | Include comparison data |
| `table_name` | string | Filter by royalty file source table |

### Search

Many list endpoints support a `search` query param for text-based filtering:

```bash
GET /artist/?search=drake&page=1&size=10
```

### Bulk Operations

Most resources support bulk create and delete:

```bash
POST /artist/bulk          # Create multiple artists
DELETE /artist/bulk/delete  # Delete multiple artists
POST /asset/bulksplits     # Assign splits to multiple assets
```

---

## 3. Response Patterns

### Success Response

```json
{
  "status": "success",
  "message": "Operation successful",
  "data": { ... }
}
```

### Paginated List Response

Response shapes vary by resource. Most use `data[]`, but some use resource-specific keys:

```json
// Standard shape (labels, splits, payments, expenses, revenue, notifications)
{
  "status": "success",
  "data": [ ... ],
  "totalItems": 142,
  "totalPages": 15,
  "currentPage": 1
}

// Users — uses "Users" key, no "status" field
{
  "message": "success",
  "totalItems": 10,
  "Users": [ ... ],
  "totalPages": 1,
  "currentPage": 1
}

// Artists — uses "Artists" key, includes "filteredItems"
{
  "totalItems": 50,
  "Artists": [ ... ],
  "totalPages": 5,
  "currentPage": 1,
  "filteredItems": 50
}

// Products — uses "Products" key
// Assets — uses "data" key
```

**Tip:** Always check for both `data` and the resource-specific key (e.g., `Users`, `Artists`, `Products`) when parsing list responses.

### Summary Response (v2.6.4+)

Available on: `/artist/summary`, `/asset/summary`, `/product/summary`, `/user/summary`, `/split/summary`, `/payment/summary`, `/expense/summary`, `/revenue/summary`, `/file/summary`

```json
{
  "message": "Summary retrieved successfully",
  "summary": {
    "total": 250,
    "byStatus": { "active": 200, "inactive": 50 },
    "byFormat": { "Single": 120, "Album": 80, "EP": 50 },
    "byType": { "Audio": 230, "Video": 20 },
    "revenue": { "total": 15000.50, "currency": "USD" }
  }
}
```

### Error Response

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": ["optional", "array", "of", "details"]
  }
}
```

### Common HTTP Status Codes

| Code | Meaning |
|------|---------|
| `200` | Success |
| `201` | Created |
| `400` | Bad request / validation error |
| `401` | Unauthorized (missing or invalid token) |
| `403` | Forbidden (insufficient role/permissions) |
| `404` | Resource not found |
| `409` | Conflict (duplicate, schema disparity) |
| `422` | Unprocessable entity |
| `429` | Rate limited |
| `500` | Server error |

---

## 4. Core Resources

All resources follow the same CRUD pattern unless noted. Every resource is workspace-scoped (multi-tenant).

### Standard CRUD Pattern

```
GET    /{resource}/          # List (paginated)
POST   /{resource}/          # Create
GET    /{resource}/{id}      # Get by ID
PUT    /{resource}/{id}      # Update
DELETE /{resource}/{id}      # Delete
GET    /{resource}/summary   # Summary stats (v2.6.4+)
GET    /{resource}/download/csv  # CSV export
POST   /{resource}/bulk      # Bulk create
DELETE /{resource}/bulk/delete   # Bulk delete
```

### Users (`/user/`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `GET /user/{id}/stats` | User royalty statistics |
| `GET /user/{id}/monthly` | Monthly breakdown |
| `GET /user/{id}/artists` | User's artists |
| `GET /user/{id}/assets` | User's assets |
| `GET /user/{id}/products` | User's products |
| `GET /user/{id}/aut` | User accounting data |
| `GET /user/invites` | Pending invitations |
| `POST /user/invites/{id}/resend` | Resend invitation |
| `POST /user/invites/{id}/cancel` | Cancel invitation |

### Artists (`/artist/`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `GET /artist/{id}/assets` | Artist's tracks |
| `GET /artist/{id}/products` | Artist's releases |
| `GET /artist/{id}/splits` | Artist's splits |
| `GET /artist/{id}/splits/{type}` | Splits by type |
| `PUT /artist/{id}/splits/{type}` | Update split by type |
| `DELETE /artist/{id}/splits/{type}` | Delete split by type |
| `POST /artist/{id}/splits` | Create artist default split |
| `GET /artist/{id}/stats` | Artist analytics |
| `POST /artist/{id}/merge` | Merge duplicate artists |
| `POST /artist/bulksplit` | Bulk assign splits |
| `POST /artist/download/csv` | Download artist CSV |

Key fields: `artistName`, `signDate`, `label`, `publisher`, `copyright`, `externalId`, `artistImg`, `links` (spotify/youtube/instagram/etc.), `genres`, `realName`, `pseudonyms`

### Assets / Tracks (`/asset`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `GET /asset/{id}/artists` | Track's artists |
| `GET /asset/{id}/stats` | Track analytics |
| `POST /asset/{id}/setdefaultsplit` | Set default split |
| `GET /asset/{id}/media` | Track media files |
| `DELETE /asset/{id}/media/{mediaName}` | Delete media |
| `GET /asset/{id}/ddex-metadata` | DDEX metadata |
| `GET /asset/{id}/ddex-readiness` | DDEX readiness check |
| `GET /asset/{assetId}/works` | Musical works (ISWC) |
| `POST /asset/bulksplits` | Bulk assign splits |
| `DELETE /asset/bulk/deletesplit` | Bulk remove splits |
| `POST /asset/bulk/defaultsplit` | Bulk set default splits |

Key fields: `title`, `ISRC`, `type` (Audio|Video|Ringtone|YouTube), `version`, `displayArtist`, `mainArtist[]`, `mainGenre[]`, `subGenre[]`, `explicit`, `language`, `tempo`, `key`, `mood[]`, `lyrics`, `label`, `copyright`, `publisher`

### Products / Releases (`/product/`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `GET /product/{id}/artists` | Release's artists |
| `GET /product/{id}/assets` | Release's tracks |
| `GET /product/{id}/stats` | Release analytics |
| `POST /product/{id}/setdefaultsplit` | Set default split |
| `GET /product/{id}/media` | Release artwork/media |
| `GET /product/{id}/delivery` | Delivery info |
| `GET /product/{id}/delivery/status` | Delivery status |
| `POST /product/batch-delivery` | Batch deliver releases |
| `GET /product/{id}/deliveries` | Delivery history |
| `POST /product/{id}/deliveries/{deliveryId}/retry` | Retry failed delivery |
| `GET /product/delivery-providers` | Available distributors |
| `GET /product/download/metadata` | Metadata export |

Key fields: `title`, `UPC`, `format` (Single|EP|Album|LP), `release_date`, `takedown_date`, `label`, `display_artist`, `type` (Audio|Video), `catalog_number`, `status` (active|inactive|pending|Live|Taken Down|Scheduled|Error), `distribution`, `external_id`

### Splits (`/split/`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `POST /split/default` | Create split from artist default |
| `POST /split/match` | Find splits matching criteria |
| `DELETE /split/bulk/catalog-splits` | Remove catalog-level splits |

Split types: `simple`, `conditional`, `temporal`

```json
{
  "entity_type": "artist",
  "entity_id": "uuid",
  "split_type": "conditional",
  "effective_date": "2025-01-01",
  "shares": [
    { "user": "user-uuid-1", "percentage": 60 },
    { "user": "user-uuid-2", "percentage": 40 }
  ],
  "conditions": {
    "territories": ["US", "GB", "CA"],
    "mode": "include",
    "sources": ["Spotify", "Apple Music"],
    "period_start": "2025-01-01",
    "period_end": "2025-12-31",
    "memo": "North America streaming deal"
  }
}
```

**Rules:** Shares must sum to 100. `entity_type` is `artist`, `product`, or `asset`. Conditions are optional (omit for simple splits).

### Royalties / Analytics (`/royalty/`)

Read-only analytics endpoints. All support the standard filter params (start, end, dsp, country, etc.). Requires `royaltyAccess` feature.

| Endpoint | Description |
|----------|-------------|
| `GET /royalty/` | Main summary |
| `GET /royalty/month` | Monthly trends |
| `GET /royalty/dsp` | By DSP/platform |
| `GET /royalty/country` | By country/territory |
| `GET /royalty/artist` | By artist |
| `GET /royalty/product` | By product/release |
| `GET /royalty/asset` | By track |
| `GET /royalty/tables` | By data source |
| `GET /royalty/saletype` | By sale type (stream, download, etc.) |
| `GET /royalty/aggregator` | By distributor |
| `GET /royalty/accountingperiod` | By accounting period |

Analytics response shape:

```json
{
  "Downloads": 1500,
  "Streams": 250000,
  "Royalty": 1234.56,
  "Count": 251500,
  "RatePer1K": 4.91,
  "RoyaltyPercentage": 45.2,
  "CountPercentage": 38.7,
  "PreviousRoyalty": 1100.00,
  "PreviousCount": 220000
}
```

`PreviousRoyalty` and `PreviousCount` are only present when `includePreviousPeriod=true`.

### Accounting (`/accounting/`)

| Endpoint | Description |
|----------|-------------|
| `GET /accounting/{id}/stats` | User accounting stats |
| `GET /accounting/transactions` | Transaction list |
| `GET /accounting/transactions/summary` | Transaction summary |
| `GET /accounting/transactions/monthly` | Monthly transaction breakdown |
| `GET /accounting/getcurrentdue` | Current amount due per user |
| `GET /accounting/gettotaldue` | Total outstanding across workspace |
| `POST /accounting/refresh` | Recalculate accounting |
| `POST /accounting/refreshstats` | Refresh stats cache |
| `POST /accounting/users/{id}/recalculate` | Recalculate single user |
| `POST /accounting/tenant/recalculate` | Recalculate entire workspace |
| `GET /accounting/queue/status` | Processing queue status |

### Payments (`/payment/`)

Standard CRUD. Supports both JSON and `multipart/form-data` (for attaching receipts).

| Endpoint | Description |
|----------|-------------|
| `GET /payment/summary` | Payment totals |
| `POST /payment/bulk` | Bulk create payments |

### Payment Requests (`/payment-request/`)

| Endpoint | Description |
|----------|-------------|
| `GET /payment-request/` | List requests |
| `GET /payment-request/{id}` | Get request |
| `POST /payment-request/{id}/approve` | Approve request |
| `POST /payment-request/{id}/decline` | Decline request |

### Royalty Files (`/file/`)

| Endpoint | Description |
|----------|-------------|
| `GET /file/royalty` | List uploaded royalty files |
| `GET /file/royalty/{id}` | Get file details |
| `DELETE /file/royalty/{id}` | Delete file |
| `GET /file/summary` | File summary stats |
| `POST /file/createroyalty` | Upload royalty file |
| `GET /file/upload-url` | Get presigned upload URL |
| `POST /file/confirm-upload-completion` | Confirm upload finished |
| `GET /file/detection/{sessionId}` | Poll auto-detection status |
| `POST /file/confirm-detection/{sessionId}` | Confirm detected format |
| `GET /file/auto-processing-config` | Auto-processing settings |
| `POST /file/enable-auto-processing` | Enable auto-processing |
| `GET /file/sources` | List royalty sources (DSPs) |
| `GET /file/{id}/download` | Download file |
| `GET /file/processing/{jobId}` | Processing job status |
| `POST /file/session/{sessionId}/start-source-creator` | Bridge to source creator for unknown formats |
| `POST /file/upload-from-drive` | Upload from Google Drive |

### Labels (`/labels/`)

Standard CRUD plus:

| Endpoint | Description |
|----------|-------------|
| `GET /labels/hierarchy` | Label tree structure |

### Releases (`/releases`)

Full release lifecycle with media management:

**Release CRUD:**

| Endpoint | Description |
|----------|-------------|
| `POST /releases` | Create release (draft) |
| `GET /releases` | List releases |
| `GET /releases/stats` | Release statistics |
| `GET /releases/{id}` | Get release details |
| `PUT /releases/{id}` | Update release |
| `DELETE /releases/{id}` | Delete release |

**Lifecycle Workflow:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /releases/{id}/submit` | user | Submit for review |
| `POST /releases/{id}/review` | admin | Start review |
| `POST /releases/{id}/feedback` | admin | Add feedback |
| `POST /releases/{id}/revert-status` | admin | Revert release status |

**Release Media:**

| Endpoint | Description |
|----------|-------------|
| `POST /releases/{id}/media/files` | Upload release files (artwork, etc.) |
| `POST /releases/{id}/media/links` | Submit release links |
| `GET /releases/{id}/media` | Get release media |
| `DELETE /releases/{id}/media/{mediaId}` | Delete release media |

**Track Management:**

| Endpoint | Description |
|----------|-------------|
| `POST /releases/{id}/tracks` | Create track in release |
| `PUT /releases/{id}/tracks/{trackId}` | Update track |
| `DELETE /releases/{id}/tracks/{trackId}` | Delete track |
| `POST /releases/{id}/tracks/reorder` | Reorder release tracks |
| `POST /releases/{id}/tracks/link-asset` | Link existing asset to release |

**Track Media:**

| Endpoint | Description |
|----------|-------------|
| `POST /releases/{id}/tracks/{trackId}/media/file` | Upload track audio/video file |
| `POST /releases/{id}/tracks/{trackId}/media/link` | Submit track link |
| `GET /releases/{id}/tracks/{trackId}/media` | Get track media |
| `DELETE /releases/{id}/tracks/{trackId}/media/{mediaId}` | Delete track media |

### Expenses & Revenue

Both follow standard CRUD at `/expense/` and `/revenue/` with summary endpoints.

### Notifications (`/notifications/`)

| Endpoint | Description |
|----------|-------------|
| `GET /notifications/` | List notifications |
| `PATCH /notifications/{id}/read` | Mark as read |
| `PATCH /notifications/read-all` | Mark all as read |
| `GET /notifications/unread-count` | Unread count |

### Downloads / Reports (`/download/`)

| Endpoint | Description |
|----------|-------------|
| `POST /download/generate` | Generate report download |
| `GET /download/{id}/status` | Check generation status |
| `GET /download/list` | List available downloads |

---

## 5. Global Search (`/search`)

Cross-entity search across the entire catalog.

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /search?q={query}&types={types}` | user | Global search |
| `GET /search/recent` | user | Get recent searches |
| `DELETE /search/recent` | user | Clear search history |

**Query Parameters:**

| Param | Type | Description |
|-------|------|-------------|
| `q` | string | Search query text |
| `types` | CSV string | Entity types to search (e.g., `artist,product,asset,user`) |

---

## 6. Royalty Sources (`/sources`)

Manage royalty data sources (DSPs, distributors, custom sources).

**Tenant Routes:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /sources` | user | List tenant's royalty sources |
| `POST /sources` | user | Create new tenant source |
| `GET /sources/{id}` | user | Get source details |
| `PUT /sources/{id}` | user | Update source |
| `DELETE /sources/{id}` | user | Delete source |
| `POST /sources/{id}/activate` | user | Activate source |
| `POST /sources/{id}/deactivate` | user | Deactivate source |

---

## 7. Source Creator (`/source-creator`)

AI-powered wizard for creating new royalty source definitions. Requires `royaltyAccess` feature.

**Analysis & Mapping:**

| Endpoint | Description |
|----------|-------------|
| `POST /source-creator/analyze` | Upload and analyze file (multipart) |
| `POST /source-creator/ai-map-columns` | Get AI column mapping suggestions (rate limited) |
| `PUT /source-creator/sessions/{id}/mappings` | Save user-confirmed mappings |
| `PUT /source-creator/sessions/{id}/periods` | Confirm accounting periods |
| `POST /source-creator/sessions/{id}/suggest-name` | Get smart name suggestion |

**Query Generation & Testing:**

| Endpoint | Description |
|----------|-------------|
| `POST /source-creator/generate-queries` | Generate BigQuery SQL from mappings (rate limited) |
| `POST /source-creator/test-queries` | Test queries against sample data |

**Save & Reuse:**

| Endpoint | Description |
|----------|-------------|
| `POST /source-creator/save` | Save as draft source |
| `POST /source-creator/reuse` | Reuse existing source for new file |
| `GET /source-creator/sources/{id}/mappings` | Get source column mappings |
| `GET /source-creator/sources/{id}/queries` | Get source queries |

**Session Management:**

| Endpoint | Description |
|----------|-------------|
| `GET /source-creator/sessions` | List sessions |
| `GET /source-creator/sessions/{id}` | Get session details |
| `DELETE /source-creator/sessions/{id}` | Delete session |

**Admin Routes** (super admin only):

| Endpoint | Description |
|----------|-------------|
| `GET /source-creator/admin/drafts` | List all draft sources across tenants |
| `GET /source-creator/admin/sessions/{id}` | Get full session detail |
| `POST /source-creator/admin/{id}/promote` | Promote draft to global source |

### Source Creator Flow

```
1. POST /source-creator/analyze              → Upload file, get column analysis
2. POST /source-creator/ai-map-columns       → Get AI mapping suggestions
3. PUT  /source-creator/sessions/{id}/mappings → Confirm mappings
4. PUT  /source-creator/sessions/{id}/periods  → Confirm accounting periods
5. POST /source-creator/generate-queries      → Generate BigQuery SQL
6. POST /source-creator/test-queries          → Validate against sample data
7. POST /source-creator/save                  → Save as draft source
   (Admin) POST /source-creator/admin/{id}/promote → Promote to global
```

---

## 8. Checklist / Data Quality (`/checklist`)

Data quality validation, catalog enrichment, and workflow orchestration. Requires admin role.

**Validation Checks:**

| Endpoint | Description |
|----------|-------------|
| `GET /checklist/royaltyassets` | Assets appearing in royalty data but not in catalog |
| `GET /checklist/royaltyproducts` | Products appearing in royalty data but not in catalog |
| `GET /checklist/assetsplits` | Assets missing split assignments |
| `GET /checklist/productsplits` | Products missing split assignments |
| `GET /checklist/allsplits` | All split coverage issues |
| `GET /checklist/missingroyaltysplits` | Royalty items without splits |
| `GET /checklist/artistsplits` | Artist split user issues |
| `GET /checklist/missingprimaryartists` | Assets/products missing primary artists |
| `GET /checklist/duplicateartists` | Duplicate artist detection |
| `GET /checklist/productswithoutassets` | Products with no linked assets |
| `GET /checklist/assetswithoutproducts` | Assets with no linked products |

**Import from Royalty Data:**

| Endpoint | Description |
|----------|-------------|
| `POST /checklist/royaltyassets/import` | Import missing assets from royalty data |
| `POST /checklist/royaltyproducts/import` | Import missing products from royalty data |

**Catalog Enrichment:**

| Endpoint | Description |
|----------|-------------|
| `POST /checklist/assets/enrich` | Enrich assets with external metadata |
| `POST /checklist/products/enrich` | Enrich products with external metadata |
| `GET /checklist/enrichment` | List enrichment items |
| `GET /checklist/enrichment/job/{jobId}` | Get enrichment job status |
| `GET /checklist/enrichment/{id}` | Get enrichment item details |
| `PUT /checklist/enrichment/{id}` | Update enrichment item |
| `POST /checklist/enrichment/approve` | Approve enrichment items |
| `POST /checklist/enrichment/reject` | Reject enrichment items |
| `POST /checklist/enrichment/re-enrich` | Re-enrich rejected items |
| `POST /checklist/enrichment/cleanup` | Cleanup enrichment items |

**Workflow Orchestration:**

| Endpoint | Description |
|----------|-------------|
| `POST /checklist/workflow` | Start a checklist workflow |
| `GET /checklist/workflow` | List workflows |
| `GET /checklist/workflow/{id}` | Get workflow details |
| `POST /checklist/workflow/{id}/respond` | Respond to workflow prompt |
| `POST /checklist/workflow/{id}/cancel` | Cancel workflow |

---

## 9. DDEX Distribution (`/ddex`)

Digital Data Exchange standard support for release distribution. Requires `addonsAccess` feature + `ddex` addon.

**ERN (Electronic Release Notification):**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /ddex/ern/generate` | admin | Generate ERN message for a release |
| `POST /ddex/ern/generate-batch` | admin | Generate multiple ERN messages |

**MEAD (Music Enrichment and Description):**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /ddex/mead/generate` | admin | Generate MEAD message |
| `PUT /ddex/mead/{entityId}` | admin | Update MEAD metadata |

**Message Management:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /ddex/messages` | user | List all DDEX messages |
| `GET /ddex/messages/{messageId}` | user | Get message details |
| `POST /ddex/messages/{messageId}/validate` | admin | Validate message |
| `GET /ddex/messages/{messageId}/download` | admin | Download message XML |

**Delivery:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /ddex/delivery/deliver/{messageId}` | admin | Deliver message to provider |
| `POST /ddex/delivery/deliver-batch` | admin | Batch delivery |
| `POST /ddex/delivery/retry/{messageId}` | admin | Retry failed delivery |
| `GET /ddex/delivery/status/{messageId}` | user | Get delivery status |
| `GET /ddex/delivery/logs/{messageId}` | admin | Get delivery logs |
| `POST /ddex/delivery/test-connection` | owner | Test DSP connection |
| `POST /ddex/delivery/test-all-connections` | owner | Test all provider connections |

**Provider Management:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /ddex/providers` | user | List available DSP providers |
| `GET /ddex/providers/{providerId}` | user | Get provider details |
| `GET /ddex/providers/{providerId}/stats` | user | Get provider statistics |

**Tenant Provider Configuration:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /ddex/tenant-providers` | admin | List tenant's configured providers |
| `POST /ddex/tenant-providers` | owner | Configure new provider |
| `PUT /ddex/tenant-providers/{id}` | owner | Update provider config |
| `DELETE /ddex/tenant-providers/{id}` | owner | Remove provider config |

**Queue & Monitoring:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /ddex/queue/jobs` | admin | List queue jobs |
| `GET /ddex/queue/jobs/{jobId}` | admin | Get job details |
| `GET /ddex/queue/jobs/{jobId}/logs` | admin | Get job logs |
| `GET /ddex/monitoring/dashboard` | admin | Monitoring dashboard |

**Usage:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /ddex/usage` | admin | Get DDEX usage stats |
| `GET /ddex/usage/dashboard` | admin | Usage dashboard |

### DDEX Distribution Flow

```
1. POST /ddex/tenant-providers                    → Configure DSP provider (one-time)
2. POST /ddex/delivery/test-connection            → Verify connection
3. POST /ddex/ern/generate { releaseId }          → Generate ERN message
4. POST /ddex/messages/{messageId}/validate       → Validate message
5. POST /ddex/delivery/deliver/{messageId}        → Deliver to DSP
6. GET  /ddex/delivery/status/{messageId}         → Monitor delivery status
   If failed: POST /ddex/delivery/retry/{messageId}
```

---

## 10. AI Chat Agent (`/ai`)

Conversational AI assistant with workspace context. Requires `aiAgent` feature. Supports Vercel AI SDK streaming.

**Conversations:**

| Endpoint | Description |
|----------|-------------|
| `POST /ai/conversations` | Create conversation |
| `GET /ai/conversations` | List conversations |
| `GET /ai/conversations/{conversationId}` | Get conversation |
| `PUT /ai/conversations/{conversationId}` | Update conversation |
| `DELETE /ai/conversations/{conversationId}` | Archive conversation |

**Messages:**

| Endpoint | Description |
|----------|-------------|
| `GET /ai/conversations/{conversationId}/messages` | Get messages |
| `POST /ai/conversations/{conversationId}/messages` | Send message (rate limited) |
| `POST /ai/chat` | Vercel AI SDK streaming chat |

**Status & Configuration:**

| Endpoint | Description |
|----------|-------------|
| `GET /ai/health` | Health check (public) |
| `GET /ai/rate-limit` | Check rate limit status |
| `GET /ai/budget-status` | Check cost budget status |
| `GET /ai/suggestions` | Get context-aware follow-up suggestions |
| `GET /ai/config` | Get AI configuration |
| `POST /ai/feedback` | Submit conversation feedback |
| `GET /ai/conversations/{conversationId}/stats` | Conversation stats |

**Retention Config (admin):**

| Endpoint | Description |
|----------|-------------|
| `GET /ai/retention-config` | Get retention policy |
| `PUT /ai/retention-config` | Update retention policy |
| `POST /ai/retention/apply-now` | Apply retention immediately |

### AI Chat Streaming

The `/ai/chat` endpoint supports Vercel AI SDK streaming format:

```bash
POST /ai/chat
Content-Type: application/json

{
  "messages": [
    { "role": "user", "content": "What are my top performing artists this quarter?" }
  ],
  "conversationId": "optional-conversation-uuid"
}
```

The response is a server-sent event stream compatible with `useChat()` from the Vercel AI SDK.

---

## 11. Merlin Addon (`/merlin`)

Automated royalty file import via FTP with approval workflows. Requires `addonsAccess` feature + `merlin` addon.

**Configuration:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /merlin/config` | owner | Create or update Merlin config |
| `GET /merlin/config` | admin | Get Merlin configuration |
| `DELETE /merlin/config` | owner | Disable Merlin integration |

**Credentials & Connection:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `PUT /merlin/credentials` | owner | Update FTP credentials |
| `POST /merlin/test-connection` | owner | Test FTP connection |
| `GET /merlin/connection-status` | admin | Get connection status |
| `PUT /merlin/features` | admin | Update enabled features |

**Import Configuration** (requires `royaltyImport` addon feature):

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /merlin/import/config` | admin | Get import config |
| `PUT /merlin/import/config` | admin | Update import config |
| `PUT /merlin/import/schedule` | admin | Update import schedule |

**Import Operations:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /merlin/import/trigger` | admin | Trigger manual import |
| `GET /merlin/import/batches` | user | List import batches |
| `GET /merlin/import/batch/{id}` | user | Get batch details |
| `POST /merlin/import/batch/{id}/cancel` | admin | Cancel batch |

**Pending Import Approval:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /merlin/pending` | user | List pending imports |
| `GET /merlin/pending/grouped` | user | Pending imports grouped by source/period |
| `GET /merlin/pending/{id}` | user | Get pending import details |
| `GET /merlin/pending/{id}/preview` | user | Preview file contents |
| `GET /merlin/pending/{id}/recommendation` | user | Get confidence recommendation |
| `POST /merlin/pending/{id}/approve` | admin | Approve pending import |
| `POST /merlin/pending/{id}/reject` | admin | Reject pending import |
| `POST /merlin/pending/bulk-approve` | admin | Bulk approve |
| `POST /merlin/pending/bulk-reject` | admin | Bulk reject |

**Auto-Approval:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /merlin/batches/{batchId}/auto-approvable` | user | Get auto-approvable candidates |
| `GET /merlin/imports/groupable` | user | Get groupable imports |

**History & Stats:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /merlin/sources` | user | List Merlin-compatible sources |
| `GET /merlin/history` | user | Import history |
| `GET /merlin/stats` | user | Import statistics |
| `GET /merlin/metrics` | user | Comprehensive metrics |

---

## 12. Data Shares (`/data-shares`)

Cross-tenant royalty data sharing for labels sharing sources.

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /data-shares` | admin | List data shares |
| `POST /data-shares` | owner | Create data share |
| `GET /data-shares/{id}` | admin | Get share details |
| `PUT /data-shares/{id}` | owner | Update share |
| `DELETE /data-shares/{id}` | owner | Delete share |

---

## 13. Music Publishing

Publishing management with CWR (Common Works Registration) export. Requires `publisher` addon.

### Publishers (`/publishers`)

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /publishers` | user | List publishers |
| `POST /publishers` | admin | Create publisher |
| `GET /publishers/{id}` | user | Get publisher |
| `PUT /publishers/{id}` | admin | Update publisher |
| `DELETE /publishers/{id}` | admin | Delete publisher |
| `GET /publishers-with-user-data` | user | Publishers with associated user data |

**Territory Management:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /publishers/{id}/territories` | user | List territories |
| `POST /publishers/{id}/territories` | admin | Add territory |
| `PUT /publishers/{id}/territories/{territoryId}` | admin | Update territory |
| `DELETE /publishers/{id}/territories/{territoryId}` | admin | Delete territory |

**Agreement Management:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /publishers/{id}/agreements` | user | List agreements |
| `POST /publishers/{id}/agreements` | admin | Create agreement |
| `PUT /publishers/{id}/agreements/{agreementId}` | admin | Update agreement |
| `DELETE /publishers/{id}/agreements/{agreementId}` | admin | Delete agreement |

**Sub-Publishing:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /sub-publishing` | admin | Create sub-publishing agreement |
| `GET /sub-publishing` | user | List sub-publishing agreements |
| `GET /sub-publishing/{id}` | user | Get agreement details |
| `PUT /sub-publishing/{id}` | admin | Update agreement |
| `POST /sub-publishing/check-conflicts` | admin | Check territory conflicts |

### Writers (`/writers`)

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /writers` | user | List writers |
| `POST /writers` | admin | Create writer |
| `GET /writers/{id}` | user | Get writer |
| `PUT /writers/{id}` | admin | Update writer |
| `DELETE /writers/{id}` | admin | Delete writer |
| `GET /writers-with-user-data` | user | Writers with associated user data |
| `POST /writers/{writerId}/works/{workId}` | admin | Assign writer to work |
| `DELETE /writers/{writerId}/works/{workId}` | admin | Remove writer from work |

### Musical Works (`/works`)

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /works` | user | List works |
| `POST /works` | admin | Create work |
| `GET /works/{id}` | user | Get work |
| `PUT /works/{id}` | admin | Update work |
| `DELETE /works/{id}` | admin | Delete work |

**Work-Recording Links:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /works/{workId}/recordings/{assetId}` | admin | Link work to recording |
| `GET /works/{workId}/recordings` | user | Get work's recordings |
| `POST /works/{workId}/recordings/{assetId}/primary` | admin | Set primary recording |
| `GET /works/{workId}/recordings/primary` | user | Get primary recording |
| `GET /works/recordings/{assetId}/works` | user | Get recording's works |

**Work Registrations:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /works/registrations` | admin | Create/update registration |
| `GET /works/registrations` | user | List all registrations |
| `GET /works/{workId}/registrations` | user | Get work's registrations |
| `GET /works/registrations/summary` | user | Registration summary |
| `GET /works/registrations/{id}` | user | Get registration details |
| `DELETE /works/registrations/{id}` | admin | Delete registration |
| `PATCH /works/registrations/{id}/status` | admin | Update registration status |

**Work-Writer Relationships:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /works/work-writers` | user | List work-writer relationships |

### CWR Export (`/cwr`)

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /cwr/export` | admin | Export CWR file |
| `GET /cwr/status/{publisherId}` | user | Get export status |
| `POST /cwr/cancel` | admin | Cancel export |
| `POST /cwr/acknowledgment` | admin | Process acknowledgment file (upload) |
| `GET /cwr/works/registration-status` | user | Get work registration statuses |
| `GET /cwr/works/{workId}/registration` | user | Get work's CWR registration |
| `PATCH /cwr/registrations/{registrationId}/status` | admin | Update registration status |

---

## 14. Currency Management (`/currencies`)

**Public Routes:**

| Endpoint | Description |
|----------|-------------|
| `GET /currencies` | List all supported currencies |
| `GET /currencies/{code}` | Get currency details |

**Tenant Routes:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /currencies/tenant` | user | Get tenant's enabled currencies |
| `GET /currencies/tenant/default` | user | Get tenant's default currency |
| `POST /currencies/tenant/{code}/enable` | admin | Enable currency |
| `POST /currencies/tenant/{code}/disable` | admin | Disable currency |
| `POST /currencies/tenant/{code}/default` | owner | Set default currency |

---

## 15. Billing & Subscriptions (`/billing`)

Subscription management with Stripe integration.

**Current State:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /billing/active` | user | Get active subscription |
| `GET /billing/sync-status` | user | Subscription sync status |
| `GET /billing/usage` | user | Usage summary |

**Plans:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /billing/plans` | user | List available plans |
| `GET /billing/plans/stripe` | user | Get Stripe plans |

**Subscription Management:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `GET /billing/subscriptions` | admin | List subscriptions |
| `GET /billing/subscriptions/{id}` | admin | Get subscription |
| `POST /billing/subscriptions` | owner | Create subscription |
| `POST /billing/subscriptions/{id}/cancel` | owner | Cancel subscription |

**Plan Changes:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /billing/upgrade/checkout-session` | owner | Create Stripe checkout for upgrade |
| `POST /billing/upgrade/{lookupKey}` | owner | Direct plan upgrade |
| `POST /billing/downgrade` | owner | Downgrade plan |

**Custom Invoices:**

| Endpoint | Role | Description |
|----------|------|-------------|
| `POST /billing/invoices/custom` | owner | Create custom invoice |
| `PUT /billing/invoices/custom/{id}` | owner | Update custom invoice |
| `DELETE /billing/invoices/custom/{id}` | owner | Delete custom invoice |
| `POST /billing/invoices/custom/{id}/mark-paid` | owner | Mark invoice as paid |
| `GET /billing/invoices` | admin | List invoices |

*

…(truncated)
