openapi: 3.1.0
info:
  title: MusicArk Core API
  version: 0.1.11-dev
  description: Independent local-first API for MusicArk. Cookie sessions and scoped Bearer tokens cover administrator-managed local users, isolated favorites, playlists, queues, playback history and progress, the shared catalog, incremental/full indexing, event-driven root monitoring with configurable scheduled-scan fallback, batch metadata editing, restricted folder management, local metadata re-scraping, direct playback, cached progressive transcoding, and authenticated HLS VOD. Third-party clients may exchange a MusicArk username and password for a non-expiring player token; repeated login by the same account and device rotates that device's existing token instead of accumulating active tokens. Administrator connections receive library:write in addition to the four player scopes, while member connections remain limited to player scopes. Passwords are never used as Bearer credentials or sent with media requests. User management is administrator CookieSession-only. Operations marked with library:write require an administrator both for CookieSession calls and for the owner of a Bearer token. Folder mutations require an explicit source-change confirmation and empty-folder deletion never removes media files. Container-side filesystem events remain best-effort on host bind mounts; scan_interval_minutes is the durable polling fallback when native host events are delayed or lost.
servers:
  - url: /api/core/v1
security:
  - CookieSession: []
x-musicark-cors:
  pathPattern: /api/core/v1/{path...}
  allowOrigin: '*'
  allowCredentials: false
  allowMethods: [GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS]
  allowHeaders: [Authorization, Content-Type, Range, If-None-Match, If-Match, Idempotency-Key]
  description: Every versioned API path accepts an unauthenticated OPTIONS preflight. Actual requests still require the operation's declared CookieSession or Bearer authentication.
tags:
  - { name: Health, description: Process health and runtime capability discovery. }
  - { name: Identity, description: Instance bootstrap, web sessions, users, profiles, and personal API tokens. }
  - { name: Administration, description: Administrator-only backups, recycle bin, system status, and audit records. }
  - { name: Storage, description: Managed music folders, restricted browsing, and folder maintenance. }
  - { name: Operations, description: Scans, duplicate analysis, background operations, issues, and progress events. }
  - { name: Catalog, description: Shared tracks, artists, releases, facets, search, and metadata maintenance. }
  - { name: Enrichment, description: Built-in online sources, candidate review, and atomic enrichment change plans. }
  - { name: Collections, description: User favorites, playlists, queues, history, recommendations, and play-later state. }
  - { name: Playback, description: Transcode profiles, listening sessions, streams, HLS, and device progress. }
  - { name: Media, description: Authenticated images and lyrics resources. }
paths:
  /livez:
    get:
      servers: [{ url: / }]
      operationId: livez
      summary: Check process liveness
      tags: [Health]
      description: "Returns data for Check process liveness. No authentication is required."
      security: []
      responses:
        '200': { description: Process is live, content: { application/json: { schema: { $ref: '#/components/schemas/Health' } } } }
  /readyz:
    get:
      servers: [{ url: / }]
      operationId: readyz
      summary: Check service readiness
      tags: [Health]
      description: "Returns data for Check service readiness. No authentication is required."
      security: []
      responses:
        '200': { description: Database and data directory are ready, content: { application/json: { schema: { $ref: '#/components/schemas/Health' } } } }
        '503': { $ref: '#/components/responses/Problem' }
  /openapi.yaml:
    get:
      servers: [{ url: / }]
      operationId: openAPIContract
      summary: Download OpenAPI Contract
      tags: [Health]
      description: Returns the exact OpenAPI 3.1 contract shipped with the running MusicArk build.
      security: []
      responses:
        '200': { description: OpenAPI 3.1 YAML document, content: { application/yaml: { schema: { type: string } } } }
        '503': { $ref: '#/components/responses/Problem' }
  /api-docs:
    get:
      servers: [{ url: / }]
      operationId: apiDocumentation
      summary: Open API Documentation
      tags: [Health]
      description: Returns an offline documentation landing page with links to this instance's contract and runtime capabilities.
      security: []
      responses:
        '200': { description: API documentation landing page, content: { text/html: { schema: { type: string } } } }
  /bootstrap/status:
    get:
      operationId: bootstrapStatus
      summary: Bootstrap Status
      tags: [Identity]
      description: "Returns data for Bootstrap Status. No authentication is required."
      security: []
      responses:
        '200': { description: Instance status, content: { application/json: { schema: { $ref: '#/components/schemas/BootstrapStatus' } } } }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /bootstrap:
    post:
      operationId: bootstrap
      summary: Bootstrap
      tags: [Identity]
      description: "Executes Bootstrap and returns the documented result. No authentication is required."
      security: []
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/BootstrapRequest' } } } }
      responses:
        '201': { description: Administrator and session created, content: { application/json: { schema: { $ref: '#/components/schemas/Session' } } } }
        '409': { $ref: '#/components/responses/Problem' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /session:
    get:
      operationId: currentSession
      summary: Current Session
      tags: [Identity]
      description: "Returns data for Current Session. A valid CookieSession is required."
      responses:
        '200': { description: Current session, content: { application/json: { schema: { $ref: '#/components/schemas/Session' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    post:
      operationId: login
      summary: Login
      tags: [Identity]
      description: "Executes Login and returns the documented result. No authentication is required."
      security: []
      requestBody: { required: true, content: { application/json: { schema: { type: object, required: [username,password], additionalProperties: false, properties: { username: { type: string }, password: { type: string, format: password } } }, example: { username: alice, password: correct-horse-battery-staple } } } }
      responses:
        '201': { description: Session created, content: { application/json: { schema: { $ref: '#/components/schemas/Session' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    delete:
      operationId: logout
      summary: Logout
      tags: [Identity]
      description: "Deletes or clears the resource targeted by Logout. A valid CookieSession is required."
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Logged out }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /users:
    get:
      operationId: users
      summary: Users
      tags: [Identity]
      description: Lists local users. Administrator CookieSession only; Bearer tokens are never accepted.
      parameters: [{ name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 1000, default: 100 } }, { $ref: '#/components/parameters/After' }]
      responses:
        '200': { description: User page, content: { application/json: { schema: { $ref: '#/components/schemas/UserPage' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    post:
      operationId: createUser
      summary: Create User
      tags: [Identity]
      description: Creates an administrator or member with isolated favorites, playlists, queues, progress, history, tokens and sessions.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateUserRequest' } } } }
      responses:
        '201': { description: User created, content: { application/json: { schema: { $ref: '#/components/schemas/User' } } } }
        '403': { $ref: '#/components/responses/Problem' }
        '409': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /users/{user_id}:
    parameters: [{ name: user_id, in: path, required: true, schema: { type: string } }]
    patch:
      operationId: updateUser
      summary: Update User
      tags: [Identity]
      description: Updates a role, enables/disables an account, or resets its password. Usernames are immutable after creation. Disabling and password resets revoke that user's sessions and API tokens. The current administrator and last active administrator are protected.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateUserRequest' } } } }
      responses:
        '200': { description: User updated, content: { application/json: { schema: { $ref: '#/components/schemas/User' } } } }
        '403': { $ref: '#/components/responses/Problem' }
        '404': { $ref: '#/components/responses/Problem' }
        '409': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    delete:
      operationId: deleteUser
      summary: Delete User
      tags: [Identity]
      description: Permanently deletes a non-current user and cascades their favorites, playlists, queue, progress, history, listening sessions, API tokens and web sessions. Shared catalog data and source files are not deleted.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteUserRequest' } } } }
      responses:
        '204': { description: User and private data deleted }
        '403': { $ref: '#/components/responses/Problem' }
        '404': { $ref: '#/components/responses/Problem' }
        '409': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /me/profile:
    patch:
      operationId: updateOwnProfile
      summary: Update Own Profile
      tags: [Identity]
      description: Updates the current user's password. Usernames are immutable after creation. Password changes require the current password, preserve the current web session, and revoke every other web session and API token owned by the user.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateOwnProfileRequest' } } } }
      responses:
        '200': { description: Profile updated, content: { application/json: { schema: { $ref: '#/components/schemas/UserProfile' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /me/avatar:
    put:
      operationId: putOwnAvatar
      summary: Put Own Avatar
      tags: [Identity]
      description: Replaces the current user's avatar with a validated JPEG, PNG, GIF, or WebP image up to 20 MB.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody:
        required: true
        content:
          image/jpeg: { schema: { type: string, format: binary } }
          image/png: { schema: { type: string, format: binary } }
          image/gif: { schema: { type: string, format: binary } }
          image/webp: { schema: { type: string, format: binary } }
      responses:
        '200': { description: Avatar updated, content: { application/json: { schema: { $ref: '#/components/schemas/UserProfile' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '413': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
    delete:
      operationId: deleteOwnAvatar
      summary: Delete Own Avatar
      tags: [Identity]
      description: Removes the current user's custom avatar.
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses:
        '200': { description: Avatar removed, content: { application/json: { schema: { $ref: '#/components/schemas/UserProfile' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /runtime:
    get:
      operationId: runtime
      summary: Runtime
      tags: [Health]
      description: "Returns data for Runtime. No authentication is required."
      security: []
      responses: { '200': { description: Runtime capabilities, content: { application/json: { schema: { $ref: '#/components/schemas/Runtime' } } } }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /system/status:
    get:
      operationId: systemStatus
      summary: System Status
      tags: [Administration]
      description: Returns administrator-only runtime readiness, library counts, compatibility capability booleans, structured five-state capability details, uptime, and database size. Reading this resource never initiates external enrichment-source probes.
      responses: { '200': { description: System status, content: { application/json: { schema: { $ref: '#/components/schemas/SystemStatus' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /system/status/capabilities/{capability}/probe:
    parameters:
      - { name: capability, in: path, required: true, schema: { type: string, enum: [data_storage,local_scan,direct_playback,ffprobe,ffmpeg,transcoding,transcode_cache,hls,chromaprint,online_enrichment,realtime_monitoring,scheduled_scans] } }
    post:
      operationId: probeSystemCapability
      summary: Probe System Capability
      tags: [Administration]
      description: Explicitly checks one system capability and returns its current five-state result. Probing online enrichment checks all enabled upstream sources; ordinary system-status reads remain offline-only.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '200': { description: Capability check completed, content: { application/json: { schema: { $ref: '#/components/schemas/SystemCapabilityProbeResult' } } } }, '403': { $ref: '#/components/responses/Problem' }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /file-mutation-audits:
    get:
      operationId: fileMutationAudits
      summary: File Mutation Audits
      tags: [Administration]
      description: Returns administrator-only, stable paginated source-file mutation records.
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }, { name: status, in: query, schema: { type: string, enum: [pending,completed,failed,reconcile_required] } }, { name: operation, in: query, schema: { type: string } }]
      responses: { '200': { description: File mutation audit page, content: { application/json: { schema: { $ref: '#/components/schemas/FileMutationAuditPage' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /admin-audit-events:
    get:
      operationId: adminAuditEvents
      summary: Admin Audit Events
      tags: [Administration]
      description: Returns immutable administrator and security mutation events with stable actor and target snapshots.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }, { name: category, in: query, schema: { type: string } }, { name: action, in: query, schema: { type: string } }, { name: status, in: query, schema: { type: string, enum: [completed,failed] } }, { name: actor, in: query, schema: { type: string } }]
      responses: { '200': { description: Administrator audit page, content: { application/json: { schema: { $ref: '#/components/schemas/AdminAuditEventPage' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /recycle-bin:
    get:
      operationId: recycleBin
      summary: Recycle Bin
      tags: [Administration]
      description: Lists administrator-only source files moved into MusicArk's per-root recycle area.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Active recycle-bin entries, content: { application/json: { schema: { $ref: '#/components/schemas/RecycleBinPage' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /recycle-bin/{recycle_id}/restore:
    parameters: [{ name: recycle_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: restoreRecycledMedia
      summary: Restore Recycled Media
      tags: [Administration]
      description: "Executes Restore Recycled Media and returns the documented result. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Source file restored to its original path and catalog entries reactivated, content: { application/json: { schema: { $ref: '#/components/schemas/RestoreRecycledMediaResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /recycle-bin/{recycle_id}:
    parameters: [{ name: recycle_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    delete:
      operationId: purgeRecycledMedia
      summary: Purge Recycled Media
      tags: [Administration]
      description: "Deletes or clears the resource targeted by Purge Recycled Media. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PurgeRecycledMediaRequest' } } } }
      responses: { '200': { description: Recycled source permanently removed and its catalog records deleted, content: { application/json: { schema: { $ref: '#/components/schemas/PurgeRecycledMediaResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /backups:
    get:
      operationId: backups
      summary: Backups
      tags: [Administration]
      description: Lists administrator-only database and artwork backup archives and any pending startup restore.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Backup archives, content: { application/json: { schema: { $ref: '#/components/schemas/BackupList' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: createBackup
      summary: Create Backup
      tags: [Administration]
      description: "Executes Create Backup and returns the documented result. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '201': { description: Consistent SQLite snapshot and artwork archive created, content: { application/json: { schema: { $ref: '#/components/schemas/Backup' } } } }, '403': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' } }
  /backups/{backup_id}/download:
    parameters: [{ name: backup_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: downloadBackup
      summary: Download Backup
      tags: [Administration]
      description: "Returns data for Download Backup. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Backup ZIP archive, content: { application/zip: { schema: { type: string, format: binary } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /backups/{backup_id}:
    parameters: [{ name: backup_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    delete:
      operationId: deleteBackup
      summary: Delete Backup
      tags: [Administration]
      description: "Deletes or clears the resource targeted by Delete Backup. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteBackupRequest' } } } }
      responses: { '204': { description: Backup archive deleted }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /backups/restore:
    post:
      operationId: stageBackupRestore
      summary: Stage Backup Restore
      tags: [Administration]
      description: Validates and stages an uploaded backup. The current database remains active until the next application restart; a safety backup is created automatically.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/zip: { schema: { type: string, format: binary } } } }
      responses: { '202': { description: Restore staged for the next startup, content: { application/json: { schema: { $ref: '#/components/schemas/StageBackupRestoreResponse' } } } }, '409': { $ref: '#/components/responses/Problem' }, '413': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' } }
  /backups/restore/restart:
    post:
      operationId: restartForBackupRestore
      summary: Restart To Apply Backup Restore
      tags: [Administration]
      description: Gracefully restarts MusicArk in place and applies the already staged backup during startup.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RestartBackupRestoreRequest' } } } }
      responses: { '202': { description: Graceful restart accepted, content: { application/json: { schema: { $ref: '#/components/schemas/RestartBackupRestoreResponse' } } } }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' } }
  /storage-roots:
    get:
      operationId: listStorageRoots
      summary: List Storage Roots
      tags: [Storage]
      description: "Returns data for List Storage Roots. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Storage roots, content: { application/json: { schema: { type: object, required: [items], properties: { items: { type: array, items: { $ref: '#/components/schemas/StorageRoot' } } } } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: createStorageRoot
      summary: Create Storage Root
      tags: [Storage]
      description: "Executes Create Storage Root and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateRootRequest' } } } }
      responses: { '201': { description: Root created, content: { application/json: { schema: { $ref: '#/components/schemas/StorageRoot' } } } }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /storage-roots/candidates:
    get:
      operationId: listStorageRootCandidates
      summary: List Storage Root Candidates
      tags: [Storage]
      description: Lists container mount targets under the configured media bases without exposing host source paths. CookieSession administrators only.
      security: [{ CookieSession: [] }]
      responses:
        '200': { description: Mapped media-directory candidates, content: { application/json: { schema: { type: object, required: [items], additionalProperties: false, properties: { items: { type: array, items: { $ref: '#/components/schemas/StorageRootCandidate' } } } } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '503': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /storage-roots/candidates/browse:
    get:
      operationId: browseStorageRootCandidate
      summary: Browse Storage Root Candidate
      tags: [Storage]
      description: Browses real child directories inside a discovered container mount without exposing host source paths. CookieSession administrators only.
      security: [{ CookieSession: [] }]
      parameters:
        - { name: root, in: query, required: true, schema: { type: string }, description: Exact container target returned by listStorageRootCandidates. }
        - { name: path, in: query, schema: { type: string }, description: Relative child path inside the selected mapped root. }
      responses:
        '200': { description: Candidate directory listing, content: { application/json: { schema: { $ref: '#/components/schemas/StorageRootCandidateBrowse' } } } }
        '401': { $ref: '#/components/responses/Problem' }
        '403': { $ref: '#/components/responses/Problem' }
        '422': { $ref: '#/components/responses/Problem' }
        '503': { $ref: '#/components/responses/Problem' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /storage-roots/{root_id}:
    parameters: [{ name: root_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: getStorageRoot
      summary: Get Storage Root
      tags: [Storage]
      description: "Returns data for Get Storage Root. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Storage root, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/StorageRoot' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateStorageRoot
      summary: Update Storage Root
      tags: [Storage]
      description: Updates root settings. realtime_monitoring requests low-latency Linux-container filesystem watches; scan_interval_minutes independently schedules incremental scans and is the reliability fallback for bind mounts that lose host events. Enabling either automatic mode can queue an initial incremental run for a root that has never completed a scan.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateRootRequest' } } } }
      responses: { '200': { description: Storage root updated, content: { application/json: { schema: { $ref: '#/components/schemas/StorageRoot' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteStorageRoot
      summary: Delete Storage Root
      tags: [Storage]
      description: "Deletes or clears the resource targeted by Delete Storage Root. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteRootRequest' } } } }
      responses: { '204': { description: Unused storage-root registration removed without deleting source files }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /storage-roots/{root_id}/probe:
    parameters: [{ name: root_id, in: path, required: true, schema: { type: string } }]
    post:
      operationId: probeStorageRoot
      summary: Probe Storage Root
      tags: [Storage]
      description: "Executes Probe Storage Root and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '200': { description: Probe result, content: { application/json: { schema: { $ref: '#/components/schemas/ProbeRootResponse' } } } }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /storage-roots/{root_id}/browse:
    parameters:
      - { name: root_id, in: path, required: true, schema: { type: string } }
      - { name: path, in: query, schema: { type: string } }
      - { $ref: '#/components/parameters/Limit' }
      - { $ref: '#/components/parameters/After' }
      - { name: child_limit, in: query, schema: { type: integer, minimum: 1, maximum: 1000, default: 200 } }
      - { name: child_after, in: query, schema: { type: string } }
    get:
      operationId: browseStorageRoot
      summary: Browse Storage Root
      tags: [Storage]
      description: "Returns data for Browse Storage Root. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Restricted directory listing, content: { application/json: { schema: { $ref: '#/components/schemas/BrowseStorageRootResponse' } } } }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /storage-roots/{root_id}/folders:
    parameters: [{ name: root_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: createFolder
      summary: Create Folder
      tags: [Storage]
      description: "Executes Create Folder and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateFolderRequest' } } } }
      responses: { '201': { description: Source folder created, content: { application/json: { schema: { $ref: '#/components/schemas/FolderMutationResult' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: renameFolder
      summary: Rename Folder
      tags: [Storage]
      description: "Updates the resource state for Rename Folder. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RenameFolderRequest' } } } }
      responses: { '200': { description: Source folder and matching catalog paths renamed, content: { application/json: { schema: { $ref: '#/components/schemas/FolderMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteEmptyFolder
      summary: Delete Empty Folder
      tags: [Storage]
      description: "Deletes or clears the resource targeted by Delete Empty Folder. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteFolderRequest' } } } }
      responses: { '200': { description: Empty source folder deleted, content: { application/json: { schema: { $ref: '#/components/schemas/FolderMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /storage-roots/{root_id}/folders/move:
    parameters: [{ name: root_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: moveFolder
      summary: Move Folder
      tags: [Storage]
      description: "Executes Move Folder and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/MoveFolderRequest' } } } }
      responses: { '200': { description: Source folder and matching catalog paths moved, content: { application/json: { schema: { $ref: '#/components/schemas/FolderMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs:
    post:
      operationId: createIndexRun
      summary: Create Index Run
      tags: [Operations]
      description: Starts a root-wide index run. incremental skips unchanged local file contexts; full re-extracts every candidate; force is a compatibility alias with the current full re-extraction behavior. Every mode preserves user and adopted-enrichment layers.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateIndexRunRequest' } } } }
      responses: { '202': { description: Index run accepted, content: { application/json: { schema: { $ref: '#/components/schemas/IndexRunAccepted' } } } }, '409': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearIndexRuns
      summary: Clear Index Runs
      tags: [Operations]
      description: "Deletes or clears the resource targeted by Clear Index Runs. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Completed scan records and their issue details cleared }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: getIndexRun
      summary: Get Index Run
      tags: [Operations]
      description: "Returns data for Get Index Run. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Index run, content: { application/json: { schema: { $ref: '#/components/schemas/IndexRun' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/abort:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: abortIndexRun
      summary: Abort Index Run
      tags: [Operations]
      description: "Executes Abort Index Run and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '202': { description: Cancellation requested, content: { application/json: { schema: { type: object, required: [status], additionalProperties: false, properties: { status: { type: string, const: cancelling } } } } } }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/issues:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: indexIssues
      summary: Index Issues
      tags: [Operations]
      description: "Returns data for Index Issues. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Index issues, content: { application/json: { schema: { $ref: '#/components/schemas/IndexIssueList' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /missing-files/pending:
    get:
      operationId: pendingMissingFiles
      summary: Pending Missing Files
      tags: [Operations]
      description: Returns the newest administrator-only scan snapshot that still contains missing media records, so the cleanup prompt can be restored after a page reload, login, or service restart.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Pending missing-file summary, content: { application/json: { schema: { $ref: '#/components/schemas/PendingMissingFilesResponse' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/missing-files:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: indexRunMissingFiles
      summary: Index Run Missing Files
      tags: [Operations]
      description: Returns the administrator-only snapshot of files newly marked missing by this completed scan. Paths are relative to the registered media root.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Missing-file snapshot, content: { application/json: { schema: { $ref: '#/components/schemas/IndexRunMissingFilesResponse' } } } }, '403': { $ref: '#/components/responses/Problem' }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/missing-files/cleanup:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: cleanupIndexRunMissingFiles
      summary: Cleanup Index Run Missing Files
      tags: [Operations]
      description: Removes database records for snapshot files that are still missing after a filesystem recheck. The atomic cleanup removes track-scoped applied enrichment values and external IDs before deleting media records and cascading catalog/personal relationships. It never deletes source files.
      security: [{ CookieSession: [] }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/MissingFilesCleanupRequest' } } } }
      responses: { '200': { description: Missing-file cleanup result, content: { application/json: { schema: { $ref: '#/components/schemas/MissingFilesCleanupResponse' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /operations:
    get:
      operationId: operations
      summary: Operations
      tags: [Operations]
      description: "Returns data for Operations. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters:
        - name: type
          in: query
          required: false
          description: Optional exact operation type filter; the scan-history UI uses catalog_index so unrelated background jobs cannot displace scan records.
          schema: { type: string, enum: [catalog_index, duplicate_analysis, online_enrichment] }
        - { $ref: '#/components/parameters/Limit' }
        - { $ref: '#/components/parameters/After' }
      responses: { '200': { description: Operations, content: { application/json: { schema: { $ref: '#/components/schemas/OperationPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /operation-events:
    get:
      operationId: operationEvents
      summary: Operation Events
      tags: [Operations]
      description: "Streams operation.progress plus user-filtered home.changed, playback.progress, and media.unavailable notifications to signed-in web sessions. Successful shared-queue replacement emits home.changed with reason playback_queue_changed so the web client can re-read /me/playback-queue. Accepted device progress writes emit playback.progress with device_id so the web client can immediately re-read /me/playback-progress; periodic GET remains the reconnect fallback. This CookieSession-only stream is not a Bearer player protocol; GET resources remain authoritative after reconnect."
      responses:
        '200': { description: Server-sent operation.progress and user-filtered home.changed, playback.progress, and media.unavailable events, content: { text/event-stream: { schema: { type: string } } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /duplicate-analysis-runs:
    post:
      operationId: createDuplicateAnalysisRun
      summary: Create Duplicate Analysis Run
      tags: [Operations]
      description: Starts an administrator-only background run that incrementally computes exact file hashes and acoustic fingerprints. Analysis never deletes files.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateDuplicateAnalysisRunRequest' } } } }
      responses: { '202': { description: Duplicate analysis accepted, content: { application/json: { schema: { $ref: '#/components/schemas/DuplicateAnalysisAccepted' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearDuplicateAnalysisRuns
      summary: Clear Duplicate Analysis Runs
      tags: [Operations]
      description: Deletes finished duplicate-analysis operation history only. Queued or running analysis and computed duplicate signatures/results are preserved.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Finished duplicate-analysis history cleared }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /duplicate-analysis-runs/{operation_id}/abort:
    parameters: [{ name: operation_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: abortDuplicateAnalysisRun
      summary: Abort Duplicate Analysis Run
      tags: [Operations]
      description: "Executes Abort Duplicate Analysis Run and returns the documented result. A valid CookieSession is required."
      security: [{ CookieSession: [] }]
      responses: { '202': { description: Cancellation requested, content: { application/json: { schema: { type: object, required: [status], properties: { status: { type: string, const: cancelling } } } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/duplicate-groups:
    get:
      operationId: duplicateGroups
      summary: Duplicate Groups
      tags: [Operations]
      description: Returns administrator-only exact and acoustic duplicate groups without exposing raw hashes, fingerprints, or absolute source paths.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Current duplicate analysis results, content: { application/json: { schema: { $ref: '#/components/schemas/DuplicateGroupsResponse' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/duplicate-cleanups:
    post:
      operationId: cleanupDuplicateGroups
      summary: Cleanup Duplicate Groups
      tags: [Operations]
      description: Moves up to 500 selected duplicate source files into MusicArk's recycle area after revalidating groups, writable roots, managed paths, regular-file type, and expected file versions. At least one active file remains in every group.
      security: [{ CookieSession: [] }]
      parameters:
        - { $ref: '#/components/parameters/CSRF' }
        - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 1, maxLength: 200 } }
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DuplicateCleanupRequest' } } } }
      responses: { '200': { description: Cleanup result including per-file audit status, content: { application/json: { schema: { $ref: '#/components/schemas/DuplicateCleanupResponse' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks:
    get:
      operationId: tracks
      summary: Tracks
      tags: [Catalog]
      description: Returns the current user's visible catalog as a stable cursor-paginated track page. Filters are combined with AND except comma-separated missing fields, which use OR.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters:
        - { $ref: '#/components/parameters/Limit' }
        - { $ref: '#/components/parameters/After' }
        - { name: q, in: query, schema: { type: string } }
        - { name: sort, in: query, schema: { type: string, enum: [added_at,title,artist,album,year,duration,modified_at,format,size], default: added_at } }
        - { name: direction, in: query, schema: { type: string, enum: [asc,desc] } }
        - { name: root_id, in: query, schema: { type: string } }
        - { name: artist_id, in: query, schema: { type: string } }
        - { name: release_id, in: query, schema: { type: string } }
        - { name: format, in: query, description: Comma-separated source formats., schema: { type: string } }
        - { name: artist, in: query, schema: { type: string } }
        - { name: album, in: query, schema: { type: string } }
        - { name: genre, in: query, schema: { type: string } }
        - { name: style, in: query, description: Exact effective style value; style is independent from genre., schema: { type: string } }
        - { name: year, in: query, description: Valid music year from 1000 through the current UTC year plus one; out-of-range values return 422., schema: { type: integer, minimum: 1000 } }
        - { name: decade, in: query, description: Decade beginning at 1000 or later and not beyond the decade containing the current UTC year plus one; out-of-range values return 422., schema: { type: integer, minimum: 1000, multipleOf: 10 } }
        - { name: language, in: query, description: Exact effective track language value., schema: { type: string } }
        - { name: favorite, in: query, schema: { type: boolean } }
        - { name: playback_state, in: query, schema: { type: string, enum: [played,unplayed] } }
        - { name: missing, in: query, description: Comma-separated missing fields matched with OR., schema: { type: string, example: artwork,lyrics } }
      responses: { '200': { description: Track page, content: { application/json: { schema: { $ref: '#/components/schemas/TrackPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: batchUpdateTracks
      summary: Batch Update Tracks
      tags: [Catalog]
      description: "Updates the resource state for Batch Update Tracks. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/BatchUpdateTracksRequest' } } } }
      responses: { '200': { description: Tracks updated atomically, content: { application/json: { schema: { $ref: '#/components/schemas/BatchMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/home:
    get:
      operationId: catalogHome
      summary: Catalog Home
      tags: [Catalog]
      description: "Returns data for Catalog Home. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: User-specific home listening sections, content: { application/json: { schema: { $ref: '#/components/schemas/HomeSections' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/favorite-tracks:
    get:
      operationId: favoriteTracks
      summary: Favorite Tracks
      tags: [Collections]
      description: "Returns data for Favorite Tracks. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses:
        '200':
          description: Current user's favorite tracks
          content: { application/json: { schema: { $ref: '#/components/schemas/FavoriteTrackPage' } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /me/favorites:
    get:
      operationId: favorites
      summary: Favorites
      tags: [Collections]
      description: "Returns data for Favorites. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses:
        '200': { description: Current user's unified track, album and artist favorites with counts, content: { application/json: { schema: { $ref: '#/components/schemas/Favorites' } } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /me/favorite-tracks/{track_id}:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: addFavoriteTrack
      summary: Add Favorite Track
      tags: [Collections]
      description: "Creates or replaces the resource state for Add Favorite Track. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Track favorited }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteFavoriteTrack
      summary: Delete Favorite Track
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Favorite Track. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Favorite removed }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/favorite-albums:
    get:
      operationId: favoriteAlbums
      summary: Favorite Albums
      tags: [Collections]
      description: "Returns data for Favorite Albums. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Current user's favorite albums, content: { application/json: { schema: { $ref: '#/components/schemas/FavoriteReleasePage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/favorite-albums/{album_id}:
    parameters: [{ name: album_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: addFavoriteAlbum
      summary: Add Favorite Album
      tags: [Collections]
      description: "Creates or replaces the resource state for Add Favorite Album. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Album favorited }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteFavoriteAlbum
      summary: Delete Favorite Album
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Favorite Album. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Album favorite removed }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/favorite-artists:
    get:
      operationId: favoriteArtists
      summary: Favorite Artists
      tags: [Collections]
      description: "Returns data for Favorite Artists. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Current user's favorite artists, content: { application/json: { schema: { $ref: '#/components/schemas/FavoriteArtistPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/favorite-artists/{artist_id}:
    parameters: [{ name: artist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: addFavoriteArtist
      summary: Add Favorite Artist
      tags: [Collections]
      description: "Creates or replaces the resource state for Add Favorite Artist. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Artist favorited }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteFavoriteArtist
      summary: Delete Favorite Artist
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Favorite Artist. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Artist favorite removed }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists:
    get:
      operationId: playlists
      summary: Playlists
      tags: [Collections]
      description: Lists only the authenticated user's playlists; playlists and their ordering are isolated between accounts.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Current user's paginated playlists, content: { application/json: { schema: { $ref: '#/components/schemas/PlaylistPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: createPlaylist
      summary: Create Playlist
      tags: [Collections]
      description: Creates an empty playlist owned by the authenticated user. The name is required and unique conflicts are reported with a stable problem code.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreatePlaylistRequest' }, example: { name: 夜间驾驶, description: 适合长途驾驶的个人歌单 } } } }
      responses: { '201': { description: Playlist created, content: { application/json: { schema: { $ref: '#/components/schemas/Playlist' } } } }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: playlistDetail
      summary: Playlist Detail
      tags: [Collections]
      description: "Returns data for Playlist Detail. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Playlist and tracks, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/PlaylistDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updatePlaylist
      summary: Update Playlist
      tags: [Collections]
      description: "Updates the resource state for Update Playlist. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdatePlaylistRequest' } } } }
      responses: { '200': { description: Updated playlist, content: { application/json: { schema: { $ref: '#/components/schemas/Playlist' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deletePlaylist
      summary: Delete Playlist
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Playlist. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      responses: { '204': { description: Playlist deleted }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}/duplicate:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }, { $ref: '#/components/parameters/IdempotencyKey' }]
    post:
      operationId: duplicatePlaylist
      summary: Duplicate Playlist
      tags: [Collections]
      description: "Executes Duplicate Playlist and returns the documented result. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '201': { description: Playlist duplicated with tracks and custom artwork, content: { application/json: { schema: { $ref: '#/components/schemas/Playlist' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}/artwork:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: putPlaylistArtwork
      summary: Put Playlist Artwork
      tags: [Collections]
      description: "Creates or replaces the resource state for Put Playlist Artwork. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      requestBody:
        required: true
        content:
          image/jpeg: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/png: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/gif: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/webp: { schema: { type: string, format: binary, maxLength: 20971520 } }
      responses: { '200': { description: Updated playlist, content: { application/json: { schema: { $ref: '#/components/schemas/Playlist' } } } }, '404': { $ref: '#/components/responses/Problem' }, '413': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deletePlaylistArtwork
      summary: Delete Playlist Artwork
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Playlist Artwork. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '200': { description: Updated playlist, content: { application/json: { schema: { $ref: '#/components/schemas/Playlist' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}/tracks:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: addPlaylistTracks
      summary: Add Playlist Tracks
      tags: [Collections]
      description: "Executes Add Playlist Tracks and returns the documented result. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/AddPlaylistTracksRequest' } } } }
      responses: { '200': { description: Bulk add result, content: { application/json: { schema: { $ref: '#/components/schemas/PlaylistTrackMutationResult' } } } }, '204': { description: Single track added using the backwards-compatible track_id form }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deletePlaylistTracks
      summary: Delete Playlist Tracks
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Playlist Tracks. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeletePlaylistTracksRequest' } } } }
      responses: { '200': { description: Bulk remove result, content: { application/json: { schema: { $ref: '#/components/schemas/PlaylistTrackMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}/track-order:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
    put:
      operationId: reorderPlaylistTracks
      summary: Reorder Playlist Tracks
      tags: [Collections]
      description: "Creates or replaces the resource state for Reorder Playlist Tracks. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ReorderPlaylistTracksRequest' } } } }
      responses: { '204': { description: Complete playlist order replaced atomically }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /playlists/{playlist_id}/tracks/{track_id}:
    parameters: [{ name: playlist_id, in: path, required: true, schema: { type: string } }, { name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    delete:
      operationId: deletePlaylistTrack
      summary: Delete Playlist Track
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Playlist Track. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Track removed from playlist }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/playback-queue:
    get:
      operationId: playbackQueue
      summary: Playback Queue
      tags: [Collections]
      description: Returns the authenticated user's cross-device queue, current index, repeat mode, shuffle state, and volume.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '200': { description: Current user's persisted playback queue, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackQueue' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    put:
      operationId: putPlaybackQueue
      summary: Put Playback Queue
      tags: [Collections]
      description: Atomically replaces the authenticated user's persisted queue and playback controls. After commit, signed-in web sessions for the same user receive a user-filtered home.changed event with reason playback_queue_changed and re-read the queue; concurrent clients still use last-write-wins replacement semantics.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackQueueInput' } } } }
      responses: { '204': { description: Playback queue persisted }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/play-later:
    get:
      operationId: playLater
      summary: Play Later
      tags: [Collections]
      description: "Returns data for Play Later. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Current user's ordered play-later list, content: { application/json: { schema: { $ref: '#/components/schemas/TrackPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearPlayLater
      summary: Clear Play Later
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Clear Play Later. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Play-later list cleared }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/play-later/{track_id}:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: addPlayLaterTrack
      summary: Add Play Later Track
      tags: [Collections]
      description: "Creates or replaces the resource state for Add Play Later Track. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Track added idempotently to the end of the play-later list }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deletePlayLaterTrack
      summary: Delete Play Later Track
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Delete Play Later Track. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Track removed from the play-later list }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/playback-progress:
    get:
      operationId: personalPlaybackProgress
      summary: Personal Playback Progress
      tags: [Collections]
      description: "Returns data for Personal Playback Progress. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ name: device_id, in: query, schema: { type: string, maxLength: 120 } }]
      responses: { '200': { description: Resumable progress for the current user's devices, content: { application/json: { schema: { $ref: '#/components/schemas/PersonalPlaybackProgressList' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/playback-progress/{device_id}:
    parameters: [{ name: device_id, in: path, required: true, schema: { type: string, maxLength: 120 } }, { $ref: '#/components/parameters/CSRF' }]
    delete:
      operationId: deletePersonalPlaybackProgress
      summary: Delete Personal Playback Progress
      tags: [Collections]
      description: "Deletes the authenticated user's saved progress for one device. When a row is removed, signed-in web sessions for that user receive playback.progress and immediately re-read /me/playback-progress. Use a CookieSession or a Bearer token with `playback:control`."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '204': { description: Device resume position cleared }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/playback-history:
    get:
      operationId: playbackHistory
      summary: Playback History
      tags: [Collections]
      description: "Returns data for Playback History. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }, { name: latest_per_track, in: query, description: Return only the latest playback event for each track., schema: { type: boolean, default: false } }]
      responses: { '200': { description: Stable playback history for the current user, optionally reduced to the latest event per track, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackHistoryPage' } } } }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearPlaybackHistory
      summary: Clear Playback History
      tags: [Collections]
      description: "Deletes or clears the resource targeted by Clear Playback History. Use a CookieSession or a Bearer token with `playback:control` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Current user's playback events cleared }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/recently-played:
    get:
      operationId: recentlyPlayed
      summary: Recently Played
      tags: [Collections]
      description: "Returns data for Recently Played. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Paginated recently played tracks, content: { application/json: { schema: { $ref: '#/components/schemas/HomeTrackPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/most-played:
    get:
      operationId: mostPlayed
      summary: Most Played
      tags: [Collections]
      description: "Returns data for Most Played. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Paginated most-played tracks, content: { application/json: { schema: { $ref: '#/components/schemas/HomeTrackPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/recommendations/tracks:
    get:
      operationId: recommendedTracks
      summary: Recommended Tracks
      tags: [Collections]
      description: "Returns data for Recommended Tracks. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Paginated personalized track recommendations, content: { application/json: { schema: { $ref: '#/components/schemas/HomeTrackPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/recommendations/artists:
    get:
      operationId: recommendedArtists
      summary: Recommended Artists
      tags: [Collections]
      description: "Returns data for Recommended Artists. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Paginated personalized artist recommendations, content: { application/json: { schema: { $ref: '#/components/schemas/HomeArtistPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/api-tokens:
    get:
      operationId: apiTokens
      summary: List API tokens
      tags: [Identity]
      description: "Returns data for List API tokens. A valid CookieSession is required."
      responses: { '200': { description: Current user's active API tokens, content: { application/json: { schema: { $ref: '#/components/schemas/APITokenList' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: createAPIToken
      summary: Create API Token
      tags: [Identity]
      description: "Executes Create API Token and returns the documented result. A valid CookieSession is required."
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateAPITokenRequest' }, example: { name: Living room player, expires_in_days: 365, scopes: ['catalog:read', 'playback:stream', 'playback:control', 'playback:report'] } } } }
      responses: { '201': { description: API token created; the plaintext token is returned only once, content: { application/json: { schema: { $ref: '#/components/schemas/CreatedAPIToken' } } } }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /me/api-tokens/{token_id}:
    parameters: [{ name: token_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    patch:
      operationId: updateAPIToken
      summary: Update API Token
      tags: [Identity]
      description: "Updates the resource state for Update API Token. A valid CookieSession is required."
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateAPITokenRequest' } } } }
      responses: { '204': { description: Token scopes updated }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteAPIToken
      summary: Delete API Token
      tags: [Identity]
      description: "Deletes or clears the resource targeted by Delete API Token. A valid CookieSession is required."
      responses: { '204': { description: API token revoked }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /player/session:
    post:
      operationId: playerLogin
      summary: Connect Player With Password
      tags: [Identity]
      description: Exchanges a MusicArk username and password for a random non-expiring Bearer token. Repeating the request for the same account and device keeps one token_id, rotates the access_token, and immediately invalidates the previously returned access_token. A stable device_id is recommended; when omitted, normalized client_name or User-Agent is used as the compatibility identity. Administrators receive the four player scopes plus library:write; members receive the four player scopes. The shared login rate limit applies. The password is not encoded into the token and must not be sent again after this response.
      security: []
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PlayerLoginRequest' }, example: { username: alice, password: correct-horse-battery-staple, client_name: Living room player, device_id: living-room-player-01 } } } }
      responses: { '200': { description: Existing player device token rotated; token_id is preserved and only the newly returned plaintext access token remains valid, content: { application/json: { schema: { $ref: '#/components/schemas/PlayerSession' } } } }, '201': { description: New player device authorization created; the plaintext access token is returned only in this response, content: { application/json: { schema: { $ref: '#/components/schemas/PlayerSession' } } } }, '401': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '429': { $ref: '#/components/responses/RateLimited' }, '400': { $ref: '#/components/responses/BadRequest' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: playerLogout
      summary: Disconnect Current Player
      tags: [Identity]
      description: Revokes the Bearer token used for this request. Direct-login tokens always include catalog:read when issued.
      security: [{ PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '204': { description: Current player token revoked }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /player/progress:
    get:
      operationId: externalPlaybackProgress
      summary: External Playback Progress
      tags: [Playback]
      description: "Returns data for External Playback Progress. Use a CookieSession or a Bearer token with `playback:report` unless the operation declares Bearer-only security."
      security: [{ PlaybackBearer: [] }]
      x-required-bearer-scope: playback:report
      parameters: [{ name: device_id, in: query, schema: { type: string, maxLength: 120 } }]
      responses: { '200': { description: Last reported progress for each device, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackProgressList' } } } }, '401': { $ref: '#/components/responses/Problem' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: reportExternalPlaybackProgress
      summary: Report External Playback Progress
      tags: [Playback]
      description: "Reports an external player's playback state. Playback history is created after 3 seconds or 50% of the track duration, whichever comes first; a completed report also qualifies. Repeated reports with the same playback_id remain idempotent. A report that actually inserts or replaces device progress emits a user-filtered playback.progress event so signed-in web sessions can immediately re-read /me/playback-progress; an older occurred_at that does not replace current state emits no event. Use a Bearer token with `playback:report`."
      security: [{ PlaybackBearer: [] }]
      x-required-bearer-scope: playback:report
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackProgressReport' }, example: { track_id: trk_01example, playback_id: living-room-20260812-000001, device_id: living-room, position_ms: 42800, duration_ms: 215000, state: playing, source: my-player, occurred_at: '2026-08-12T12:10:00Z' } } } }
      responses: { '200': { description: Progress persisted and an eligible play recorded idempotently, content: { application/json: { schema: { $ref: '#/components/schemas/PlaybackProgressResponse' } } } }, '401': { $ref: '#/components/responses/Problem' }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: trackDetail
      summary: Track Detail
      tags: [Catalog]
      description: "Returns data for Track Detail. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Track detail, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/TrackDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateTrack
      summary: Update Track
      tags: [Catalog]
      description: "Updates the resource state for Update Track. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateTrackRequest' } } } }
      responses: { '200': { description: Updated track, content: { application/json: { schema: { $ref: '#/components/schemas/Track' } } } }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/scrape:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: scrapeTrack
      summary: Scrape Track
      tags: [Catalog]
      description: "Executes Scrape Track and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Track metadata extracted again from its source file, content: { application/json: { schema: { $ref: '#/components/schemas/Track' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/write-tags:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IdempotencyKey' }]
    post:
      operationId: writeTrackTags
      summary: Write Track Tags
      tags: [Catalog]
      description: "Executes Write Track Tags and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ConfirmedTrackSourceRequest' } } } }
      responses: { '200': { description: Current MusicArk title, artists, album, track, disc and year atomically written to the source file through FFmpeg stream copy, content: { application/json: { schema: { $ref: '#/components/schemas/WriteTrackTagsResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' } }
  /catalog/tracks/write-tags:
    post:
      operationId: batchWriteTrackTags
      summary: Batch Write Track Tags
      tags: [Catalog]
      description: "Executes Batch Write Track Tags and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/BatchWriteTrackTagsRequest' } } } }
      responses: { '200': { description: Per-track tag-write results, content: { application/json: { schema: { $ref: '#/components/schemas/BatchWriteTrackTagsResponse' } } } }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/move-source:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IdempotencyKey' }]
    post:
      operationId: moveTrackSource
      summary: Move Track Source
      tags: [Catalog]
      description: "Executes Move Track Source and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/MoveTrackSourceRequest' } } } }
      responses: { '200': { description: Source file moved or renamed within/between writable media roots and catalog paths updated atomically, content: { application/json: { schema: { $ref: '#/components/schemas/MoveTrackSourceResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' } }
  /catalog/tracks/{track_id}/organize-preview:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: trackOrganizerPreview
      summary: Track Organizer Preview
      tags: [Catalog]
      description: "Executes Track Organizer Preview and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/OrganizerPreviewRequest' } } } }
      responses: { '200': { description: Rendered target path and current file version, content: { application/json: { schema: { $ref: '#/components/schemas/OrganizerPreviewItem' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/organize-preview:
    post:
      operationId: batchOrganizerPreview
      summary: Batch Organizer Preview
      tags: [Catalog]
      description: "Executes Batch Organizer Preview and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/OrganizerPreviewRequest' } } } }
      responses: { '200': { description: Rendered target paths for up to 100 tracks, content: { application/json: { schema: { $ref: '#/components/schemas/BatchOrganizerPreviewResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/source:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IdempotencyKey' }]
    delete:
      operationId: deleteTrackSource
      summary: Delete Track Source
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Track Source. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteTrackSourceRequest' } } } }
      responses: { '200': { description: Source file moved into the root-local recycle area and catalog entry marked unavailable, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteTrackSourceResponse' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '500': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' } }
  /catalog/tracks/{track_id}/artwork:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: putTrackArtwork
      summary: Put Track Artwork
      tags: [Catalog]
      description: "Creates or replaces the resource state for Put Track Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody:
        required: true
        content:
          image/jpeg: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/png: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/gif: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/webp: { schema: { type: string, format: binary, maxLength: 20971520 } }
      responses: { '200': { description: Track detail with custom artwork, content: { application/json: { schema: { $ref: '#/components/schemas/TrackDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '413': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteTrackArtwork
      summary: Delete Track Artwork
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Track Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Track detail with inherited artwork restored, content: { application/json: { schema: { $ref: '#/components/schemas/TrackDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/artists:
    get:
      operationId: artists
      summary: Artists
      tags: [Catalog]
      description: "Returns data for Artists. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }, { name: q, in: query, schema: { type: string } }, { name: direction, in: query, schema: { type: string, enum: [asc,desc], default: asc } }]
      responses: { '200': { description: Artist page, content: { application/json: { schema: { $ref: '#/components/schemas/ArtistPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: batchUpdateArtists
      summary: Batch Update Artists
      tags: [Catalog]
      description: "Updates the resource state for Batch Update Artists. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/BatchUpdateArtistsRequest' } } } }
      responses: { '200': { description: Artists updated atomically, content: { application/json: { schema: { $ref: '#/components/schemas/BatchMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/facets:
    get:
      operationId: catalogFacets
      summary: Catalog Facets
      tags: [Catalog]
      description: "Returns data for Catalog Facets. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Catalog genre, style, language, year, decade, and format facets with active-track counts, content: { application/json: { schema: { $ref: '#/components/schemas/CatalogFacets' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/artists/{artist_id}:
    parameters: [{ name: artist_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: artistDetail
      summary: Artist Detail
      tags: [Catalog]
      description: "Returns data for Artist Detail. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Artist detail, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/ArtistDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateArtist
      summary: Update Artist
      tags: [Catalog]
      description: "Updates the resource state for Update Artist. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateArtistRequest' } } } }
      responses: { '200': { description: Updated artist detail, content: { application/json: { schema: { $ref: '#/components/schemas/ArtistDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteUnusedArtist
      summary: Delete Unused Artist
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Unused Artist. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      responses: { '204': { description: Unused artist deleted }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/artists/{artist_id}/merge:
    parameters: [{ name: artist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }, { $ref: '#/components/parameters/IdempotencyKey' }, { name: dry_run, in: query, schema: { type: boolean, default: false } }]
    post:
      operationId: mergeArtist
      summary: Merge Artist
      tags: [Catalog]
      description: "Executes Merge Artist and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/MergeCatalogRequest' } } } }
      responses: { '200': { description: Merge preview or completed artist merge, content: { application/json: { schema: { $ref: '#/components/schemas/MergeCatalogResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/artists/{artist_id}/artwork:
    parameters: [{ name: artist_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: putArtistArtwork
      summary: Put Artist Artwork
      tags: [Catalog]
      description: "Creates or replaces the resource state for Put Artist Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody:
        required: true
        content:
          image/jpeg: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/png: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/gif: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/webp: { schema: { type: string, format: binary, maxLength: 20971520 } }
      responses: { '200': { description: Updated artist detail, content: { application/json: { schema: { $ref: '#/components/schemas/ArtistDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '413': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteArtistArtwork
      summary: Delete Artist Artwork
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Artist Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Artist detail with source artwork restored, content: { application/json: { schema: { $ref: '#/components/schemas/ArtistDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/releases:
    get:
      operationId: releases
      summary: Releases
      tags: [Catalog]
      description: "Returns data for Releases. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters:
        - { $ref: '#/components/parameters/Limit' }
        - { $ref: '#/components/parameters/After' }
        - { name: q, in: query, schema: { type: string } }
        - { name: sort, in: query, schema: { type: string, enum: [title,artist,year,added_at], default: title } }
        - { name: direction, in: query, schema: { type: string, enum: [asc,desc] } }
      responses: { '200': { description: Release page, content: { application/json: { schema: { $ref: '#/components/schemas/ReleasePage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: batchUpdateReleases
      summary: Batch Update Releases
      tags: [Catalog]
      description: "Updates the resource state for Batch Update Releases. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/BatchUpdateReleasesRequest' } } } }
      responses: { '200': { description: Releases updated atomically, content: { application/json: { schema: { $ref: '#/components/schemas/BatchMutationResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/releases/{release_id}:
    parameters: [{ name: release_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: releaseDetail
      summary: Release Detail
      tags: [Catalog]
      description: "Returns data for Release Detail. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Release detail, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/ReleaseDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateRelease
      summary: Update Release
      tags: [Catalog]
      description: "Updates the resource state for Update Release. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateReleaseRequest' } } } }
      responses: { '200': { description: Updated release detail, content: { application/json: { schema: { $ref: '#/components/schemas/ReleaseDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteRelease
      summary: Delete Release
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Release. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }]
      responses: { '200': { description: Release deleted and tracks detached without modifying source files, content: { application/json: { schema: { $ref: '#/components/schemas/DeleteReleaseResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/releases/{release_id}/merge:
    parameters: [{ name: release_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }, { $ref: '#/components/parameters/IfMatch' }, { $ref: '#/components/parameters/IdempotencyKey' }, { name: dry_run, in: query, schema: { type: boolean, default: false } }]
    post:
      operationId: mergeRelease
      summary: Merge Release
      tags: [Catalog]
      description: "Executes Merge Release and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/MergeCatalogRequest' } } } }
      responses: { '200': { description: Merge preview or completed release merge, content: { application/json: { schema: { $ref: '#/components/schemas/MergeCatalogResult' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/releases/{release_id}/artwork:
    parameters: [{ name: release_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    put:
      operationId: putReleaseArtwork
      summary: Put Release Artwork
      tags: [Catalog]
      description: "Creates or replaces the resource state for Put Release Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      requestBody:
        required: true
        content:
          image/jpeg: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/png: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/gif: { schema: { type: string, format: binary, maxLength: 20971520 } }
          image/webp: { schema: { type: string, format: binary, maxLength: 20971520 } }
      responses: { '200': { description: Updated release detail, content: { application/json: { schema: { $ref: '#/components/schemas/ReleaseDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '413': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteReleaseArtwork
      summary: Delete Release Artwork
      tags: [Catalog]
      description: "Deletes or clears the resource targeted by Delete Release Artwork. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Release detail with source artwork restored, content: { application/json: { schema: { $ref: '#/components/schemas/ReleaseDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/query:
    get:
      operationId: search
      summary: Search
      tags: [Catalog]
      description: Searches effective track, release, artist, and playlist metadata and returns grouped results for the authenticated user.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      parameters: [{ name: q, in: query, required: true, schema: { type: string } }]
      responses: { '200': { description: Grouped search results, content: { application/json: { schema: { $ref: '#/components/schemas/SearchResults' } } } }, '400': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/lyrics:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: trackLyrics
      summary: Track Lyrics
      tags: [Catalog]
      description: "Returns data for Track Lyrics. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Lyrics summaries, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsSummaryList' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    post:
      operationId: createTrackLyrics
      summary: Create Track Lyrics
      tags: [Catalog]
      description: "Executes Create Track Lyrics and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsMutationRequest' } } } }
      responses: { '200': { description: Created plain or synchronized lyrics document, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsDocument' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /lyrics/{lyrics_id}:
    parameters: [{ name: lyrics_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: lyrics
      summary: Lyrics
      tags: [Media]
      description: "Returns data for Lyrics. Use a CookieSession or a Bearer token with `catalog:read` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses: { '200': { description: Plain text, LRC, or synchronized lyrics document, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsDocument' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    put:
      operationId: updateLyrics
      summary: Update Lyrics
      tags: [Media]
      description: "Creates or replaces the resource state for Update Lyrics. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsMutationRequest' } } } }
      responses: { '200': { description: Updated lyrics document, content: { application/json: { schema: { $ref: '#/components/schemas/LyricsDocument' } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteLyrics
      summary: Delete Lyrics
      tags: [Media]
      description: "Deletes or clears the resource targeted by Delete Lyrics. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Lyrics document deleted and another document selected as default when needed }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /lyrics/{lyrics_id}/default:
    parameters: [{ name: lyrics_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: setDefaultLyrics
      summary: Set Default Lyrics
      tags: [Media]
      description: "Executes Set Default Lyrics and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '204': { description: Lyrics document selected as the track default }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /images/{image_id}:
    parameters:
      - { name: image_id, in: path, required: true, schema: { type: string } }
      - { name: size, in: query, description: Omit for the unchanged original bytes or choose a supported long-edge JPEG thumbnail size, schema: { type: integer, enum: [64,128,256,512,1024] } }
    get:
      operationId: image
      summary: Image
      tags: [Media]
      description: "Returns the unchanged original artwork when size is omitted, or a Catmull-Rom JPEG quality 88 thumbnail at a supported long-edge size. Thumbnail representations are persistently cached by content and size; each representation has its own ETag. Use a CookieSession or a Bearer token with `catalog:read`."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: catalog:read
      responses:
        '200':
          description: Artwork bytes
          headers:
            ETag: { description: Stable validator for the image content and selected representation size, schema: { type: string } }
            Cache-Control: { description: Private immutable cache policy for content-addressed artwork, schema: { type: string } }
          content:
            image/jpeg: { schema: { type: string, format: binary } }
            image/png: { schema: { type: string, format: binary } }
            image/gif: { schema: { type: string, format: binary } }
            image/webp: { schema: { type: string, format: binary } }
        '304': { description: The selected original or thumbnail representation matches If-None-Match }
        '422': { $ref: '#/components/responses/Problem' }
        '404': { $ref: '#/components/responses/Problem' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /transcode-profiles:
    get:
      operationId: transcodeProfiles
      summary: Transcode Profiles
      tags: [Playback]
      description: Returns the stable built-in profile catalog together with runtime availability. Clients must choose only entries whose available field is true.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses:
        '200':
          description: Fixed safe transcoding profiles and current FFmpeg availability
          content: { application/json: { schema: { $ref: '#/components/schemas/TranscodeProfileList' } } }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/InternalServerError' }
  /transcode-cache-settings:
    get:
      operationId: transcodeCacheSettings
      summary: Transcode Cache Settings
      tags: [Administration, Playback]
      description: Returns the administrator-managed shared transcode cache limit and current finalized cache usage. The configured environment value is used until an administrator saves an override.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Current transcode cache settings, content: { application/json: { schema: { $ref: '#/components/schemas/TranscodeCacheSettings' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateTranscodeCacheSettings
      summary: Update Transcode Cache Settings
      tags: [Administration, Playback]
      description: Persists an administrator-selected shared cache limit and immediately removes the oldest finalized entries until usage is within the new limit. Zero disables and clears finalized transcode caching.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateTranscodeCacheSettingsRequest' }, example: { max_bytes: 5368709120 } } } }
      responses: { '200': { description: Updated transcode cache settings, content: { application/json: { schema: { $ref: '#/components/schemas/TranscodeCacheSettings' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearTranscodeCache
      summary: Clear Transcode Cache
      tags: [Administration, Playback]
      description: Removes all finalized and abandoned transcode cache entries without changing the configured shared cache limit. Returns 409 while a cache-producing transcode job is active.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '200': { description: Current transcode cache settings after clearing, content: { application/json: { schema: { $ref: '#/components/schemas/TranscodeCacheSettings' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /preferences/me/transcoding:
    get:
      operationId: userTranscodingPreference
      summary: User Transcoding Preference
      tags: [Playback]
      description: Returns the current account's independent playback policy and default profile. MusicArk has no administrator-controlled instance default or inheritance state.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      responses: { '200': { description: Current account's isolated preference, using the built-in safe default until first saved, content: { application/json: { schema: { $ref: '#/components/schemas/UserTranscodingPreference' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateUserTranscodingPreference
      summary: Update User Transcoding Preference
      tags: [Playback]
      description: Replaces the current account's playback policy and default profile without changing any other user's settings.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:control
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateUserTranscodingPreferenceRequest' }, example: { playback_policy: always_transcode, default_profile_id: web_opus_128_v1 } } } }
      responses: { '200': { description: Updated effective preference, content: { application/json: { schema: { $ref: '#/components/schemas/UserTranscodingPreference' } } } }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions:
    post:
      operationId: createListeningSession
      summary: Create Listening Session
      tags: [Playback]
      description: Creates a user-bound 60-minute playback session after resolving direct playback, explicit profile selection, the user's preference, client capabilities, source version, and optional start position.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ListeningRequest' }, example: { track_id: trk_01example, preference: original_if_supported, start_ms: 42000, client_capabilities: { mime_types: [audio/mpeg, audio/mp4], supports_hls: true, supports_media_source: true } } } } }
      responses: { '201': { description: Direct, cached progressive-transcode, or native-HLS-capable listening session, content: { application/json: { schema: { $ref: '#/components/schemas/ListeningSession' } } } }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions/{session_id}/stream:
    parameters:
      - { name: session_id, in: path, required: true, schema: { type: string } }
      - { name: Range, in: header, schema: { type: string } }
    get:
      operationId: stream
      summary: Stream
      tags: [Playback]
      description: "Returns data for Stream. Use a CookieSession or a Bearer token with `playback:stream` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses: { '200': { description: 'Complete original audio, a profile-selected live transcode, or a cached transcode. Cached transcodes support byte ranges and return X-MusicArk-Cache: hit; first generation returns miss. A genuine live transcode failure after headers reports TRANSCODE_RUNTIME_FAILED in the declared X-MusicArk-Error-Code trailer.', content: { application/octet-stream: { schema: { type: string, format: binary } } } }, '206': { description: Partial original or cached transcoded audio, content: { application/octet-stream: { schema: { type: string, format: binary } } } }, '409': { description: Transcode process could not start (TRANSCODE_START_FAILED), content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } } }, '416': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    head:
      operationId: streamHead
      summary: Stream Head
      tags: [Playback]
      description: "Returns the response headers for Stream Head without a body. Use a CookieSession or a Bearer token with `playback:stream` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses: { '200': { description: Audio headers }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions/{session_id}/hls/index.m3u8:
    parameters: [{ name: session_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: hlsManifest
      summary: Get HLS manifest
      tags: [Playback]
      description: Generates or reuses a session-scoped cached HLS VOD rendition for HLS-compatible MP3/AAC transcode profiles.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses: { '200': { description: HLS media playlist, content: { application/vnd.apple.mpegurl: { schema: { type: string } } } }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '410': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions/{session_id}/hls/{segment}:
    parameters:
      - { name: session_id, in: path, required: true, schema: { type: string } }
      - { name: segment, in: path, required: true, schema: { type: string, pattern: '^segment-[0-9]{5}\.ts$' } }
    get:
      operationId: hlsSegment
      summary: Get HLS segment
      tags: [Playback]
      description: "Returns data for Get HLS segment. Use a CookieSession or a Bearer token with `playback:stream` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses: { '200': { description: Cached MPEG-TS audio segment, content: { video/mp2t: { schema: { type: string, format: binary } } } }, '206': { description: Partial cached MPEG-TS audio segment, content: { video/mp2t: { schema: { type: string, format: binary } } } }, '404': { $ref: '#/components/responses/Problem' }, '410': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions/{session_id}/progress:
    parameters: [{ name: session_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: listeningProgress
      summary: Listening Progress
      tags: [Playback]
      description: "Persists progress for an active listening session and emits user-filtered playback.progress so signed-in web sessions can immediately re-read /me/playback-progress. Qualifying plays remain idempotent per listening session. Use a CookieSession or a Bearer token with `playback:report`."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:report
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ListeningProgressRequest' } } } }
      responses: { '204': { description: Progress accepted; qualifying plays are recorded once per session }, '404': { $ref: '#/components/responses/Problem' }, '410': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /listening-sessions/{session_id}:
    parameters: [{ name: session_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    delete:
      operationId: endListeningSession
      summary: End Listening Session
      tags: [Playback]
      description: "Deletes or clears the resource targeted by End Listening Session. Use a CookieSession or a Bearer token with `playback:stream` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: playback:stream
      responses: { '204': { description: Session ended }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-sources:
    get:
      operationId: enrichmentSources
      summary: Enrichment Sources
      tags: [Enrichment]
      description: Lists the fixed, compile-time online enrichment connectors and probe state. All connectors remain built in; enrichment preferences choose the non-empty subset queried by new runs.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Enrichment sources, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentSourceList' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-sources/{source_id}:
    parameters: [{ name: source_id, in: path, required: true, schema: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] } }]
    patch:
      operationId: updateEnrichmentSource
      summary: Update Enrichment Source
      tags: [Enrichment]
      description: Compatibility endpoint for the former source switch. Sending enabled=true is idempotent; enabled=false is rejected because all built-in sources remain enabled.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateEnrichmentSourceRequest' } } } }
      responses: { '200': { description: Updated source list, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentSourceList' } } } }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-sources/{source_id}/probe:
    parameters: [{ name: source_id, in: path, required: true, schema: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] } }]
    post:
      operationId: probeEnrichmentSource
      summary: Probe Enrichment Source
      tags: [Enrichment]
      description: Performs an explicit administrator-requested connectivity test without storing upstream response bodies.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '200': { description: Connectivity test succeeded, content: { application/json: { schema: { type: object, required: [source_id,status,probed_at], properties: { source_id: { type: string }, status: { type: string, const: ok }, probed_at: { type: string, format: date-time } } } } } }, '403': { $ref: '#/components/responses/Problem' }, '502': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-quality:
    get:
      operationId: enrichmentQuality
      summary: Enrichment Quality
      tags: [Enrichment]
      description: Lists tracks missing metadata, artwork, or lyrics. Supports pages of up to 1000 items so the information enrichment center can load every matching track. Administrator CookieSession only.
      security: [{ CookieSession: [] }]
      parameters:
        - { name: issue, in: query, schema: { type: string, enum: [metadata,artwork,lyrics,all], default: all } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 1000, default: 100 } }
        - { $ref: '#/components/parameters/After' }
      responses: { '200': { description: Enrichment quality page, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentQualityPage' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-automation:
    get:
      operationId: enrichmentAutomationSettings
      summary: Enrichment Automation Settings
      tags: [Enrichment]
      description: Reads the administrator opt-in for automatic post-scan enrichment. Disabled by default.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Automation settings, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentAutomationSettings' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateEnrichmentAutomationSettings
      summary: Update Enrichment Automation Settings
      tags: [Enrichment]
      description: Explicitly opts in or out of sending newly added track descriptors to configured online sources after a scan.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateEnrichmentAutomationRequest' } } } }
      responses: { '200': { description: Updated automation settings, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentAutomationSettings' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-preferences:
    get:
      operationId: enrichmentPreferences
      summary: Enrichment Preferences
      tags: [Enrichment]
      description: Reads the instance-wide automatic matching strategy and non-empty built-in source subset used by new online enrichment runs. Existing runs keep their creation-time snapshot.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Enrichment preferences, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentPreferences' } } } }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    patch:
      operationId: updateEnrichmentPreferences
      summary: Update Enrichment Preferences
      tags: [Enrichment]
      description: Updates the matching strategy or selected source subset. At least one source must remain selected.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/UpdateEnrichmentPreferencesRequest' } } } }
      responses: { '200': { description: Updated enrichment preferences, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentPreferences' } } } }, '403': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/enrichment-opportunity:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: postScanEnrichmentOpportunity
      summary: Post Scan Enrichment Opportunity
      tags: [Operations]
      description: Counts newly added tracks from this scan that are missing metadata, artwork, or lyrics.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Post-scan enrichment opportunity, content: { application/json: { schema: { $ref: '#/components/schemas/PostScanEnrichmentOpportunity' } } } }, '403': { $ref: '#/components/responses/Problem' }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /index-runs/{run_id}/enrichment:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }, { $ref: '#/components/parameters/CSRF' }]
    post:
      operationId: startPostScanEnrichment
      summary: Start Post Scan Enrichment
      tags: [Operations]
      description: Starts an idempotent auto_high_confidence enrichment run for every newly added, incomplete track from this scan, snapshotting the current match_mode and selected sources.
      security: [{ CookieSession: [] }]
      responses: { '200': { description: Existing run, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentRun' } } } }, '202': { description: Run accepted, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentRun' } } } }, '403': { $ref: '#/components/responses/Problem' }, '404': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-runs:
    post:
      operationId: createEnrichmentRun
      summary: Create Enrichment Run
      tags: [Enrichment]
      description: Creates an online enrichment run for explicit tracks or an administrator quality filter without an application-level track-count cap. A quality-filter selection can exclude individual track IDs without sending every selected ID. The server snapshots the current instance match_mode; a non-empty source_ids request overrides the preferred source subset for this run, while an empty list falls back to the preference. Automatic modes only write MusicArk's enrichment layer.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateEnrichmentRunRequest' } } } }
      responses: { '202': { description: Enrichment run accepted, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentRun' } } } }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: clearEnrichmentRuns
      summary: Clear Enrichment Runs
      tags: [Enrichment]
      description: Clears finished, unreferenced enrichment runs while preserving active runs and adopted provenance.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '200': { description: Finished enrichment history cleared }, '403': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-runs/{run_id}:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: enrichmentRun
      summary: Enrichment Run
      tags: [Enrichment]
      description: "Returns data for Enrichment Run. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Enrichment run, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentRun' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteEnrichmentRun
      summary: Delete Enrichment Run
      tags: [Enrichment]
      description: Deletes a finished run only when no adopted field still references its candidates.
      security: [{ CookieSession: [] }]
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Run deleted }, '403': { $ref: '#/components/responses/Problem' }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-runs/{run_id}/abort:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    post:
      operationId: abortEnrichmentRun
      summary: Abort Enrichment Run
      tags: [Enrichment]
      description: "Executes Abort Enrichment Run and returns the documented result. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '202': { description: Cancellation requested }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-runs/{run_id}/resume:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    post:
      operationId: resumeEnrichmentRun
      summary: Resume Enrichment Run
      tags: [Enrichment]
      description: Explicitly resumes an interrupted, cancelled, or failed run, or retries failed targets from a completed or review-ready run while preserving applied and reviewable results.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '202': { description: Run queued again }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-runs/{run_id}/targets:
    parameters: [{ name: run_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: enrichmentTargets
      summary: Enrichment Targets
      tags: [Enrichment]
      description: "Returns data for Enrichment Targets. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/Limit' }, { $ref: '#/components/parameters/After' }]
      responses: { '200': { description: Enrichment targets, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentTargetPage' } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-targets/{target_id}/candidates:
    parameters: [{ name: target_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: enrichmentCandidates
      summary: Enrichment Candidates
      tags: [Enrichment]
      description: "Returns data for Enrichment Candidates. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Sanitized candidate summaries, content: { application/json: { schema: { type: object, required: [items], properties: { items: { type: array, items: { $ref: '#/components/schemas/EnrichmentCandidate' } } } } } } }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-candidates/{candidate_id}:
    parameters: [{ name: candidate_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: enrichmentCandidate
      summary: Enrichment Candidate
      tags: [Enrichment]
      description: "Returns data for Enrichment Candidate. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Candidate with field assertions, content: { application/json: { schema: { $ref: '#/components/schemas/EnrichmentCandidateDetail' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /enrichment-candidates/{candidate_id}/artwork-preview:
    parameters: [{ name: candidate_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: enrichmentArtworkPreview
      summary: Enrichment Artwork Preview
      tags: [Enrichment]
      description: Downloads, validates, decodes, stores, and serves remote artwork through a same-origin URL.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '302': { description: Redirect to validated same-origin image }, '422': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /change-plans:
    post:
      operationId: createChangePlan
      summary: Create Change Plan
      tags: [Enrichment]
      description: Creates a 15-minute, pre-commit safety preview for applying or removing selected enrichment fields. The window applies only to this uncommitted plan; successfully applied metadata, artwork, and lyrics are persistent and do not expire.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CreateChangePlanRequest' } } } }
      responses: { '201': { description: Change plan preview, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/ChangePlan' } } } }, '409': { $ref: '#/components/responses/Problem' }, '422': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /change-plans/{plan_id}:
    parameters: [{ name: plan_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: changePlan
      summary: Change Plan
      tags: [Enrichment]
      description: "Returns data for Change Plan. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Change plan, headers: { ETag: { schema: { type: string } } }, content: { application/json: { schema: { $ref: '#/components/schemas/ChangePlan' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
    delete:
      operationId: deleteChangePlan
      summary: Delete Change Plan
      tags: [Enrichment]
      description: "Deletes or clears the resource targeted by Delete Change Plan. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters: [{ $ref: '#/components/parameters/CSRF' }]
      responses: { '204': { description: Plan abandoned }, '409': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /change-plans/{plan_id}/commit:
    parameters: [{ name: plan_id, in: path, required: true, schema: { type: string } }]
    post:
      operationId: commitChangePlan
      summary: Commit Change Plan
      tags: [Enrichment]
      description: Atomically commits a non-expired preview after revalidating every target version. No partial write is performed. Committed enrichment values are persistent until explicitly overwritten or removed and do not inherit the preview expiry.
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      parameters:
        - { $ref: '#/components/parameters/CSRF' }
        - { name: If-Match, in: header, required: true, schema: { type: string } }
        - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 1, maxLength: 200 } }
      requestBody: { required: true, content: { application/json: { schema: { type: object, required: [commit_token], additionalProperties: false, properties: { commit_token: { type: string } } } } } }
      responses: { '200': { description: Plan committed }, '409': { $ref: '#/components/responses/Problem' }, '412': { $ref: '#/components/responses/Problem' }, '428': { $ref: '#/components/responses/Problem' }, '400': { $ref: '#/components/responses/BadRequest' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
  /catalog/tracks/{track_id}/provenance:
    parameters: [{ name: track_id, in: path, required: true, schema: { type: string } }]
    get:
      operationId: trackProvenance
      summary: Track Provenance
      tags: [Catalog]
      description: "Returns data for Track Provenance. Use a CookieSession or a Bearer token with `library:write` unless the operation declares Bearer-only security."
      security: [{ CookieSession: [] }, { PlaybackBearer: [] }]
      x-required-bearer-scope: library:write
      responses: { '200': { description: Effective online field origins and fixed precedence, content: { application/json: { schema: { $ref: '#/components/schemas/TrackProvenance' } } } }, '404': { $ref: '#/components/responses/Problem' }, '401': { $ref: '#/components/responses/Unauthorized' }, '403': { $ref: '#/components/responses/Forbidden' }, '500': { $ref: '#/components/responses/InternalServerError' } }
components:
  securitySchemes:
    CookieSession:
      type: apiKey
      in: cookie
      name: musicark_session
    PlaybackBearer:
      type: http
      scheme: bearer
      bearerFormat: MusicArk API token (mak_...)
      description: Player token created by the user or returned by /player/session after password authentication. Each operation declares its required permission in x-required-bearer-scope.
  parameters:
    CSRF: { name: X-CSRF-Token, in: header, required: false, description: Required when authenticating with CookieSession; not used with PlaybackBearer., schema: { type: string } }
    Limit: { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100 } }
    After: { name: after, in: query, schema: { type: string } }
    IfMatch: { name: If-Match, in: header, required: false, description: Required for Bearer updates and destructive catalog operations; use the ETag returned by the corresponding detail endpoint., schema: { type: string } }
    IdempotencyKey: { name: Idempotency-Key, in: header, required: false, description: Required for high-risk Bearer mutations. Reusing the key with the same request replays the stored result; reusing it with a different request returns 409., schema: { type: string, minLength: 1, maxLength: 200 } }
  responses:
    Problem:
      description: Problem detail
      content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
    BadRequest:
      description: The request body, JSON syntax, field type, query syntax, or framing is invalid.
      content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
    Unauthorized:
      description: A required CookieSession or Bearer token is missing, invalid, expired, disabled, or revoked.
      content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
    Forbidden:
      description: Authentication succeeded but the account, token scope, administrator role, CSRF token, or request origin is insufficient.
      content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
    InternalServerError:
      description: The server could not complete the request because of an unexpected runtime, database, filesystem, or media-tool failure.
      content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
    RateLimited:
      description: Too many requests. Wait for the number of seconds in Retry-After before retrying.
      headers:
        Retry-After: { description: Delay in seconds before another attempt., schema: { type: integer, minimum: 1 } }
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
          example: { type: 'https://docs.musicark.local/problems/LOGIN_RATE_LIMITED', title: Too many login attempts, status: 429, code: LOGIN_RATE_LIMITED, detail: Try again later., instance: /api/core/v1/session, request_id: req_01example }
  schemas:
    Health:
      description: "Represents the MusicArk health resource or value object."
      type: object
      required: [status]
      properties:
        status: { type: string, enum: [live, ready] }
        version: { type: string }
    Runtime:
      description: "Represents the MusicArk runtime resource or value object."
      type: object
      required: [version,started_at,capabilities,security]
      additionalProperties: false
      properties:
        version: { type: string }
        started_at: { type: string, format: date-time }
        capabilities:
          type: object
          required: [local_scan,direct_playback,ffmpeg,ffprobe,chromaprint,transcoding,transcode_cache,hls,online_enrichment,scheduled_scans]
          additionalProperties: false
          properties: { local_scan: { type: boolean }, direct_playback: { type: boolean }, ffmpeg: { type: boolean }, ffprobe: { type: boolean }, chromaprint: { type: boolean }, transcoding: { type: boolean }, transcode_cache: { type: boolean }, hls: { type: boolean }, online_enrichment: { type: boolean }, scheduled_scans: { type: boolean } }
        security:
          type: object
          required: [secure_cookies,development_http]
          additionalProperties: false
          properties: { secure_cookies: { type: boolean }, development_http: { type: boolean } }
    SystemStatus:
      description: "Administrator system status with backward-compatible capability booleans and authoritative structured capability details."
      type: object
      required: [status,version,started_at,checked_at,uptime_seconds,database_bytes,counts,capabilities,capability_details]
      additionalProperties: false
      properties:
        status: { type: string, const: ready }
        version: { type: string }
        started_at: { type: string, format: date-time }
        checked_at: { type: string, format: date-time }
        uptime_seconds: { type: integer, minimum: 0 }
        database_bytes: { type: integer, minimum: 0 }
        counts:
          type: object
          required: [tracks,releases,artists,roots,users,operations]
          additionalProperties: false
          properties: { tracks: { type: integer }, releases: { type: integer }, artists: { type: integer }, roots: { type: integer }, users: { type: integer }, operations: { type: integer } }
        capabilities:
          type: object
          required: [data_storage,local_scan,direct_playback,ffmpeg,ffprobe,chromaprint,transcoding,transcode_cache,hls,online_enrichment,realtime_monitoring,scheduled_scans]
          additionalProperties: false
          properties: { data_storage: { type: boolean }, local_scan: { type: boolean }, direct_playback: { type: boolean }, ffmpeg: { type: boolean }, ffprobe: { type: boolean }, chromaprint: { type: boolean }, transcoding: { type: boolean }, transcode_cache: { type: boolean }, hls: { type: boolean }, online_enrichment: { type: boolean }, realtime_monitoring: { type: boolean }, scheduled_scans: { type: boolean } }
        capability_details:
          type: object
          required: [data_storage,local_scan,direct_playback,ffmpeg,ffprobe,chromaprint,transcoding,transcode_cache,hls,online_enrichment,realtime_monitoring,scheduled_scans]
          additionalProperties: false
          properties:
            data_storage: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            local_scan: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            direct_playback: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            ffmpeg: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            ffprobe: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            chromaprint: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            transcoding: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            transcode_cache: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            hls: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            online_enrichment: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            realtime_monitoring: { $ref: '#/components/schemas/SystemCapabilityDetail' }
            scheduled_scans: { $ref: '#/components/schemas/SystemCapabilityDetail' }
    SystemCapabilityDetail:
      description: "A probed feature state. checked_at is null when no live check has been completed."
      type: object
      required: [status,summary,checked_at]
      additionalProperties: false
      properties:
        status: { type: string, enum: [available,partial,disabled,unavailable,unknown] }
        summary: { type: string }
        checked_at: { type: [string,'null'], format: date-time }
    SystemCapabilityProbeResult:
      description: "The immediately updated result of an explicit administrator-requested system capability check."
      type: object
      required: [key,available,detail]
      additionalProperties: false
      properties:
        key: { type: string, enum: [data_storage,local_scan,direct_playback,ffprobe,ffmpeg,transcoding,transcode_cache,hls,chromaprint,online_enrichment,realtime_monitoring,scheduled_scans] }
        available: { type: boolean }
        detail: { $ref: '#/components/schemas/SystemCapabilityDetail' }
    FileMutationAudit:
      description: "Represents the MusicArk file mutation audit resource or value object."
      type: object
      required: [id,operation,root_id,root_name,track_id,relative_path,target_relative_path,expected_file_version,requested_by,requested_via,request_id,status,result,created_at,updated_at,finished_at]
      properties:
        id: { type: string }
        operation: { type: string }
        root_id: { type: [string,'null'] }
        root_name: { type: [string,'null'] }
        track_id: { type: [string,'null'] }
        relative_path: { type: string }
        target_relative_path: { type: [string,'null'] }
        expected_file_version: { type: [string,'null'] }
        requested_by: { type: [string,'null'] }
        requested_via: { type: string }
        request_id: { type: [string,'null'] }
        status: { type: string, enum: [pending,completed,failed,reconcile_required] }
        result: { type: object, additionalProperties: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        finished_at: { type: [string,'null'], format: date-time }
    FileMutationAuditPage:
      description: "Cursor-paginated MusicArk file mutation audit collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/FileMutationAudit' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    AdminAuditEvent:
      description: "Represents the MusicArk admin audit event resource or value object."
      type: object
      required: [id,category,action,actor_type,actor_user_id,actor_token_id,actor_display_name,target_type,target_id,target_display_name,status,http_method,request_path,request_id,client_prefix,summary,created_at,finished_at]
      properties:
        id: { type: string }
        category: { type: string }
        action: { type: string }
        actor_type: { type: string, enum: [user,token,system,anonymous] }
        actor_user_id: { type: [string,'null'] }
        actor_token_id: { type: [string,'null'] }
        actor_display_name: { type: string }
        target_type: { type: [string,'null'] }
        target_id: { type: [string,'null'] }
        target_display_name: { type: [string,'null'] }
        status: { type: string, enum: [completed,failed] }
        http_method: { type: [string,'null'] }
        request_path: { type: [string,'null'] }
        request_id: { type: [string,'null'] }
        client_prefix: { type: [string,'null'] }
        summary: { type: object, additionalProperties: true }
        created_at: { type: string, format: date-time }
        finished_at: { type: string, format: date-time }
    AdminAuditEventPage:
      description: "Cursor-paginated MusicArk admin audit event collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/AdminAuditEvent' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    RecycledMediaFile:
      description: "Represents the MusicArk recycled media file resource or value object."
      type: object
      required: [id,media_file_id,root_id,root_name,original_relative_path,size_bytes,track_count,title,display_artist,requested_by,recycled_at,expires_at]
      additionalProperties: false
      properties:
        id: { type: string }
        media_file_id: { type: string }
        root_id: { type: string }
        root_name: { type: string }
        original_relative_path: { type: string }
        size_bytes: { type: integer }
        track_count: { type: integer }
        title: { type: string }
        display_artist: { type: string }
        requested_by: { type: [string,'null'] }
        recycled_at: { type: string, format: date-time }
        expires_at: { type: string, format: date-time }
    RecycleBinPage:
      description: "Cursor-paginated MusicArk recycle bin collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/RecycledMediaFile' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    RestoreRecycledMediaResponse:
      description: "Response payload returned by the MusicArk Restore Recycled Media operation."
      type: object
      required: [restored,recycle_id,relative_path,audit_id]
      additionalProperties: false
      properties: { restored: { type: boolean, const: true }, recycle_id: { type: string }, relative_path: { type: string }, audit_id: { type: string } }
    PurgeRecycledMediaRequest:
      description: "Request payload for the MusicArk Purge Recycled Media operation."
      type: object
      required: [confirm_permanent_delete]
      additionalProperties: false
      properties: { confirm_permanent_delete: { type: boolean, const: true } }
    PurgeRecycledMediaResponse:
      description: "Response payload returned by the MusicArk Purge Recycled Media operation."
      type: object
      required: [purged,recycle_id,removed_tracks,freed_bytes,audit_id]
      additionalProperties: false
      properties: { purged: { type: boolean, const: true }, recycle_id: { type: string }, removed_tracks: { type: integer }, freed_bytes: { type: integer }, audit_id: { type: string } }
    Backup:
      description: "Represents the MusicArk backup resource or value object."
      type: object
      required: [id,created_at,app_version,size_bytes,artwork_files,download_url]
      additionalProperties: false
      properties:
        id: { type: string }
        created_at: { type: string, format: date-time }
        app_version: { type: string }
        size_bytes: { type: integer }
        artwork_files: { type: integer }
        download_url: { type: string }
    BackupList:
      description: "MusicArk backup collection."
      type: object
      required: [items,pending_restore]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Backup' } }
        pending_restore:
          anyOf:
            - type: object
              required: [backup_id,safety_backup_id,requested_at,restart_required]
              additionalProperties: false
              properties: { backup_id: { type: string }, safety_backup_id: { type: string }, requested_at: { type: string, format: date-time }, restart_required: { type: boolean, const: true } }
            - type: 'null'
    DeleteBackupRequest:
      description: "Request payload for the MusicArk Delete Backup operation."
      type: object
      required: [confirm_backup_delete]
      additionalProperties: false
      properties: { confirm_backup_delete: { type: boolean, const: true } }
    StageBackupRestoreResponse:
      description: "Response payload returned by the MusicArk Stage Backup Restore operation."
      type: object
      required: [staged,backup_id,safety_backup_id,restart_required]
      additionalProperties: false
      properties: { staged: { type: boolean, const: true }, backup_id: { type: string }, safety_backup_id: { type: string }, restart_required: { type: boolean, const: true } }
    RestartBackupRestoreRequest:
      description: "Request payload for the MusicArk Restart Backup Restore operation."
      type: object
      required: [confirm_restore_restart]
      additionalProperties: false
      properties: { confirm_restore_restart: { type: boolean, const: true } }
    RestartBackupRestoreResponse:
      description: "Response payload returned by the MusicArk Restart Backup Restore operation."
      type: object
      required: [restarting]
      additionalProperties: false
      properties: { restarting: { type: boolean, const: true } }
    Problem:
      description: "Represents the MusicArk problem resource or value object."
      type: object
      required: [type,title,status,code,detail]
      properties: { type: { type: string }, title: { type: string }, status: { type: integer }, code: { type: string }, detail: { type: string }, request_id: { type: string } }
    BootstrapStatus:
      description: "Represents the MusicArk bootstrap status resource or value object."
      type: object
      required: [initialized,version]
      properties: { initialized: { type: boolean }, version: { type: string } }
    BootstrapRequest:
      description: "Request payload for the MusicArk Bootstrap operation."
      type: object
      required: [username,password]
      additionalProperties: false
      properties: { username: { type: string, minLength: 2, maxLength: 64 }, password: { type: string, minLength: 10, maxLength: 256 }, locale: { type: string }, timezone: { type: string } }
    Session:
      description: "Represents the MusicArk session resource or value object."
      type: object
      required: [user,csrf_token]
      properties:
        user: { type: object, required: [id,username,role,avatar_url], properties: { id: { type: string }, username: { type: string }, role: { type: string }, avatar_url: { type: [string,'null'] } } }
        csrf_token: { type: string }
        expires_at: { type: string, format: date-time, description: Expiry of the authenticated web session. }
    User:
      description: "Represents the MusicArk user resource or value object."
      type: object
      required: [id,username,role,locale,timezone,avatar_url,disabled_at,created_at,updated_at,last_seen_at,active_session_count]
      additionalProperties: false
      properties:
        id: { type: string }
        username: { type: string, minLength: 2, maxLength: 64 }
        role: { type: string, enum: [admin,member,viewer] }
        locale: { type: string }
        timezone: { type: string }
        avatar_url: { type: [string,'null'] }
        disabled_at: { type: [string,'null'], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        last_seen_at: { type: [string,'null'], format: date-time }
        active_session_count: { type: integer, minimum: 0 }
    UserPage:
      description: "Cursor-paginated MusicArk user collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/User' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer, minimum: 0 }
    CreateUserRequest:
      description: "Request payload for the MusicArk Create User operation."
      type: object
      required: [username,password]
      additionalProperties: false
      properties:
        username: { type: string, minLength: 2, maxLength: 64 }
        password: { type: string, minLength: 10, maxLength: 256 }
        role: { type: string, enum: [admin,member], default: member }
    UpdateUserRequest:
      description: "Request payload for the MusicArk Update User operation."
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        password: { type: string, minLength: 10, maxLength: 256, description: Resets the password and revokes old sessions and API tokens. }
        role: { type: string, enum: [admin,member] }
        disabled: { type: boolean }
    UserProfile:
      description: "Represents the MusicArk user profile resource or value object."
      type: object
      required: [id,username,role,avatar_url]
      additionalProperties: false
      properties:
        id: { type: string }
        username: { type: string, minLength: 2, maxLength: 64 }
        role: { type: string, enum: [admin,member] }
        avatar_url: { type: [string,'null'] }
    UpdateOwnProfileRequest:
      description: "Request payload for the MusicArk Update Own Profile operation."
      type: object
      required: [current_password,new_password]
      additionalProperties: false
      properties:
        current_password: { type: string, description: Verifies the currently authenticated user before changing the password. }
        new_password: { type: string, minLength: 10, maxLength: 256, description: Revokes the user's other web sessions and every API token. }
    DeleteUserRequest:
      description: "Request payload for the MusicArk Delete User operation."
      type: object
      required: [confirm_user_deletion]
      additionalProperties: false
      properties:
        confirm_user_deletion: { type: boolean, const: true }
    ScanIntervalMinutes:
      description: 0 disables scheduled scans. Values from 5 through 10080 run periodic incremental scans and provide a host-event polling fallback.
      default: 0
      oneOf:
        - type: integer
          const: 0
        - type: integer
          minimum: 5
          maximum: 10080
    StorageRoot:
      description: "Represents the MusicArk storage root resource or value object."
      type: object
      required: [id,name,writable,follow_symlinks,exclude_rules,enabled,realtime_monitoring,scan_interval_minutes,created_at,updated_at]
      additionalProperties: false
      properties:
        id: { type: string }
        name: { type: string }
        container_path: { type: string, description: "Omitted from catalog:read Bearer responses; available to CookieSession and library:write clients." }
        writable: { type: boolean }
        follow_symlinks: { type: boolean }
        exclude_rules: { type: array, items: { type: string } }
        enabled: { type: boolean }
        realtime_monitoring: { type: boolean, description: "Disabled by default. Requests best-effort low-latency Linux-container events; bind-mounted host filesystems may delay or lose these events." }
        scan_interval_minutes: { $ref: '#/components/schemas/ScanIntervalMinutes' }
        last_scan_completed_at: { type: [string,'null'], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    StorageRootCandidate:
      description: "Represents the MusicArk storage root candidate resource or value object."
      type: object
      required: [container_path,suggested_name,readable,writable,already_added]
      additionalProperties: false
      properties:
        container_path: { type: string, description: Mount target visible inside the container; never the host source path. }
        suggested_name: { type: string }
        readable: { type: boolean }
        writable: { type: boolean, description: True only when both the mount and the effective filesystem permit writes. }
        already_added: { type: boolean }
        root_id: { type: string, description: Present when the candidate is already registered. }
    StorageRootCandidateBrowse:
      description: "Represents the MusicArk storage root candidate browse resource or value object."
      type: object
      required: [root_path,path,container_path,parent_path,can_go_up,directories]
      additionalProperties: false
      properties:
        root_path: { type: string }
        path: { type: string, description: Relative path inside root_path. }
        container_path: { type: string, description: Current directory path inside the container. }
        parent_path: { type: string, description: Relative parent path; empty at the mapped root. }
        can_go_up: { type: boolean }
        directories:
          type: array
          items:
            type: object
            required: [name,path]
            additionalProperties: false
            properties:
              name: { type: string }
              path: { type: string, description: Relative path inside root_path. }
    CreateRootRequest:
      description: "Request payload for the MusicArk Create Root operation."
      type: object
      required: [name,container_path]
      additionalProperties: false
      properties: { name: { type: string }, container_path: { type: string }, exclude_rules: { type: array, items: { type: string } }, follow_symlinks: { type: boolean, const: false }, writable: { type: boolean, default: false }, realtime_monitoring: { type: boolean, default: false, description: Requests best-effort container-side event monitoring. }, scan_interval_minutes: { $ref: '#/components/schemas/ScanIntervalMinutes' }, confirm_source_change: { type: boolean, description: Required and true when writable is enabled. } }
    UpdateRootRequest:
      description: "Request payload for the MusicArk Update Root operation."
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 100 }
        exclude_rules: { type: array, items: { type: string } }
        enabled: { type: boolean }
        writable: { type: boolean }
        realtime_monitoring: { type: boolean, description: Enables or disables best-effort container-side event monitoring. }
        scan_interval_minutes: { $ref: '#/components/schemas/ScanIntervalMinutes' }
        confirm_source_change: { type: boolean, description: Required and true when changing writable from false to true. }
    DeleteRootRequest:
      description: "Request payload for the MusicArk Delete Root operation."
      type: object
      required: [confirm_source_change]
      additionalProperties: false
      properties: { confirm_source_change: { type: boolean, const: true } }
    ProbeRootResponse:
      description: "Response payload returned by the MusicArk Probe Root operation."
      type: object
      required: [exists,readable,filesystem_writable,application_writable,resolved_path]
      additionalProperties: false
      properties:
        exists: { type: boolean }
        readable: { type: boolean }
        filesystem_writable: { type: boolean }
        application_writable: { type: boolean }
        resolved_path: { type: string }
    CreateFolderRequest:
      description: "Request payload for the MusicArk Create Folder operation."
      type: object
      required: [path,name,confirm_source_change]
      additionalProperties: false
      properties:
        path: { type: string, description: Parent path relative to the registered media root. }
        name: { type: string, minLength: 1, maxLength: 255 }
        confirm_source_change: { type: boolean, const: true }
    RenameFolderRequest:
      description: "Request payload for the MusicArk Rename Folder operation."
      type: object
      required: [path,name,confirm_source_change]
      additionalProperties: false
      properties:
        path: { type: string, minLength: 1, description: Existing child folder path relative to the registered media root. }
        name: { type: string, minLength: 1, maxLength: 255 }
        confirm_source_change: { type: boolean, const: true }
    MoveFolderRequest:
      description: "Request payload for the MusicArk Move Folder operation."
      type: object
      required: [path,target_parent,confirm_source_change]
      additionalProperties: false
      properties:
        path: { type: string, minLength: 1 }
        target_parent: { type: string, description: Destination parent relative to the same registered media root. }
        confirm_source_change: { type: boolean, const: true }
    DeleteFolderRequest:
      description: "Request payload for the MusicArk Delete Folder operation."
      type: object
      required: [path,confirm_source_change]
      additionalProperties: false
      properties:
        path: { type: string, minLength: 1 }
        confirm_source_change: { type: boolean, const: true }
    BrowseStorageRootResponse:
      description: "Response payload returned by the MusicArk Browse Storage Root operation."
      type: object
      required: [root_id,path,children,children_total,children_page,tracks,total,page]
      additionalProperties: false
      properties:
        root_id: { type: string }
        path: { type: string }
        children:
          type: array
          items:
            type: object
            required: [name,path,kind]
            properties:
              name: { type: string }
              path: { type: string }
              kind: { type: string, enum: [directory,file] }
              size_bytes: { type: integer }
        tracks:
          type: array
          items: { $ref: '#/components/schemas/Track' }
        total: { type: integer }
        page: { $ref: '#/components/schemas/PageInfo' }
        children_total: { type: integer }
        children_page: { $ref: '#/components/schemas/PageInfo' }
    FolderMutationResult:
      description: "Represents the MusicArk folder mutation result resource or value object."
      type: object
      additionalProperties: false
      properties:
        created: { type: boolean }
        renamed: { type: boolean }
        moved: { type: boolean }
        deleted: { type: boolean }
        root_id: { type: string }
        old_path: { type: string }
        path: { type: string }
        name: { type: string }
    CreateIndexRunRequest:
      description: "Request payload for the MusicArk Create Index Run operation."
      type: object
      required: [root_id]
      additionalProperties: false
      properties: { root_id: { type: string }, mode: { type: string, enum: [incremental,full,force], default: incremental, description: "incremental traverses the root and skips unchanged local file contexts; full re-extracts every candidate; force is retained for compatibility and currently behaves like full. Realtime monitoring creates incremental runs." } }
    IndexRunAccepted:
      description: "Represents the MusicArk index run accepted resource or value object."
      type: object
      required: [operation,index_run_id]
      additionalProperties: false
      properties:
        operation:
          type: object
          required: [id,type,status,created_at]
          additionalProperties: false
          properties: { id: { type: string }, type: { type: string, const: catalog_index }, status: { type: string, const: queued }, created_at: { type: string, format: date-time } }
        index_run_id: { type: string }
    IndexRun:
      description: "Represents the MusicArk index run resource or value object."
      type: object
      required: [id,operation_id,root_id,mode,status,phase,counts,current_relative_path,created_at,started_at,finished_at,updated_at]
      additionalProperties: false
      properties:
        id: { type: string }
        operation_id: { type: string }
        root_id: { type: string }
        mode: { type: string, enum: [incremental,full,force] }
        status: { type: string }
        phase: { type: string }
        counts:
          type: object
          required: [discovered,processed,added,updated,unchanged,missing,failed]
          additionalProperties: false
          properties: { discovered: { type: integer }, processed: { type: integer }, added: { type: integer }, updated: { type: integer }, unchanged: { type: integer }, missing: { type: integer }, failed: { type: integer } }
        current_relative_path: { type: [string,'null'] }
        created_at: { type: string, format: date-time }
        started_at: { type: [string,'null'], format: date-time }
        finished_at: { type: [string,'null'], format: date-time }
        updated_at: { type: string, format: date-time }
    MissingFileTrack:
      description: "Represents the MusicArk missing file track resource or value object."
      type: object
      required: [id,title,display_artist,artwork_url]
      additionalProperties: false
      properties:
        id: { type: string }
        title: { type: string }
        display_artist: { type: string }
        artwork_url: { type: [string,'null'] }
    IndexRunMissingFile:
      description: "Represents the MusicArk index run missing file resource or value object."
      type: object
      required: [id,media_file_id,relative_path,size_bytes,status,tracks,detected_at,removed_at]
      additionalProperties: false
      properties:
        id: { type: string }
        media_file_id: { type: [string,'null'] }
        relative_path: { type: string }
        size_bytes: { type: integer }
        status: { type: string }
        tracks: { type: array, items: { $ref: '#/components/schemas/MissingFileTrack' } }
        detected_at: { type: string, format: date-time }
        removed_at: { type: [string,'null'], format: date-time }
    PendingMissingFilesResponse:
      description: The newest scan snapshot with unresolved missing media records, or null when no action is pending.
      type: object
      required: [run_id,current_missing_count]
      additionalProperties: false
      properties:
        run_id: { type: [string,'null'] }
        current_missing_count: { type: integer, minimum: 0 }
    IndexRunMissingFilesResponse:
      description: "Response payload returned by the MusicArk Index Run Missing Files operation."
      type: object
      required: [run_id,root_id,root_name,detected_count,current_missing_count,removed_count,has_more,items]
      additionalProperties: false
      properties:
        run_id: { type: string }
        root_id: { type: string }
        root_name: { type: string }
        detected_count: { type: integer }
        current_missing_count: { type: integer }
        removed_count: { type: integer }
        has_more: { type: boolean }
        items: { type: array, items: { $ref: '#/components/schemas/IndexRunMissingFile' } }
    MissingFilesCleanupRequest:
      description: "Request payload for the MusicArk Missing Files Cleanup operation."
      type: object
      required: [confirm_library_removal]
      additionalProperties: false
      properties:
        confirm_library_removal: { type: boolean, const: true }
    MissingFilesCleanupResponse:
      description: "Response payload returned by the MusicArk Missing Files Cleanup operation."
      type: object
      required: [removed_files,removed_tracks,skipped_files]
      additionalProperties: false
      properties:
        removed_files: { type: integer }
        removed_tracks: { type: integer }
        skipped_files: { type: integer }
    CreateDuplicateAnalysisRunRequest:
      description: "Request payload for the MusicArk Create Duplicate Analysis Run operation."
      type: object
      required: [mode]
      additionalProperties: false
      properties: { mode: { type: string, enum: [incremental,retry_failed,rebuild] } }
    DuplicateAnalysisAccepted:
      description: "Represents the MusicArk duplicate analysis accepted resource or value object."
      type: object
      required: [operation]
      additionalProperties: false
      properties:
        operation:
          type: object
          required: [id,type,status,phase]
          additionalProperties: false
          properties: { id: { type: string }, type: { type: string, const: duplicate_analysis }, status: { type: string, const: queued }, phase: { type: string, const: queued } }
    DuplicateAnalysisState:
      description: "Represents the MusicArk duplicate analysis state resource or value object."
      type: object
      required: [chromaprint_available,eligible_files,fingerprinted_files,failed_files,skipped_multi_track_files,active_operation,last_completed_at]
      additionalProperties: false
      properties:
        chromaprint_available: { type: boolean }
        eligible_files: { type: integer }
        fingerprinted_files: { type: integer }
        failed_files: { type: integer }
        skipped_multi_track_files: { type: integer }
        active_operation:
          anyOf:
            - type: object
              required: [id,status,phase,progress_current,progress_total]
              properties: { id: { type: string }, status: { type: string }, phase: { type: string }, progress_current: { type: integer }, progress_total: { type: integer } }
            - type: 'null'
        last_completed_at: { type: [string,'null'], format: date-time }
    DuplicateGroupItem:
      description: "Represents the MusicArk duplicate group item resource or value object."
      type: object
      required: [track_id,title,display_artist,album,artwork_url,format,codec,duration_ms,size_bytes,bitrate_bps,sample_rate_hz,bit_depth,channels,root_name,relative_path,file_version,writable,exact_cluster_id,recommended_keep,added_at]
      additionalProperties: false
      properties:
        track_id: { type: string }
        title: { type: string }
        display_artist: { type: string }
        album: { type: string }
        artwork_url: { type: [string,'null'] }
        format: { type: string }
        codec: { type: string }
        duration_ms: { type: integer }
        size_bytes: { type: integer }
        bitrate_bps: { type: [integer,'null'] }
        sample_rate_hz: { type: [integer,'null'] }
        bit_depth: { type: [integer,'null'] }
        channels: { type: [integer,'null'] }
        root_name: { type: string }
        relative_path: { type: string }
        file_version: { type: string }
        writable: { type: boolean }
        exact_cluster_id: { type: [string,'null'] }
        recommended_keep: { type: boolean }
        added_at: { type: string, format: date-time }
    DuplicateGroup:
      description: "Represents the MusicArk duplicate group resource or value object."
      type: object
      required: [id,match_type,recommended_keep_track_id,recommendation,reclaimable_bytes,items]
      additionalProperties: false
      properties:
        id: { type: string }
        match_type: { type: string, enum: [exact,acoustic] }
        recommended_keep_track_id: { type: string }
        recommendation: { type: string }
        reclaimable_bytes: { type: integer }
        items: { type: array, minItems: 2, items: { $ref: '#/components/schemas/DuplicateGroupItem' } }
    DuplicateGroupsResponse:
      description: "Response payload returned by the MusicArk Duplicate Groups operation."
      type: object
      required: [analysis,summary,groups]
      additionalProperties: false
      properties:
        analysis: { $ref: '#/components/schemas/DuplicateAnalysisState' }
        summary:
          type: object
          required: [total_groups,redundant_files,reclaimable_bytes]
          additionalProperties: false
          properties: { total_groups: { type: integer }, redundant_files: { type: integer }, reclaimable_bytes: { type: integer } }
        groups: { type: array, items: { $ref: '#/components/schemas/DuplicateGroup' } }
    DuplicateCleanupRequest:
      description: "Request payload for the MusicArk Duplicate Cleanup operation."
      type: object
      required: [confirm_source_change,groups]
      additionalProperties: false
      properties:
        confirm_source_change: { type: boolean, const: true }
        groups:
          type: array
          minItems: 1
          items:
            type: object
            required: [group_id,keep_track_id,delete]
            additionalProperties: false
            properties:
              group_id: { type: string }
              keep_track_id: { type: string }
              delete:
                type: array
                minItems: 1
                items:
                  type: object
                  required: [track_id,expected_file_version]
                  additionalProperties: false
                  properties: { track_id: { type: string }, expected_file_version: { type: string } }
    DuplicateCleanupResponse:
      description: "Response payload returned by the MusicArk Duplicate Cleanup operation."
      type: object
      required: [status,recycled_files,affected_tracks,held_bytes,items]
      additionalProperties: false
      properties:
        status: { type: string, enum: [completed,completed_with_errors] }
        recycled_files: { type: integer }
        affected_tracks: { type: integer }
        held_bytes: { type: integer }
        items:
          type: array
          items:
            type: object
            required: [track_id,relative_path,status]
            properties: { track_id: { type: string }, relative_path: { type: string }, status: { type: string, enum: [completed,failed,reconcile_required] }, code: { type: string }, audit_id: { type: string }, recycle_id: { type: string } }
    Track:
      description: "Represents the MusicArk track resource or value object."
      type: object
      required: [id,title,display_artist,artist_names,release,track_number,track_total,disc_number,disc_total,year,composers,genres,style,language,release_date,duration_ms,format,size_bytes,artwork_url,availability,added_at]
      properties:
        id: { type: string }
        title: { type: string }
        display_artist: { type: string }
        artist_names: { type: array, items: { type: string } }
        release:
          anyOf:
            - type: object
              required: [id,title,artwork_url]
              properties:
                id: { type: string }
                title: { type: string }
                artwork_url: { type: [string,'null'] }
            - type: 'null'
        track_number: { type: [integer,'null'] }
        track_total: { type: [integer,'null'] }
        disc_number: { type: [integer,'null'] }
        disc_total: { type: [integer,'null'] }
        year: { type: [integer,'null'], minimum: 1000, description: Valid values do not exceed the current UTC year plus one. }
        composers: { type: array, items: { type: string } }
        genres: { type: array, description: Effective genre values., items: { type: string } }
        style: { type: string, description: Effective explicit style value. Empty means unknown; MusicArk does not infer style from genres. }
        language: { type: string, description: Effective explicit track language. Empty means unknown. }
        release_date: { type: string }
        duration_ms: { type: integer }
        format: { type: string }
        codec: { type: string }
        size_bytes: { type: integer }
        artwork_url: { type: [string,'null'] }
        availability: { type: string }
        added_at: { type: string, format: date-time }
    UpdateTrackRequest:
      description: "Request payload for the MusicArk Update Track operation."
      type: object
      required: [title,artists,album,track_number,year]
      additionalProperties: false
      properties:
        title: { type: string, minLength: 1, maxLength: 500 }
        artists: { type: string, minLength: 1, maxLength: 2000 }
        album: { type: string, maxLength: 500 }
        track_number: { type: [integer,'null'], minimum: 1, maximum: 999 }
        track_total: { type: [integer,'null'], minimum: 1, maximum: 999 }
        disc_number: { type: [integer,'null'], minimum: 1, maximum: 999 }
        disc_total: { type: [integer,'null'], minimum: 1, maximum: 999 }
        year: { type: [integer,'null'], minimum: 1000, description: Valid values do not exceed the current UTC year plus one; out-of-range values return 422. }
        composers: { type: string, maxLength: 2000 }
        genres: { type: string, maxLength: 2000 }
        language: { type: string, maxLength: 50 }
        release_date: { type: string, pattern: '^$|^[0-9]{4}-[0-9]{2}-[0-9]{2}$' }
    BatchTrackUpdate:
      description: "Represents the MusicArk batch track update resource or value object."
      allOf:
        - { $ref: '#/components/schemas/UpdateTrackRequest' }
        - type: object
          required: [id]
          properties: { id: { type: string } }
    BatchUpdateTracksRequest:
      description: "Request payload for the MusicArk Batch Update Tracks operation."
      type: object
      required: [items]
      additionalProperties: false
      properties: { items: { type: array, minItems: 1, maxItems: 100, items: { $ref: '#/components/schemas/BatchTrackUpdate' } } }
    UpdateReleaseRequest:
      description: "Request payload for the MusicArk Update Release operation."
      type: object
      required: [title,description]
      additionalProperties: false
      properties:
        title: { type: string, minLength: 1, maxLength: 500 }
        description: { type: string, maxLength: 2000 }
    UpdateArtistRequest:
      description: "Request payload for the MusicArk Update Artist operation."
      type: object
      required: [name,description]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 500 }
        description: { type: string, maxLength: 2000 }
    BatchArtistUpdate:
      description: "Represents the MusicArk batch artist update resource or value object."
      allOf:
        - { $ref: '#/components/schemas/UpdateArtistRequest' }
        - type: object
          required: [id]
          properties: { id: { type: string } }
    BatchUpdateArtistsRequest:
      description: "Request payload for the MusicArk Batch Update Artists operation."
      type: object
      required: [items]
      additionalProperties: false
      properties: { items: { type: array, minItems: 1, maxItems: 100, items: { $ref: '#/components/schemas/BatchArtistUpdate' } } }
    BatchReleaseUpdate:
      description: "Represents the MusicArk batch release update resource or value object."
      allOf:
        - { $ref: '#/components/schemas/UpdateReleaseRequest' }
        - type: object
          required: [id]
          properties: { id: { type: string } }
    BatchUpdateReleasesRequest:
      description: "Request payload for the MusicArk Batch Update Releases operation."
      type: object
      required: [items]
      additionalProperties: false
      properties: { items: { type: array, minItems: 1, maxItems: 100, items: { $ref: '#/components/schemas/BatchReleaseUpdate' } } }
    MergeCatalogRequest:
      description: "Request payload for the MusicArk Merge Catalog operation."
      type: object
      required: [target_id]
      additionalProperties: false
      properties: { target_id: { type: string, description: Existing target artist or release ID. } }
    MergeCatalogResult:
      description: "Represents the MusicArk merge catalog result resource or value object."
      type: object
      required: [source_id,target_id]
      additionalProperties: false
      properties:
        source_id: { type: string }
        target_id: { type: string }
        dry_run: { type: boolean }
        merged: { type: boolean }
        source_tracks: { type: integer }
        source_track_associations: { type: integer }
        source_releases: { type: integer }
        source_release_associations: { type: integer }
        source_artist_associations: { type: integer }
        track_conflicts: { type: integer }
        release_conflicts: { type: integer }
        artist_conflicts: { type: integer }
        favorite_conflicts: { type: integer }
    Release:
      description: "Represents the MusicArk release resource or value object."
      type: object
      required: [id,title,year,artwork_url,display_artist,track_count,duration_ms,added_at]
      properties:
        id: { type: string }
        title: { type: string }
        year: { type: [integer,'null'], minimum: 1000, description: Valid values do not exceed the current UTC year plus one. }
        artwork_url: { type: [string,'null'] }
        display_artist: { type: string }
        track_count: { type: integer }
        duration_ms: { type: integer }
        added_at: { type: string, format: date-time }
    CatalogFacetItem:
      description: "Represents the MusicArk catalog facet item resource or value object."
      type: object
      required: [value,label,count,artwork_url]
      additionalProperties: false
      properties:
        value: { type: string }
        label: { type: string }
        count: { type: integer, minimum: 1 }
        artwork_url: { type: [string,'null'] }
    CatalogFacets:
      description: "Represents the MusicArk catalog facets resource or value object."
      type: object
      required: [genres,styles,languages,years,decades,formats]
      additionalProperties: false
      properties:
        genres: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
        styles: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
        languages: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
        years: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
        decades: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
        formats: { type: array, items: { $ref: '#/components/schemas/CatalogFacetItem' } }
    Playlist:
      description: "Represents the MusicArk playlist resource or value object."
      type: object
      required: [id,name,description,track_count,duration_ms,artwork_url,has_custom_artwork,created_at,updated_at]
      properties:
        id: { type: string }
        name: { type: string }
        description: { type: string }
        track_count: { type: integer }
        duration_ms: { type: integer }
        artwork_url: { type: [string,'null'] }
        has_custom_artwork: { type: boolean }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    PlaylistPage:
      description: "Cursor-paginated MusicArk playlist collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Playlist' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    CreatePlaylistRequest:
      description: "Request payload for the MusicArk Create Playlist operation."
      type: object
      required: [name]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 120 }
        description: { type: string, maxLength: 500 }
    UpdatePlaylistRequest:
      description: "Request payload for the MusicArk Update Playlist operation."
      type: object
      required: [name,description]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 120 }
        description: { type: string, maxLength: 500 }
    PlaylistDetail:
      description: "Represents the MusicArk playlist detail resource or value object."
      type: object
      required: [playlist,tracks,page,total]
      additionalProperties: false
      properties:
        playlist: { $ref: '#/components/schemas/Playlist' }
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    AddPlaylistTracksRequest:
      type: object
      additionalProperties: false
      description: Use track_id for one backwards-compatible add, or track_ids for an atomic bulk add of up to 500 unique tracks.
      properties:
        track_id: { type: string }
        track_ids: { type: array, minItems: 1, maxItems: 500, uniqueItems: true, items: { type: string, minLength: 1 } }
      oneOf: [{ required: [track_id] }, { required: [track_ids] }]
    DeletePlaylistTracksRequest:
      description: "Request payload for the MusicArk Delete Playlist Tracks operation."
      type: object
      required: [track_ids]
      additionalProperties: false
      properties: { track_ids: { type: array, minItems: 1, maxItems: 500, uniqueItems: true, items: { type: string, minLength: 1 } } }
    ReorderPlaylistTracksRequest:
      description: "Request payload for the MusicArk Reorder Playlist Tracks operation."
      type: object
      required: [track_ids]
      additionalProperties: false
      properties: { track_ids: { type: array, maxItems: 10000, uniqueItems: true, items: { type: string, minLength: 1 } } }
    PlaylistTrackMutationResult:
      description: "Represents the MusicArk playlist track mutation result resource or value object."
      type: object
      additionalProperties: false
      properties:
        added: { type: integer }
        already_present: { type: integer }
        removed: { type: integer }
        not_present: { type: integer }
    BatchMutationResult:
      description: "Represents the MusicArk batch mutation result resource or value object."
      type: object
      required: [updated]
      additionalProperties: false
      properties: { updated: { type: integer } }
    DeleteReleaseResult:
      description: "Represents the MusicArk delete release result resource or value object."
      type: object
      required: [deleted,detached_tracks]
      additionalProperties: false
      properties: { deleted: { type: boolean, const: true }, detached_tracks: { type: integer } }
    DeleteTrackSourceRequest:
      description: "Request payload for the MusicArk Delete Track Source operation."
      type: object
      required: [confirm_source_change,expected_file_version]
      additionalProperties: false
      properties:
        confirm_source_change: { type: boolean, const: true }
        expected_file_version: { type: string, minLength: 3 }
    ConfirmedTrackSourceRequest:
      description: "Request payload for the MusicArk Confirmed Track Source operation."
      type: object
      required: [confirm_source_change,expected_file_version]
      additionalProperties: false
      properties:
        confirm_source_change: { type: boolean, const: true }
        expected_file_version: { type: string, minLength: 3 }
    MoveTrackSourceRequest:
      description: "Request payload for the MusicArk Move Track Source operation."
      type: object
      required: [confirm_source_change,expected_file_version]
      additionalProperties: false
      properties:
        confirm_source_change: { type: boolean, const: true }
        expected_file_version: { type: string, minLength: 3 }
        target_parent: { type: string, maxLength: 4096, description: Relative target folder used when target_relative_path is omitted. }
        target_root_id: { type: string, description: Optional writable destination media root; defaults to the source root. }
        target_relative_path: { type: string, maxLength: 4096, description: Explicit destination path including filename and extension. }
        create_directories: { type: boolean, default: false }
    BatchWriteTrackTagsRequest:
      description: "Request payload for the MusicArk Batch Write Track Tags operation."
      type: object
      required: [confirm_source_change,items]
      additionalProperties: false
      properties:
        confirm_source_change: { type: boolean, const: true }
        items:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            required: [track_id,expected_file_version]
            additionalProperties: false
            properties: { track_id: { type: string }, expected_file_version: { type: string } }
    WriteTrackTagsResponse:
      description: "Response payload returned by the MusicArk Write Track Tags operation."
      type: object
      required: [written,relative_path,file_version,audit_id]
      additionalProperties: false
      properties:
        written: { type: boolean, const: true }
        relative_path: { type: string }
        file_version: { type: string }
        audit_id: { type: string }
    BatchWriteTrackTagsResponse:
      description: "Response payload returned by the MusicArk Batch Write Track Tags operation."
      type: object
      required: [status,completed,failed,items]
      additionalProperties: false
      properties:
        status: { type: string, enum: [completed,completed_with_errors] }
        completed: { type: integer }
        failed: { type: integer }
        items:
          type: array
          items:
            type: object
            required: [track_id,status]
            properties: { track_id: { type: string }, status: { type: string, enum: [completed,failed] }, relative_path: { type: string }, file_version: { type: string }, audit_id: { type: string }, code: { type: string }, detail: { type: string } }
    OrganizerPreviewRequest:
      description: "Request payload for the MusicArk Organizer Preview operation."
      type: object
      additionalProperties: false
      properties:
        track_ids: { type: array, minItems: 1, maxItems: 100, items: { type: string } }
        target_root_id: { type: string }
        template: { type: string, maxLength: 2000, default: '{artist}/{album}/{disc}-{track} {title}.{ext}' }
    OrganizerPreviewItem:
      description: "Represents the MusicArk organizer preview item resource or value object."
      type: object
      required: [track_id,current_root_id,current_relative_path,target_root_id,target_relative_path,expected_file_version,conflict]
      additionalProperties: false
      properties:
        track_id: { type: string }
        current_root_id: { type: string }
        current_relative_path: { type: string }
        target_root_id: { type: string }
        target_relative_path: { type: string }
        expected_file_version: { type: string }
        conflict: { type: boolean }
    BatchOrganizerPreviewResponse:
      description: "Response payload returned by the MusicArk Batch Organizer Preview operation."
      type: object
      required: [items,template]
      additionalProperties: false
      properties: { items: { type: array, items: { $ref: '#/components/schemas/OrganizerPreviewItem' } }, template: { type: string } }
    MoveTrackSourceResponse:
      description: "Response payload returned by the MusicArk Move Track Source operation."
      type: object
      required: [moved,old_root_id,root_id,old_relative_path,relative_path,file_version,audit_id]
      additionalProperties: false
      properties:
        moved: { type: boolean, const: true }
        old_root_id: { type: string }
        root_id: { type: string }
        old_relative_path: { type: string }
        relative_path: { type: string }
        file_version: { type: string }
        audit_id: { type: string }
    DeleteTrackSourceResponse:
      description: "Response payload returned by the MusicArk Delete Track Source operation."
      type: object
      required: [recycled,recycle_id,relative_path,affected_tracks,audit_id]
      additionalProperties: false
      properties:
        recycled: { type: boolean, const: true }
        recycle_id: { type: string }
        relative_path: { type: string }
        affected_tracks: { type: integer }
        audit_id: { type: string }
    Artist:
      description: "Represents the MusicArk artist resource or value object."
      type: object
      required: [id,name,artwork_url,track_count,release_count]
      properties:
        id: { type: string }
        name: { type: string }
        artwork_url: { type: [string,'null'] }
        track_count: { type: integer }
        release_count: { type: integer }
    Favorites:
      description: "Represents the MusicArk favorites resource or value object."
      type: object
      required: [tracks,albums,artists,counts,pages]
      additionalProperties: false
      properties:
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        albums: { type: array, items: { $ref: '#/components/schemas/Release' } }
        artists: { type: array, items: { $ref: '#/components/schemas/Artist' } }
        counts:
          type: object
          required: [tracks,albums,artists]
          properties: { tracks: { type: integer }, albums: { type: integer }, artists: { type: integer } }
        pages:
          type: object
          required: [tracks,albums,artists]
          additionalProperties: false
          properties: { tracks: { $ref: '#/components/schemas/PageInfo' }, albums: { $ref: '#/components/schemas/PageInfo' }, artists: { $ref: '#/components/schemas/PageInfo' } }
    FavoriteTrackPage:
      description: "Cursor-paginated MusicArk favorite track collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties: { items: { type: array, items: { $ref: '#/components/schemas/Track' } }, page: { $ref: '#/components/schemas/PageInfo' }, total: { type: integer } }
    FavoriteReleasePage:
      description: "Cursor-paginated MusicArk favorite release collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties: { items: { type: array, items: { $ref: '#/components/schemas/Release' } }, page: { $ref: '#/components/schemas/PageInfo' }, total: { type: integer } }
    FavoriteArtistPage:
      description: "Cursor-paginated MusicArk favorite artist collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties: { items: { type: array, items: { $ref: '#/components/schemas/Artist' } }, page: { $ref: '#/components/schemas/PageInfo' }, total: { type: integer } }
    TrackDetail:
      description: "Represents the MusicArk track detail resource or value object."
      type: object
      required: [track,has_custom_artwork,inherited_artwork_url,technical,source,capabilities]
      properties:
        track: { $ref: '#/components/schemas/Track' }
        has_custom_artwork: { type: boolean }
        inherited_artwork_url:
          description: "Effective artwork URL after removing the track-level custom artwork; null when no inherited artwork is available."
          type: [string,'null']
        technical:
          type: object
          required: [sample_rate_hz,bit_depth,channels,bitrate_bps]
          properties:
            sample_rate_hz: { type: [integer,'null'] }
            bit_depth: { type: [integer,'null'] }
            channels: { type: [integer,'null'] }
            bitrate_bps: { type: [integer,'null'] }
        source:
          type: object
          required: [root_name,relative_path,file_version,raw_tags]
          properties:
            root_name: { type: string }
            relative_path: { type: string }
            file_version: { type: string }
            raw_tags: {}
            is_cue: { type: boolean }
            cue_source: { type: [string,'null'] }
            cue_index: { type: [integer,'null'] }
            cue_start_ms: { type: [integer,'null'] }
            cue_end_ms: { type: [integer,'null'] }
        capabilities:
          type: object
          required: [can_play,can_edit_metadata,can_write_tags,can_move_file,can_delete_file]
          properties:
            can_play: { type: boolean }
            can_edit_metadata: { type: boolean }
            can_write_tags: { type: boolean }
            can_move_file: { type: boolean }
            can_delete_file: { type: boolean }
    ReleaseDetail:
      description: "Represents the MusicArk release detail resource or value object."
      type: object
      required: [release,tracks,page,total]
      additionalProperties: false
      properties:
        release:
          type: object
          required: [id,title,description,display_artist,year,artwork_url,inherited_artwork_url,has_custom_artwork,updated_at]
          properties:
            id: { type: string }
            title: { type: string }
            description: { type: string }
            display_artist: { type: string }
            year: { type: [integer,'null'], minimum: 1000, description: Valid values do not exceed the current UTC year plus one. }
            artwork_url: { type: [string,'null'] }
            inherited_artwork_url: { type: [string,'null'], description: "Effective artwork URL after removing the release-level custom artwork." }
            has_custom_artwork: { type: boolean }
            updated_at: { type: string, format: date-time }
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    ArtistDetail:
      description: "Represents the MusicArk artist detail resource or value object."
      type: object
      required: [artist,tracks,page,total]
      additionalProperties: false
      properties:
        artist:
          type: object
          required: [id,name,description,artwork_url,inherited_artwork_url,has_custom_artwork,updated_at,track_count,release_count]
          properties: { id: { type: string }, name: { type: string }, description: { type: string }, artwork_url: { type: [string,'null'] }, inherited_artwork_url: { type: [string,'null'], description: "Effective artwork URL after removing the artist-level custom artwork." }, has_custom_artwork: { type: boolean }, updated_at: { type: string, format: date-time }, track_count: { type: integer }, release_count: { type: integer } }
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    SearchResults:
      description: "Represents the MusicArk search results resource or value object."
      type: object
      required: [tracks,releases,artists,playlists,totals]
      properties:
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        releases:
          type: array
          items:
            type: object
            required: [id,title,artwork_url]
            properties: { id: { type: string }, title: { type: string }, artwork_url: { type: [string,'null'] } }
        artists:
          type: array
          items:
            type: object
            required: [id,name,artwork_url]
            properties: { id: { type: string }, name: { type: string }, artwork_url: { type: [string,'null'] } }
        playlists: { type: array, items: { $ref: '#/components/schemas/Playlist' } }
        totals:
          type: object
          required: [tracks,releases,artists,playlists]
          properties:
            tracks: { type: integer }
            releases: { type: integer }
            artists: { type: integer }
            playlists: { type: integer }
    LyricsSummary:
      description: "Represents the MusicArk lyrics summary resource or value object."
      type: object
      required: [id,kind,language,source,source_name,is_default,parse_status,created_at]
      properties:
        id: { type: string }
        kind: { type: string }
        language: { type: [string,'null'] }
        source: { type: string }
        source_name: { type: [string,'null'] }
        is_default: { type: boolean }
        parse_status: { type: string }
        created_at: { type: string, format: date-time }
    LyricsSummaryList:
      description: "MusicArk lyrics summary collection."
      type: object
      required: [items]
      properties: { items: { type: array, items: { $ref: '#/components/schemas/LyricsSummary' } } }
    LyricsMutationRequest:
      description: "Request payload for the MusicArk Lyrics Mutation operation."
      type: object
      required: [content]
      additionalProperties: false
      properties:
        content: { type: string, minLength: 1, maxLength: 2097152 }
        language: { type: string, maxLength: 50 }
        source_name: { type: string, maxLength: 200 }
        is_default: { type: boolean, default: false }
    LyricsDocument:
      description: "Represents the MusicArk lyrics document resource or value object."
      type: object
      required: [id,track_id,kind,content,timeline,language,source,source_name,is_default,parse_status,created_at,updated_at]
      properties:
        id: { type: string }
        track_id: { type: string }
        kind: { type: string }
        content: { type: string }
        timeline: { type: array, items: {} }
        language: { type: [string,'null'] }
        source: { type: string }
        source_name: { type: [string,'null'] }
        is_default: { type: boolean }
        parse_status: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    Operation:
      description: "Represents the MusicArk operation resource or value object."
      type: object
      required: [id,type,status,phase,progress_current,progress_total,summary,index_run_id,enrichment_run_id,root_id,root_name,mode,trigger_source,requested_by,issues,created_at,updated_at]
      properties:
        id: { type: string }
        type: { type: string }
        status: { type: string }
        phase: { type: string }
        progress_current: { type: integer }
        progress_total: { type: integer }
        summary: { type: object, additionalProperties: true }
        index_run_id: { type: [string,'null'] }
        enrichment_run_id: { type: [string,'null'] }
        root_id: { type: [string,'null'] }
        root_name: { type: [string,'null'] }
        mode: { type: string }
        trigger_source: { type: string }
        requested_by: { type: [string,'null'] }
        issues: { type: array, items: { $ref: '#/components/schemas/IndexIssue' } }
        created_at: { type: string, format: date-time }
        started_at: { type: [string,'null'], format: date-time }
        finished_at: { type: [string,'null'], format: date-time }
        updated_at: { type: string, format: date-time }
    OperationPage:
      description: "Cursor-paginated MusicArk operation collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Operation' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    IndexIssue:
      description: "Represents the MusicArk index issue resource or value object."
      type: object
      required: [id,relative_path,code,message,retryable,created_at]
      properties:
        id: { type: string }
        relative_path: { type: string }
        code: { type: string }
        message: { type: string }
        retryable: { type: boolean }
        created_at: { type: string, format: date-time }
    IndexIssueList:
      description: "MusicArk index issue collection."
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/IndexIssue' } }
    PageInfo:
      description: "Represents the MusicArk page info resource or value object."
      type: object
      required: [limit,next_cursor,has_more]
      properties:
        limit: { type: integer }
        next_cursor: { type: [string,'null'] }
        has_more: { type: boolean }
    TrackPage:
      description: "Cursor-paginated MusicArk track collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Track' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    HomeTrack:
      description: "Represents the MusicArk home track resource or value object."
      type: object
      required: [track,played_at,play_count]
      properties:
        track: { $ref: '#/components/schemas/Track' }
        played_at: { type: [string,'null'], format: date-time }
        play_count: { type: integer, minimum: 0 }
        reason: { type: string }
    HomeArtist:
      description: "Represents the MusicArk home artist resource or value object."
      type: object
      required: [artist,played_at,play_count,reason]
      properties:
        artist: { $ref: '#/components/schemas/Artist' }
        played_at: { type: [string,'null'], format: date-time }
        play_count: { type: integer, minimum: 0 }
        reason: { type: string }
    HomeRelease:
      description: "Represents the MusicArk home release resource or value object."
      type: object
      required: [id,title,artwork_url]
      properties:
        id: { type: string }
        title: { type: string }
        artwork_url: { type: [string,'null'] }
    HomeSections:
      description: "Represents the MusicArk home sections resource or value object."
      type: object
      required: [recently_played,recommended,most_played,recommended_artists,cover_flow,daily_mix,favorite_tracks,recent_releases,library_track_count,generated_at,refresh_after,section_errors]
      properties:
        recently_played: { type: array, items: { $ref: '#/components/schemas/HomeTrack' } }
        recommended: { type: array, items: { $ref: '#/components/schemas/HomeTrack' } }
        most_played: { type: array, items: { $ref: '#/components/schemas/HomeTrack' } }
        recommended_artists: { type: array, items: { $ref: '#/components/schemas/HomeArtist' } }
        cover_flow: { type: array, items: { $ref: '#/components/schemas/HomeRelease' } }
        daily_mix: { type: array, items: { $ref: '#/components/schemas/Track' } }
        favorite_tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        recent_releases: { type: array, items: { $ref: '#/components/schemas/Release' } }
        library_track_count: { type: integer, minimum: 0 }
        generated_at: { type: string, format: date-time }
        refresh_after: { type: string, format: date-time }
        section_errors: { type: object, additionalProperties: { type: string } }
    HomeTrackPage:
      description: "Cursor-paginated MusicArk home track collection."
      type: object
      required: [items,page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/HomeTrack' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    HomeArtistPage:
      description: "Cursor-paginated MusicArk home artist collection."
      type: object
      required: [items,page]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/HomeArtist' } }
        page: { $ref: '#/components/schemas/PageInfo' }
    PlaybackQueue:
      description: "Represents the MusicArk playback queue resource or value object."
      type: object
      required: [tracks,current_index,volume,repeat_mode,updated_at]
      properties:
        tracks: { type: array, items: { $ref: '#/components/schemas/Track' } }
        current_index: { type: integer, minimum: -1 }
        volume: { type: number, minimum: 0, maximum: 1 }
        repeat_mode: { type: string, enum: [off,all,one,shuffle] }
        updated_at: { type: [string,'null'], format: date-time }
    PlaybackQueueInput:
      description: "Input payload for MusicArk playback queue."
      type: object
      required: [track_ids,current_index,volume,repeat_mode]
      additionalProperties: false
      properties:
        track_ids: { type: array, maxItems: 10000, items: { type: string } }
        current_index: { type: integer, minimum: -1 }
        volume: { type: number, minimum: 0, maximum: 1 }
        repeat_mode: { type: string, enum: [off,all,one,shuffle] }
    PersonalPlaybackProgress:
      description: "Represents the MusicArk personal playback progress resource or value object."
      type: object
      required: [track,playback_id,device_id,position_ms,duration_ms,state,source,occurred_at,updated_at]
      additionalProperties: false
      properties:
        track: { $ref: '#/components/schemas/Track' }
        playback_id: { type: string }
        device_id: { type: string }
        position_ms: { type: integer, minimum: 0 }
        duration_ms: { type: integer, minimum: 0 }
        state: { type: string, enum: [playing,paused,stopped,completed] }
        source: { type: string }
        occurred_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    PersonalPlaybackProgressList:
      description: "MusicArk personal playback progress collection."
      type: object
      required: [items]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/PersonalPlaybackProgress' } }
    PlaybackHistoryItem:
      description: "Represents the MusicArk playback history item resource or value object."
      type: object
      required: [id,track,played_at,source,device_id,position_ms,duration_ms]
      additionalProperties: false
      properties:
        id: { type: string }
        track: { $ref: '#/components/schemas/Track' }
        played_at: { type: string, format: date-time }
        source: { type: string }
        device_id: { type: [string,'null'] }
        position_ms: { type: [integer,'null'], minimum: 0 }
        duration_ms: { type: [integer,'null'], minimum: 0 }
    PlaybackHistoryPage:
      description: "Cursor-paginated MusicArk playback history collection."
      type: object
      required: [items,page,total]
      additionalProperties: false
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/PlaybackHistoryItem' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer, minimum: 0 }
    CreateAPITokenRequest:
      description: "Request payload for the MusicArk Create APIToken operation."
      type: object
      required: [name]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 80 }
        expires_in_days: { type: integer, minimum: 0, maximum: 3650, default: 0, description: Zero creates a token without an expiry. }
        scopes:
          type: array
          uniqueItems: true
          description: Defaults to every player scope when omitted.
          items: { $ref: '#/components/schemas/PlayerScope' }
    UpdateAPITokenRequest:
      description: "Request payload for the MusicArk Update APIToken operation."
      type: object
      required: [scopes]
      additionalProperties: false
      properties:
        scopes: { type: array, minItems: 1, uniqueItems: true, items: { $ref: '#/components/schemas/PlayerScope' } }
    PlayerLoginRequest:
      description: Username-and-password credentials used to establish or rotate one third-party player authorization per account and device.
      type: object
      required: [username,password]
      additionalProperties: false
      properties:
        username: { type: string, minLength: 1, maxLength: 64 }
        password: { type: string, minLength: 1, maxLength: 256, format: password }
        client_name: { type: string, maxLength: 64, description: Optional user-visible player or device name; User-Agent is used when omitted. }
        device_id: { type: string, maxLength: 128, description: Optional stable opaque identifier generated and persisted by the client. Reuse the same value on that device so repeated login rotates one token. When omitted, normalized client_name or User-Agent is used and equal names are treated as the same device. }
    PlayerSession:
      description: Non-expiring third-party player authorization created or rotated from valid MusicArk account credentials. Repeated login for the same account and device preserves token_id but replaces access_token, so only the newest access_token remains valid. The authorization remains valid until explicitly revoked or invalidated by an account security change.
      type: object
      required: [token_id,access_token,token_type,expires_at,scopes,user]
      additionalProperties: false
      properties:
        token_id: { type: string }
        access_token: { type: string, description: Random mak_ Bearer token returned only in this response. }
        token_type: { type: string, const: Bearer }
        expires_at: { type: 'null', description: Password-connected player tokens do not expire automatically. }
        scopes: { type: array, minItems: 4, maxItems: 5, items: { $ref: '#/components/schemas/PlayerScope' } }
        user:
          type: object
          required: [id,username,role,avatar_url]
          additionalProperties: false
          properties:
            id: { type: string }
            username: { type: string }
            role: { type: string, enum: [admin,member] }
            avatar_url: { type: [string,'null'] }
    PlayerScope:
      type: string
      enum: ['catalog:read','playback:stream','playback:control','playback:report','library:write']
      description: library:write is privileged. It is included by default only for administrator password connections; manual token creation must request it explicitly, and member accounts cannot use it. It permits catalog and source-folder maintenance, including irreversible source-file deletion where an endpoint explicitly supports it.
    APIToken:
      description: "Represents the MusicArk apitoken resource or value object."
      type: object
      required: [id,name,scopes,created_at,last_used_at,expires_at]
      properties:
        id: { type: string }
        name: { type: string }
        scopes: { type: array, items: { $ref: '#/components/schemas/PlayerScope' } }
        created_at: { type: string, format: date-time }
        last_used_at: { type: [string,'null'], format: date-time }
        expires_at: { type: [string,'null'], format: date-time }
    CreatedAPIToken:
      description: "Represents the MusicArk created apitoken resource or value object."
      allOf:
        - { $ref: '#/components/schemas/APIToken' }
        - type: object
          required: [token]
          properties:
            token: { type: string, description: Plaintext bearer token returned only during creation. }
    APITokenList:
      description: "MusicArk apitoken collection."
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/APIToken' } }
    PlaybackProgressReport:
      description: "Represents the MusicArk playback progress report resource or value object."
      type: object
      required: [track_id,playback_id,device_id,position_ms,state]
      additionalProperties: false
      properties:
        track_id: { type: string }
        playback_id: { type: string, minLength: 1, maxLength: 200, description: Stable client-generated ID for one playback instance; reused reports are idempotent. }
        device_id: { type: string, minLength: 1, maxLength: 120 }
        position_ms: { type: integer, minimum: 0 }
        duration_ms: { type: integer, minimum: 0, default: 0, description: Used only when the catalog has no duration. }
        state: { type: string, enum: [playing,paused,stopped,completed] }
        source: { type: string, maxLength: 80 }
        occurred_at: { type: string, format: date-time }
    PlaybackProgress:
      description: "Represents the MusicArk playback progress resource or value object."
      type: object
      required: [track_id,playback_id,device_id,position_ms,duration_ms,state,source,occurred_at,updated_at]
      properties:
        track_id: { type: string }
        playback_id: { type: string }
        device_id: { type: string }
        position_ms: { type: integer, minimum: 0 }
        duration_ms: { type: integer, minimum: 0 }
        state: { type: string, enum: [playing,paused,stopped,completed] }
        source: { type: string }
        occurred_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    PlaybackProgressResponse:
      description: "Response payload returned by the MusicArk Playback Progress operation."
      type: object
      required: [progress,qualified,play_recorded]
      properties:
        progress: { $ref: '#/components/schemas/PlaybackProgress' }
        qualified: { type: boolean, description: Whether the external report reached the earlier of 3 seconds or 50% of the track duration, or reported the completed state. }
        play_recorded: { type: boolean, description: True only when this request created the idempotent playback event. }
    PlaybackProgressList:
      description: "MusicArk playback progress collection."
      type: object
      required: [items]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/PlaybackProgress' } }
    ReleasePage:
      description: "Cursor-paginated MusicArk release collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Release' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    ArtistPage:
      description: "Cursor-paginated MusicArk artist collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/Artist' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    ListeningRequest:
      description: "Request payload for the MusicArk Listening operation."
      type: object
      required: [track_id,client_capabilities]
      additionalProperties: false
      properties:
        track_id: { type: string }
        start_ms: { type: integer, minimum: 0, default: 0 }
        preference: { type: string, enum: [original_if_supported, force_transcode], description: Omit to apply the current account's saved preference or the built-in safe default. }
        transcode_profile_id: { type: string, description: Stable ID returned by /transcode-profiles; explicit selection requires a matching client MIME/codec capability. }
        client_capabilities:
          type: object
          required: [mime_types]
          additionalProperties: false
          properties: { mime_types: { type: array, items: { type: string } }, supports_hls: { type: boolean }, supports_media_source: { type: boolean } }
    ListeningSession:
      description: "Represents the MusicArk listening session resource or value object."
      type: object
      required: [id,mode,profile_id,stream_url,mime_type,codec,duration_ms,start_ms,expires_at,seekable,cache_status]
      properties:
        id: { type: string }
        mode: { type: string, enum: [direct,transcode] }
        profile_id: { type: string, description: Actual direct or transcoding profile selected for this session. }
        stream_url: { type: string }
        manifest_url: { type: [string,'null'], description: Present for compatible native-HLS transcode sessions when the server cache is enabled. }
        mime_type: { type: string }
        codec: { type: string }
        duration_ms: { type: integer }
        start_ms: { type: integer }
        expires_at: { type: string, format: date-time }
        seekable: { type: boolean }
        cache_status: { type: string, enum: [not_applicable,miss,hit], description: Cache applicability when the session is created; stream responses expose the actual hit or miss in X-MusicArk-Cache. }
    TranscodeProfile:
      description: "Represents the MusicArk transcode profile resource or value object."
      type: object
      required: [id,format,format_label,container,mime_type,codec,browser_capability,bitrate_kbps,lossless,available,channel_policy,sample_rate_policy]
      properties:
        id: { type: string }
        format: { type: string, enum: [mp3,aac,opus,vorbis,flac] }
        format_label: { type: string }
        container: { type: string }
        mime_type: { type: string }
        codec: { type: string }
        browser_capability: { type: string }
        bitrate_kbps: { type: [integer,'null'], description: Nominal target bitrate. VBR output varies with content; high-tier mono Vorbis preserves mono and uses the corresponding channel-adjusted quality tier. }
        lossless: { type: boolean }
        available: { type: boolean }
        unavailable_reason: { type: string }
        channel_policy: { type: string }
        sample_rate_policy: { type: string }
    TranscodeProfileList:
      description: "MusicArk transcode profile collection."
      type: object
      required: [items]
      properties: { items: { type: array, items: { $ref: '#/components/schemas/TranscodeProfile' } } }
    TranscodeCacheSettings:
      description: "Administrator-managed shared transcode cache limit and current finalized cache usage."
      type: object
      required: [max_bytes,used_bytes,minimum_max_bytes,maximum_max_bytes,updated_at]
      additionalProperties: false
      properties:
        max_bytes: { type: integer, minimum: 0, maximum: 1099511627776, description: Zero disables the cache; non-zero values are at least 256 MiB. }
        used_bytes: { type: integer, minimum: 0 }
        minimum_max_bytes: { type: integer, const: 268435456 }
        maximum_max_bytes: { type: integer, const: 1099511627776 }
        updated_at: { type: [string,'null'], format: date-time }
    UpdateTranscodeCacheSettingsRequest:
      description: "Replaces the shared transcode cache limit."
      type: object
      required: [max_bytes]
      additionalProperties: false
      properties:
        max_bytes:
          description: Zero disables the cache; non-zero values must be at least 256 MiB.
          oneOf:
            - { type: integer, const: 0 }
            - { type: integer, minimum: 268435456, maximum: 1099511627776 }
    UserTranscodingPreference:
      description: "Represents the MusicArk user transcoding preference resource or value object."
      type: object
      required: [playback_policy,default_profile_id,updated_at]
      properties:
        playback_policy: { type: string, enum: [original_if_supported,always_transcode] }
        default_profile_id: { type: string }
        updated_at: { type: [string,'null'], format: date-time }
    UpdateUserTranscodingPreferenceRequest:
      description: "Request payload for the MusicArk Update User Transcoding Preference operation."
      type: object
      required: [playback_policy,default_profile_id]
      additionalProperties: false
      properties:
        playback_policy: { type: string, enum: [original_if_supported,always_transcode] }
        default_profile_id: { type: string }
    ListeningProgressRequest:
      description: "Request payload for the MusicArk Listening Progress operation."
      type: object
      required: [position_ms]
      additionalProperties: false
      properties:
        position_ms: { type: integer, minimum: 0 }
        completed: { type: boolean, default: false }
        device_id: { type: string, minLength: 1, maxLength: 120, description: Stable opaque ID for this client installation. Hardware categories such as desktop or mobile are not required or inferred. Omit only for compatibility with legacy clients. }
    EnrichmentSource:
      description: "Represents the MusicArk enrichment source resource or value object."
      type: object
      required: [id,name,capabilities,disclosure_fields,enabled,updated_at]
      properties:
        id: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] }
        name: { type: string }
        capabilities: { type: array, items: { type: string, enum: [metadata,artwork,lyrics] } }
        disclosure_fields: { type: array, items: { type: string } }
        enabled: { type: boolean, const: true, description: Built-in enrichment sources are always enabled. }
        last_probe_status: { type: [string,'null'] }
        last_probe_error_code: { type: [string,'null'] }
        last_probed_at: { type: [string,'null'], format: date-time }
        updated_at: { type: string, format: date-time }
    EnrichmentSourceList:
      description: "MusicArk enrichment source collection."
      type: object
      required: [items]
      properties: { items: { type: array, items: { $ref: '#/components/schemas/EnrichmentSource' } } }
    UpdateEnrichmentSourceRequest:
      description: "Request payload for the MusicArk Update Enrichment Source operation."
      type: object
      additionalProperties: false
      properties:
        enabled: { type: boolean, const: true, description: Only true is accepted; false returns ENRICHMENT_SOURCE_ALWAYS_ENABLED. }
    EnrichmentQualityItem:
      description: "Represents the MusicArk enrichment quality item resource or value object."
      type: object
      required: [track_id,title,artists,album,artwork_url,issues,target_version]
      properties:
        track_id: { type: string }
        title: { type: string }
        artists: { type: array, items: { type: string } }
        album: { type: string }
        artwork_url: { type: [string,'null'] }
        issues: { type: array, items: { type: string, enum: [metadata,artwork,lyrics] } }
        target_version: { type: string }
    EnrichmentQualityPage:
      description: "Cursor-paginated MusicArk enrichment quality collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/EnrichmentQualityItem' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer }
    EnrichmentAutomationSettings:
      description: "Represents the MusicArk enrichment automation settings resource or value object."
      type: object
      required: [enabled,updated_at]
      additionalProperties: false
      properties:
        enabled: { type: boolean, default: false }
        updated_at: { type: string, format: date-time }
    UpdateEnrichmentAutomationRequest:
      description: "Request payload for the MusicArk Update Enrichment Automation operation."
      type: object
      required: [enabled]
      additionalProperties: false
      properties:
        enabled: { type: boolean }
    EnrichmentPreferences:
      description: "Represents the MusicArk enrichment preferences resource or value object."
      type: object
      required: [match_mode,source_ids,updated_at]
      additionalProperties: false
      properties:
        match_mode: { type: string, enum: [balanced,careful,strict], default: balanced, description: "Automatic matching mode. UI labels map balanced to 宽松模式, careful to 标准模式, and strict to 严格模式. All modes reject conflicts. For auto_best/post-scan auto_high_confidence respectively, balanced requires 70 plus identity evidence / 90 plus high-confidence evidence; careful requires 85/92 plus high-confidence evidence; strict requires 95/95 plus strict evidence and aligned comparable track context." }
        source_ids: { type: array, minItems: 1, uniqueItems: true, description: Built-in connectors queried by newly created UI and post-scan runs., items: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] } }
        updated_at: { type: string, format: date-time }
    UpdateEnrichmentPreferencesRequest:
      description: "Request payload for the MusicArk Update Enrichment Preferences operation."
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        match_mode: { type: string, enum: [balanced,careful,strict], description: "Automatic matching mode saved for future runs: balanced = 宽松模式, careful = 标准模式, strict = 严格模式." }
        source_ids: { type: array, minItems: 1, uniqueItems: true, description: Non-empty built-in connector subset saved for future runs., items: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] } }
    PostScanEnrichmentOpportunity:
      description: "Represents the MusicArk post scan enrichment opportunity resource or value object."
      type: object
      required: [index_run_id,added_count,eligible_count,selected_count,has_more,auto_enabled,enrichment_run_id,run_status,trigger_kind]
      additionalProperties: false
      properties:
        index_run_id: { type: string }
        added_count: { type: integer, minimum: 0 }
        eligible_count: { type: integer, minimum: 0 }
        selected_count: { type: integer, minimum: 0 }
        has_more: { type: boolean }
        auto_enabled: { type: boolean }
        enrichment_run_id: { type: [string,'null'] }
        run_status: { type: [string,'null'] }
        trigger_kind: { type: [string,'null'], enum: [automatic,manual,null] }
    CreateEnrichmentRunRequest:
      description: "Request payload for the MusicArk Create Enrichment Run operation."
      type: object
      required: [selection,source_ids,requested_fields,mode]
      additionalProperties: false
      properties:
        selection:
          type: object
          additionalProperties: false
          properties:
            track_ids: { type: array, uniqueItems: true, items: { type: string } }
            quality_filter: { type: string, enum: [metadata,artwork,lyrics,all] }
            exclude_track_ids: { type: array, uniqueItems: true, description: Track IDs omitted from a quality-filter selection., items: { type: string } }
        source_ids: { type: array, description: Explicit source subset for this run. An empty array uses the saved enrichment preference., items: { type: string, enum: [musicbrainz,cover_art_archive,lrclib,qq_music,netease_music,kugou_music,kuwo_music,migu_music,lrc_cx] } }
        requested_fields: { type: array, description: Provider result fields to retain, assert, preview, and apply. style and language do not add outbound identity query parameters; they are used only when an upstream response explicitly provides them., items: { type: string, enum: [title,artist_credits,release,composers,genres,style,language,release_date,year,track_number,track_total,disc_number,disc_total,isrc,artwork,lyrics] } }
        mode: { type: string, enum: [review_only,auto_best,auto_high_confidence], description: review_only never writes automatically; auto_best is the active quality-center flow; auto_high_confidence is the post-scan flow. }
        query:
          type: object
          additionalProperties: false
          required: [title,artists]
          properties:
            title: { type: string, minLength: 1, maxLength: 300 }
            artists: { type: array, minItems: 1, maxItems: 10, items: { type: string, minLength: 1, maxLength: 200 } }
            album: { type: string, maxLength: 300 }
    EnrichmentRun:
      description: "Represents the MusicArk enrichment run resource or value object."
      type: object
      required: [id,operation_id,mode,match_mode,status,target_count,queried_count,matched_count,no_match_count,review_count,applied_count,failed_count]
      properties:
        id: { type: string }
        operation_id: { type: string }
        mode: { type: string, enum: [review_only,auto_best,auto_high_confidence] }
        match_mode: { type: string, enum: [balanced,careful,strict], description: "Creation-time automatic matching mode snapshot used by this run: balanced = 宽松模式, careful = 标准模式, strict = 严格模式." }
        status: { type: string, enum: [queued,snapshotting,querying,matching,ready_for_review,completed,cancelling,cancelled,failed,interrupted] }
        source_ids: { type: array, items: { type: string } }
        requested_fields: { type: array, items: { type: string } }
        target_count: { type: integer }
        queried_count: { type: integer }
        matched_count: { type: integer }
        no_match_count: { type: integer }
        review_count: { type: integer }
        applied_count: { type: integer }
        failed_count: { type: integer }
        created_at: { type: string, format: date-time }
        started_at: { type: [string,'null'], format: date-time }
        finished_at: { type: [string,'null'], format: date-time }
        updated_at: { type: string, format: date-time }
    EnrichmentTarget:
      description: "Represents the MusicArk enrichment target resource or value object."
      type: object
      required: [id,track_id,title,state,best_score,error_code,target_version]
      properties:
        id: { type: string }
        track_id: { type: string }
        title: { type: string }
        state: { type: string, enum: [queued,snapshotting,querying,matching,matched,applied,no_match,failed,interrupted] }
        best_score: { type: [integer,'null'], minimum: 0, maximum: 100 }
        error_code:
          type: [string,'null']
          description: Stable machine-readable code. ENRICHMENT_LOW_CONFIDENCE means candidates existed but none passed the configured automatic matching policy.
        target_version: { type: string }
    EnrichmentTargetPage:
      description: "Cursor-paginated MusicArk enrichment target collection."
      type: object
      required: [items,page,total]
      properties:
        items: { type: array, items: { $ref: '#/components/schemas/EnrichmentTarget' } }
        page: { $ref: '#/components/schemas/PageInfo' }
        total: { type: integer, minimum: 0 }
    EnrichmentScoreBreakdown:
      description: "Represents the MusicArk enrichment score breakdown resource or value object."
      type: object
      required: [identifier,isrc,title,artist,duration,release,track_context,conflicts]
      properties:
        identifier: { type: integer }
        isrc: { type: integer }
        title: { type: integer }
        artist: { type: integer }
        duration: { type: integer }
        release: { type: integer }
        track_context: { type: integer, minimum: 0, maximum: 5, description: Earned points for equal comparable track number, disc number, and year. }
        track_context_max: { type: integer, minimum: 0, maximum: 5, description: Maximum context points actually comparable on both query and candidate; omitted for legacy or zero-context results. strict (严格模式) requires track_context to equal this value when it is positive. }
        conflicts: { type: array, items: { type: string } }
    EnrichmentCandidate:
      description: "Represents the MusicArk enrichment candidate resource or value object."
      type: object
      required: [id,source_name,source_record_id,kind,score,score_breakdown,summary,requires_review,artwork_preview_url]
      properties:
        id: { type: string }
        source_name: { type: string }
        source_record_id: { type: string }
        kind: { type: string, enum: [recording,artwork,lyrics] }
        score: { type: integer, minimum: 0, maximum: 100 }
        score_breakdown: { $ref: '#/components/schemas/EnrichmentScoreBreakdown' }
        summary: { type: object, additionalProperties: true, description: Bounded provider-neutral summary. It may contain explicit genres, style, and language values returned by the provider; upstream raw objects are never retained and style is never synthesized from genres. }
        requires_review: { type: boolean, description: Baseline scorer hint based on the generic 90-point high-confidence rule; the run's match_mode performs the final automatic-adoption decision. }
        artwork_preview_url: { type: [string,'null'] }
        created_at: { type: string, format: date-time }
    EnrichmentAssertion:
      description: "Represents the MusicArk enrichment assertion resource or value object."
      type: object
      required: [field,current,suggested,confidence,conflict_code]
      properties:
        field: { type: string, description: Allowlisted field name. Metadata assertions include genres, style, and language when the candidate explicitly provides them. }
        current: {}
        suggested: {}
        confidence: { type: integer, minimum: 0, maximum: 100 }
        conflict_code: { type: [string,'null'] }
    EnrichmentCandidateDetail:
      description: "Represents the MusicArk enrichment candidate detail resource or value object."
      allOf:
        - { $ref: '#/components/schemas/EnrichmentCandidate' }
        - type: object
          required: [assertions]
          properties: { assertions: { type: array, items: { $ref: '#/components/schemas/EnrichmentAssertion' } } }
    CreateChangePlanRequest:
      description: "Request payload for the MusicArk Create Change Plan operation."
      type: object
      required: [kind,items]
      additionalProperties: false
      properties:
        kind: { type: string, enum: [apply_enrichment,remove_enrichment] }
        items:
          type: array
          minItems: 1
          maxItems: 500
          items:
            type: object
            required: [track_id,target_version,fields]
            additionalProperties: false
            properties:
              track_id: { type: string }
              target_version: { type: string }
              candidate_id: { type: string }
              fields: { type: array, minItems: 1, items: { type: string } }
    ChangePlan:
      description: "Represents the MusicArk change plan resource or value object."
      type: object
      required: [id,kind,status,expires_at,items,summary,updated_at]
      properties:
        id: { type: string }
        kind: { type: string, enum: [apply_enrichment,remove_enrichment] }
        status: { type: string, enum: [ready,committed,abandoned,expired] }
        commit_token: { type: string, description: Returned only when the plan is created. }
        etag: { type: string, description: Convenience copy of the response ETag for same-origin clients. }
        expires_at: { type: string, format: date-time, description: Expiry of the uncommitted preview and commit token only. It is not an expiry for values written by a successful commit. }
        items: { type: array, items: { type: object, additionalProperties: true } }
        summary: { type: object, required: [ready,conflict], properties: { ready: { type: integer }, conflict: { type: integer } } }
        updated_at: { type: string, format: date-time }
    TrackProvenance:
      type: object
      description: Effective field provenance. Entries with origin enrichment remain active without an automatic expiry until explicitly overwritten or removed.
      required: [track_id,target_version,items,priority]
      properties:
        track_id: { type: string }
        target_version: { type: string }
        items:
          type: array
          items:
            type: object
            required: [id,field,value,origin,source_name,source_record_id,applied_at]
            properties:
              id: { type: string }
              field: { type: string }
              value: {}
              origin: { type: string, enum: [user,enrichment,local] }
              source_name: { type: string }
              source_record_id: { type: string }
              applied_at: { type: string, format: date-time }
        priority: { type: array, items: { type: string, enum: [user,enrichment,local] } }
