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

# Import Missing Products from Royalty Data

> **/checklist/royaltyproducts/import**

<Note>
  This endpoint requires authentication. Include your Bearer token in the Authorization header.
</Note>

## Description

**/checklist/royaltyproducts/import**

Scans BigQuery royalty data for UPCs that don't exist in the catalog and stages them
in the CatalogImportItem table for admin review. Products use BigQuery metadata only
(no Spotify enrichment in v1).

**Authorization:**

* Required role: `admin` or higher

**Method:** `POST`

## Code Examples

<CodeGroup>
  ```javascript Node.js theme={null}
  const response = await fetch('https://api.royalti.io/checklist/royaltyproducts/import', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({})
  });

  const data = await response.json();
  console.log(data);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
    'https://api.royalti.io/checklist/royaltyproducts/import',
    headers={
      'Authorization': f'Bearer {token}'
    },
    json={}
  )

  data = response.json()
  print(data)
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.royalti.io/checklist/royaltyproducts/import \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{}'

  ```
</CodeGroup>


## OpenAPI

````yaml post /checklist/royaltyproducts/import
openapi: 3.0.0
info:
  title: Royalti.io API
  description: "# Royalti API\r\n\r\nThis is the Royalti music royalty management platform API server.\r\n\r\n## Overview\r\n\r\nThe Royalti API provides comprehensive music royalty management services including:\r\n- Music publishing and writer management\r\n- Royalty processing and analytics\r\n- DDEX integration for music industry standards\r\n- File processing and pattern recognition\r\n- Payment processing and distribution\r\n\r\n## Authentication\r\n\r\nThe API uses JWT-based authentication with multiple protection levels:\r\n- Public endpoints for basic operations\r\n- Protected endpoints requiring valid JWT tokens\r\n- Admin endpoints for administrative functions\r\n\r\n## Features\r\n\r\n- Multi-dimensional royalty analytics\r\n- CWR (Collective Works Registration) support\r\n- DDEX integration for music metadata\r\n- Advanced file processing with pattern recognition\r\n- Real-time data processing with queue system"
  version: 2.6.0
  contact:
    name: Royalti.io Support
    email: support@royalti.io
    url: https://royalti.io
  license:
    name: Proprietary
    url: https://royalti.io/terms
servers:
  - url: https://api.royalti.io
    description: Production server
  - url: https://api-dev.royalti.io
    description: Development server
  - url: http://localhost:8084
    description: Local development
security:
  - bearerAuth: []
tags:
  - name: Accounting
    description: Accounting and financial transaction operations
  - name: Addons
    description: |
      List, activate, configure, and toggle tenant addons. All routes
      require a valid tenant session (`bearerAuth`) and the `addonsAccess`
      plan feature (returns `403 FEATURE_REQUIRED` on `FREE`-plan tenants,
      regardless of role). Response envelope is `{ success, data }` (note:
      `success`, not the `status` field used by most other Royalti.io
      routers).
  - name: Admin Impersonation
    description: Admin user-impersonation endpoints
  - name: Billing
    description: |
      Subscription lifecycle (local + Stripe), plans, custom/Stripe invoices,
      usage summaries, the Stripe Customer Portal and Customer Session,
      payment links, and cache/maintenance utilities. All routes require a
      valid tenant session (`bearerAuth`); most additionally require
      `admin` or `owner` role via RBAC — see each endpoint's Authorization
      note. Response envelope is `{ status, message, data }` except where
      noted.
  - name: Catalog Export
    description: >
      Generate catalog exports (BackBeat CWR-workstation import, Too
      Lost/Sonosuite

      distributor formats, and catalog CSV/XLSX exports for assets, products,

      artists, labels, splits, and releases) from a code-defined template

      registry. Admin-only — distinct from the member-accessible generic

      `?export=csv` list-endpoint query param, which remains untouched.
  - name: Catalog Import
    description: |
      Tenant-scoped catalog importer built on the metadata engine: search an
      artist on a configured provider, fetch and merge cross-provider metadata
      into staged `CatalogImportItem` rows, review a read-only diff against
      the tenant's existing catalog, optionally adjust individual staged
      rows, then explicitly apply or reject the batch. Admin-only — every
      route requires the tenant `admin` role or higher.

      **Flow:** `GET /providers` (optional, to see what's active) →
      `GET /search` (resolve an artist to an external id) →
      `POST /fetch` (queues a background job) →
      `GET /job/{jobId}` (poll until complete) →
      `GET /batch/{batchId}` and `GET /batch/{batchId}/diff` (review) →
      `PUT /items/{id}` (optional per-row overrides) →
      `POST /apply` or `POST /reject` (explicit confirm).

      **Review-then-apply, always:** fetch only ever stages
      `CatalogImportItem` rows — it never writes to `Asset`/`Product`. The
      only tenant-catalog write path is `POST /apply`, which is always an
      explicit, reviewer-initiated call.

      **Idempotency:** re-running the same import is safe. Staged rows are
      deduplicated against already-pending/imported rows by ISRC/UPC before
      insert, and `POST /apply` only ever touches `status: pending` rows
      scoped to the batch/items and tenant supplied — re-applying an
      already-applied batch or a batch with no matching pending rows is a
      no-op (`imported: 0, failed: 0`).
  - name: Currencies
    description: |
      Global currency reference data, per-tenant currency enablement, and
      exchange rates used to convert royalty data between currencies for
      reporting. All routes are mounted behind the platform's standard
      `basicAuth` + RBAC stack, so every endpoint requires a valid bearer
      token even where no `require*` role gate is applied in code. Response
      envelope is `{ success, data }` for currency endpoints and
      `{ status, data }` for exchange-rate endpoints (the two controllers
      predate a shared convention).
  - name: CWR Export
    description: |
      CISAC Common Works Registration (CWR) file generation for a tenant's
      publisher. Builds an async export job against the tenant's works and
      writers, persists the rendered file to Cloud Storage, and exposes job
      status + a download endpoint for the resulting file. All endpoints
      require the `publisher` addon. Mounted at `/cwr`.
  - name: CWR Acknowledgment
    description: |
      Upload and parse CWR acknowledgment (ACK) files returned by a
      performing rights society after a registration submission, and
      inspect/update per-work registration status derived from them.
  - name: DDEX
    description: DDEX operations (ERN/MEAD, messages, delivery, providers)
  - name: DMP Import
    description: |
      Imports a tenant's catalog (writers, works, work-writer relationships,
      artists, recordings, cross-references, and acknowledgements) from a
      django-music-publisher (DMP) works JSON export, with an optional
      acknowledgements CSV export layered on top. Mounted at `/dmp`.
      Import is best-effort and per-record: a failed row is captured in
      that phase's `errors[]` array rather than aborting the whole run, so
      a "completed" response can still contain 0 created rows if every
      record errored — check `result.*.errors` before treating a response
      as fully successful.
  - name: Downloads
    description: >-
      Generate and manage data downloads (royalty reports, accounting exports,
      etc.)
  - name: Label
    description: Label management operations
  - name: PRO Credentials
    description: |
      CRUD for `TenantPROCredentials` — one credential set per (tenant,
      society) pair, used by the CWR submission pipeline to deliver
      registration files to a PRO over SFTP, API, or EMAIL. Mounted at
      `/pro-credentials`. All routes require `admin` and an active
      `publisher` addon (enforced router-wide via `router.use()`, not
      per-route).

      **Credentials are never returned by the API.** The model's `toJSON()`
      strips `credentialsEncrypted` (ciphertext) and `decryptedCredentials`
      (plaintext, populated only in-process for the connection-test
      endpoint) from every serialized response — list/get/create/update
      responses include the account metadata (`societyCode`, `accountId`,
      `submissionMethod`, `endpointConfig`, `isActive`, `lastUsedAt`) but
      never the credential payload itself, masked or otherwise. Write
      requests still accept plaintext `credentials` in the body (over TLS);
      they are encrypted at rest (pgcrypto-backed) before the row is
      persisted.
  - name: Publisher-Writer Agreements
    description: |
      Manage `PublisherWriterAgreement` records — the contractual
      relationship between a `Publisher` and a `CWRWriter`, including its
      territory grants, status lifecycle (draft → pending → active →
      expired/terminated), and validity dates. Territory conflicts are
      checked against a writer/publisher pair's OTHER active agreements
      on every create/update/add-territory call.

      All endpoints require an active `publisher` addon on the tenant
      (`checkAddon('publisher')`). Unlike most routers in this API, this
      controller does NOT use the shared `asyncWrapper`/centralized error
      handler — every method catches its own errors and writes the JSON
      response directly, so error bodies here are `{ message, error? }`,
      not the `{ status, message }` shape used elsewhere. Role
      requirements come entirely from the global RBAC route table
      (`cwr.permissions.ts`): reads need `user` or higher, writes need
      `admin` or higher — there is no route-level `requireUser`/
      `requireAdmin` call in this router's own file.
  - name: Publishers
    description: |
      Core Publisher CRUD (name, IPI numbers, publisher type, settings) plus the
      `/publishers/me` singleton lookup and the `/publishers-with-user-data`
      convenience list. `POST`/`PUT`/`DELETE` require `admin`; reads require
      only `user`.
  - name: Publisher Territories
    description: |
      Per-publisher territory rows (`PublisherTerritory`) carrying PR/MR/SR
      share splits, inclusion/exclusion, and CWR 2.2+ formula/assignment/license
      fields. Distinct from the `territories` JSONB column on `Publisher` itself
      — these endpoints manage the normalized `PublisherTerritories` table.
  - name: Publisher Agreements
    description: |
      Per-publisher agreement rows (`PublisherAgreement`) — original,
      sub-publishing, administration, or collection agreements with territory
      and right-type terms.
  - name: Publisher CWR Export
    description: |
      Generates a CWR registration file (text/plain) for a single publisher's
      exportable works. One publisher per call — a CWR file carries a single
      sender/publisher chain, so batch `publisherIds` requests are rejected.
  - name: Publisher Statements
    description: |
      Publisher royalty statement generation, listing, detail, status
      transitions, and PDF export (`PublisherStatement`). Statement status
      follows a locked state machine: `draft → pending → approved → paid`
      (with `pending → draft` and `approved → pending` as the only backward
      transitions).
  - name: Statement Reconciliation
    description: |
      Phase 6 reconciliation surface built on top of Publisher Statements:
      PRO-file ingestion (BMI/ASCAP/MLC/MANUAL), line-level work matching
      (manual + fuzzy auto-match), variance detection against expected
      royalties, variance-flag comment threads, manual statement adjustments
      (H-05, locked once a statement is `approved`/`paid`), and emailing the
      rendered statement PDF to recipients.
  - name: Sub-Publishing
    description: |
      Sub-publishing agreements between an original publisher and a
      sub-publisher (`SubPublishingAgreement`), territory-conflict checking,
      and agreement discussion threads (H-07, backed by the polymorphic
      `DisputeMessage` model with `subjectType: sub_publishing_agreement`).
  - name: Recoupment Ledger
    description: |
      Read-only running-balance ledger (`RecoupmentLedger`) for a
      sub-publishing agreement's advance/recoupment activity.
  - name: Publishing Analytics
    description: >
      Tenant-scoped aggregation endpoints over `PublisherStatementLine` and

      `RecoupmentLedger` (WP-09, locks the G-ANALYTICS response-shape

      contract). Every aggregation returns the same uniform envelope:

      `{ dimension, rows: [{ key, label, ...metrics }], totals, currency, meta?
      }`.

      All support optional `periodStart`/`periodEnd` (ISO date, inclusive)

      filtering — see each endpoint for period semantics (statement-period

      overlap vs. ledger event-date).
  - name: Publishing Bootstrap
    description: |
      Scan → review → apply pipeline that bootstraps skeleton CWR works from
      the recording catalog. Scans stage proposals only; the diff is
      read-only and recomputed live; POST /apply is the single CWR write
      path. Ownership data (shares/publishers/agreements) is never seeded.
  - name: Publishing Claims
    description: |
      Tenant-wide inbox for `WriterClaim` disputes over CWR work ownership,
      share splits, registration conflicts, or metadata. Mounted at
      `/publishing`. Per-work claim endpoints (file a claim, list a work's
      claims) live on the `/work/works/{workId}/claims` routes instead, to
      keep work-scoped CRUD together — this file documents only the
      tenant-wide inbox surface (`/publishing/claims*`).

      All routes require an active `publisher` addon. Resolve/transition
      and bulk-resolve require `admin`; list/get/message/evidence routes
      accept any authenticated tenant user — finer-grained authorization
      (claimant-only actions such as withdraw) is enforced inside the
      service layer, not by RBAC middleware.
  - name: Source Creator
    description: >
      AI-assisted wizard for onboarding a new royalty data source: file

      analysis, AI column-mapping suggestions, BigQuery SQL generation,

      sandbox testing, and draft-source persistence. All routes require

      the `royaltyAccess` subscription feature
      (`requireFeature('royaltyAccess')`)

      — tenants below the required plan get a `403` with upgrade-plan

      details instead of a generic forbidden error.
  - name: Internal Webhooks
    description: Internal system webhooks for royalty processing and downloads
  - name: Payment Webhooks
    description: Payment processor webhook endpoints
  - name: Billing Webhooks
    description: Stripe billing and subscription webhooks
  - name: Infrastructure Webhooks
    description: Cloudflare domain and SSL webhooks
  - name: Distribution Webhooks
    description: Digital distribution platform webhooks (FUGA)
  - name: Branding
    description: |
      Per-tenant brand tokens (primary/accent colours, font, logo, logo mark)
      used to theme the tenant's workspace UI and emails. Both reading and
      updating brand tokens require an admin role.
  - name: Custom Domains
    description: |
      Custom workspace domains (e.g. `app.yourlabel.com`) that resolve to a
      tenant's Royalti workspace. Domains are provisioned with automatic SSL
      and validated via a DNS TXT record or an HTTP file challenge. Adding a
      domain requires an admin role; removing it requires the tenant owner.
      A tenant may have at most one active custom domain at a time.
  - name: Email Domains
    description: |
      The tenant's custom email sending domain, used so outbound
      transactional and royalty emails are sent from the tenant's own
      domain instead of the shared `royalti.io` sender. Domain ownership is
      proven with DNS records (DKIM, SPF, and a return-path record) that
      must be added before the domain is verified. Viewing the configuration
      requires an admin role; registering, deleting, or triggering
      verification requires the tenant owner.
  - name: Email Settings
    description: |
      Combined view of the tenant's email sending configuration: the custom
      email domain status plus sender/branding fields (from-name, reply-to,
      logo, colours, footer). Some branding fields only take effect once
      the tenant has a verified custom email domain. Any tenant member can
      view settings; updating them requires an admin role or higher.
  - name: Email Templates
    description: |
      Read-only catalogue of the transactional email templates Royalti can
      send (welcome, password reset, invite, payment notifications, etc.),
      with tenant-branded HTML/text preview rendering and a test-send
      endpoint for confirming how a template looks with the tenant's
      current branding. All endpoints in this cluster require an admin role.
  - name: Works & CWR Registrations
    description: |
      Musical works (`CWRWork`) are the composition-level entities behind
      publishing: title, ISWC, writer splits, and their links to society
      registrations, recordings (masters), and ownership claims. All routes
      in this group are mounted at `/work` and require an active `publisher`
      addon on the tenant (`checkAddon('publisher')`), plus a valid session
      (`requireUser` for reads, `requireAdmin` for writes unless noted
      otherwise). Role hierarchy: `guest < user < admin < owner`; `requireAdmin`
      allows `admin` and above.

      Registrations (`WorkRegistration`) track a work's submission to a
      performing-rights society (PRO) — ASCAP, BMI, PRS, etc. — including the
      NWR (New Work Registration) CWR transaction pipeline shipped 2026-07-05
      (PR #485): a registration is created in `pending` status, explicitly
      queued for PRO submission (`queuedAt` set), picked up by the
      `proSubmissionService` worker, and may land in `submitted`,
      `registered`, `rejected`, or `conflict`. Only `pending` rows can be
      (re)queued — a row that has left `pending` is never re-armed.
      Registration conflicts represent a competing claim from a society or
      third party against a specific registration and are tracked
      separately from work-scoped ownership claims (`WriterClaim`).
  - name: Writers
    description: |
      Manage CWR (Common Works Registration) writers — the composers,
      authors, arrangers, and translators credited on a tenant's musical
      works — and their associations with individual works. Writers are
      distinct from `TenantUser` accounts; a writer may optionally be
      linked to a `TenantUser` via `tenantUserId` (`userData` in
      responses) to pull in platform account details.

      All endpoints require an active `publisher` addon on the tenant
      (`checkAddon('publisher')`) in addition to the stated role. Reads
      require the `user` role or higher; writes (create/update/delete and
      work associations) require `admin` or higher, enforced both by the
      global RBAC route table (`cwr.permissions.ts`) and, for this router,
      duplicated at the route level via `requireUser`/`requireAdmin`.
paths:
  /checklist/royaltyproducts/import:
    post:
      tags:
        - Checklists - Enrichment
      summary: Import Missing Products from Royalty Data
      description: >-
        **/checklist/royaltyproducts/import**


        Scans BigQuery royalty data for UPCs that don't exist in the catalog and
        stages them

        in the CatalogImportItem table for admin review. Products use BigQuery
        metadata only

        (no Spotify enrichment in v1).


        **Authorization:**

        - Required role: `admin` or higher


        **Method:** `POST`
      responses:
        '200':
          description: Import job queued successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: Product import job queued
                  data:
                    type: object
                    properties:
                      jobId:
                        type: string
                      batchId:
                        type: string
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  schemas:
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        JWT Authorization header using the Bearer scheme. Format: "Bearer
        {token}"

````