components:
  schemas:
    immerle.APITokenDTO:
      properties:
        connected:
          description: |-
            Connected is true if the token was used within the last few minutes.
            Only meaningful for device tokens (IsDevice).
          type: boolean
        createdAt:
          example: '2026-06-15T09:00:00Z'
          type: string
        expiresAt:
          type: string
        id:
          type: string
        isDevice:
          description: |-
            IsDevice marks an app login session rather than a manually-created
            personal/CLI token.
          type: boolean
        lastUsedAt:
          type: string
        name:
          example: my-cli
          type: string
        prefix:
          example: gsk_AbC12
          type: string
      type: object
    immerle.AccountDTO:
      properties:
        displayName:
          example: Bob Marley
          type: string
        email:
          example: bob@example.com
          type: string
        id:
          type: string
        isAdmin:
          example: false
          type: boolean
        language:
          example: fr
          type: string
        username:
          example: bob
          type: string
      type: object
    immerle.ActivityEventDTO:
      properties:
        createdAt:
          example: '2026-06-15T09:00:00Z'
          type: string
        displayName:
          example: Bob Marley
          type: string
        id:
          type: string
        item:
          $ref: '#/components/schemas/immerle.ActivityItemDTO'
        itemId:
          type: string
        itemType:
          example: track
          type: string
        privacy:
          example: public
          type: string
        type:
          example: listen
          type: string
        userId:
          type: string
        username:
          example: bob
          type: string
      type: object
    immerle.ActivityItemDTO:
      properties:
        album:
          example: Discovery
          type: string
        albumId:
          type: string
        artist:
          example: Daft Punk
          type: string
        artistId:
          type: string
        coverArt:
          type: string
        duration:
          example: 320
          type: integer
        title:
          example: One More Time
          type: string
        year:
          example: 2001
          type: integer
      type: object
    immerle.BandcampCollectionDTO:
      properties:
        items:
          items:
            $ref: '#/components/schemas/immerle.BandcampCollectionItemDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.BandcampCollectionItemDTO:
      properties:
        artUrl:
          type: string
        artistName:
          type: string
        itemTitle:
          type: string
        itemType:
          example: album
          type: string
        jobId:
          type: string
        jobStatus:
          example: completed
          type: string
        purchased:
          example: '2021-01-01T10:00:00Z'
          type: string
        saleItemId:
          example: '123456789'
          type: string
        saleItemType:
          example: p
          type: string
      type: object
    immerle.BandcampJobDTO:
      properties:
        artistName:
          type: string
        attempts:
          type: integer
        createdAt:
          example: '2026-07-18T21:42:00Z'
          type: string
        error:
          type: string
        format:
          example: flac
          type: string
        id:
          type: string
        itemTitle:
          type: string
        itemType:
          example: album
          type: string
        saleItemId:
          type: string
        saleItemType:
          type: string
        status:
          example: completed
          type: string
        trackIds:
          items:
            type: string
          type: array
          uniqueItems: false
        updatedAt:
          example: '2026-07-18T21:43:00Z'
          type: string
      type: object
    immerle.BandcampJobsDTO:
      properties:
        jobs:
          items:
            $ref: '#/components/schemas/immerle.BandcampJobDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.BandcampStatusDTO:
      properties:
        connected:
          type: boolean
        fanId:
          type: string
        lastSyncedAt:
          example: '2026-07-18T21:42:00Z'
          type: string
        needsReconnect:
          type: boolean
      type: object
    immerle.CapabilitiesDTO:
      properties:
        capabilities:
          additionalProperties:
            $ref: '#/components/schemas/immerle.CapabilityDTO'
          type: object
        protocolVersion:
          example: 1.0.0
          type: string
        server:
          example: immerle
          type: string
      type: object
    immerle.CapabilityDTO:
      properties:
        version:
          example: 1
          type: integer
      type: object
    immerle.ChartsSyncDTO:
      properties:
        synced:
          example: 6
          type: integer
      type: object
    immerle.CleanupRunDTO:
      properties:
        removed:
          example: 3
          type: integer
      type: object
    immerle.CleanupStatusDTO:
      properties:
        enabled:
          example: true
          type: boolean
        intervalSeconds:
          example: 21600
          type: integer
        maxAgeSeconds:
          example: 2592000
          type: integer
      type: object
    immerle.ConcertDTO:
      properties:
        artistName:
          example: Daft Punk
          type: string
        city:
          example: Paris
          type: string
        eventName:
          example: Daft Punk World Tour
          type: string
        id:
          type: string
        source:
          example: ticketmaster
          type: string
        startTime:
          example: '2026-09-12T19:00:00Z'
          type: string
        url:
          example: https://www.ticketmaster.com/event/...
          type: string
        venue:
          example: Accor Arena
          type: string
      type: object
    immerle.ConcertsDTO:
      properties:
        concerts:
          items:
            $ref: '#/components/schemas/immerle.ConcertDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.ConcertsStatusDTO:
      properties:
        country:
          description: >-
            Country is an ISO 3166-1 alpha-2 code (e.g. "FR"), picked from a
            fixed

            dropdown in the admin UI — not a secret, returned as-is.
          example: FR
          type: string
        enabled:
          example: false
          type: boolean
        skiddleConfigured:
          example: false
          type: boolean
        ticketmasterConfigured:
          example: false
          type: boolean
      type: object
    immerle.CreateTokenDTO:
      properties:
        id:
          type: string
        name:
          example: my-cli
          type: string
        prefix:
          example: gsk_AbC12
          type: string
        token:
          example: gsk_AbC12...full-secret
          type: string
      type: object
    immerle.DeviceDTO:
      properties:
        connected:
          description: >-
            Connected is true if the device was seen within the last few
            minutes.
          type: boolean
        createdAt:
          type: string
        expiresAt:
          type: string
        id:
          type: string
        lastIp:
          type: string
        lastSeen:
          type: string
        name:
          example: Pixel 8
          type: string
        userAgent:
          type: string
      type: object
    immerle.FederationInstancesDTO:
      properties:
        instances:
          items:
            $ref: '#/components/schemas/immerle.InstanceSummaryDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.FederationSubscriptionsDTO:
      properties:
        subscriptions:
          items:
            $ref: '#/components/schemas/immerle.InstanceSummaryDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.FederationUpdateDTO:
      properties:
        name:
          example: My living-room immerle
          type: string
        sqid:
          example: my-node
          type: string
      type: object
    immerle.ImportDTO:
      properties:
        doubtful:
          example: 2
          type: integer
        error:
          type: string
        failed:
          example: 0
          type: integer
        id:
          type: string
        items:
          items:
            $ref: '#/components/schemas/immerle.ImportItemDTO'
          type: array
          uniqueItems: false
        matched:
          example: 38
          type: integer
        missing:
          example: 2
          type: integer
        playlistId:
          type: string
        source:
          example: spotify
          type: string
        sourcePlaylistName:
          type: string
        sourceRef:
          type: string
        status:
          example: completed
          type: string
        total:
          example: 42
          type: integer
      type: object
    immerle.ImportItemDTO:
      properties:
        candidateCoverArt:
          type: string
        candidateTrackId:
          description: >-
            CandidateTrackID / CandidateCoverArt let a client preview a doubtful
            item:

            play the candidate (stream accepts the remote track id) and show its
            cover.
          type: string
        confidence:
          example: 0.62
          type: number
        id:
          type: string
        matchedTrackId:
          type: string
        note:
          type: string
        position:
          type: integer
        resolvedArtist:
          type: string
        resolvedTitle:
          type: string
        sourceAlbum:
          type: string
        sourceArtist:
          example: Daft Punk
          type: string
        sourceTitle:
          example: Da Funk
          type: string
        status:
          example: matched
          type: string
      type: object
    immerle.ImportSourceDTO:
      properties:
        configured:
          example: true
          type: boolean
        name:
          example: spotify
          type: string
      type: object
    immerle.InstanceSummaryDTO:
      properties:
        id:
          example: 3f1c2d4e-5a6b-7c8d-9e0f-1a2b3c4d5e6f
          type: string
        lastSeenAt:
          type: string
        name:
          example: A friend's immerle
          type: string
        region:
          example: eu
          type: string
        sqid:
          example: other-node
          type: string
      type: object
    immerle.JamDTO:
      properties:
        participants:
          items:
            $ref: '#/components/schemas/immerle.JamParticipantDTO'
          type: array
          uniqueItems: false
        session:
          $ref: '#/components/schemas/immerle.JamSessionDTO'
      type: object
    immerle.JamInviteDTO:
      properties:
        createdAt:
          example: '2026-06-15T09:00:00Z'
          type: string
        id:
          type: string
        inviteeId:
          type: string
        inviterDisplayName:
          example: Bob Marley
          type: string
        inviterId:
          type: string
        inviterUsername:
          example: bob
          type: string
        sessionId:
          type: string
        sessionName:
          example: Friday Night
          type: string
      type: object
    immerle.JamParticipantDTO:
      properties:
        joinedAt:
          example: '2026-06-15T09:00:00Z'
          type: string
        sessionId:
          type: string
        userId:
          type: string
        username:
          example: bob
          type: string
      type: object
    immerle.JamSessionDTO:
      properties:
        currentTrackId:
          type: string
        hostId:
          type: string
        id:
          type: string
        name:
          example: Friday Night
          type: string
        position:
          example: 42000
          type: integer
        state:
          example: playing
          type: string
        trackIds:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.LibraryStatsDTO:
      properties:
        albums:
          example: 128
          type: integer
        artists:
          example: 42
          type: integer
        totalDuration:
          example: 432000
          type: integer
        totalSize:
          example: 9663676416
          type: integer
        tracks:
          example: 1560
          type: integer
        updatedAt:
          example: '2026-06-17T09:00:00Z'
          type: string
      type: object
    immerle.LoginDTO:
      properties:
        device:
          $ref: '#/components/schemas/immerle.DeviceDTO'
        token:
          example: eyJhbGciOiJIUzI1Ni...
          type: string
      type: object
    immerle.OkResponse:
      properties:
        ok:
          example: true
          type: boolean
      type: object
    immerle.ProfileDTO:
      properties:
        activity:
          items:
            $ref: '#/components/schemas/immerle.ActivityEventDTO'
          type: array
          uniqueItems: false
        hallOfFame:
          $ref: '#/components/schemas/immerle.ProfileHallOfFameDTO'
        isSelf:
          example: false
          type: boolean
        playlists:
          items:
            $ref: '#/components/schemas/immerle.ProfilePlaylistDTO'
          type: array
          uniqueItems: false
        stats:
          $ref: '#/components/schemas/immerle.ProfileStatsDTO'
        user:
          properties:
            displayName:
              example: Bob Marley
              type: string
            id:
              type: string
            isAdmin:
              example: false
              type: boolean
            username:
              example: bob
              type: string
          type: object
      type: object
    immerle.ProfileHallOfFameDTO:
      properties:
        top:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
        total:
          example: 12
          type: integer
      type: object
    immerle.ProfilePlaylistDTO:
      properties:
        comment:
          type: string
        coverArt:
          type: string
        coverArts:
          items:
            type: string
          type: array
          uniqueItems: false
        duration:
          example: 5400
          type: integer
        id:
          type: string
        name:
          example: Summer vibes
          type: string
        songCount:
          example: 24
          type: integer
      type: object
    immerle.ProfileStatsDTO:
      properties:
        listenSeconds:
          example: 432000
          type: integer
        playlists:
          example: 5
          type: integer
        plays:
          example: 1234
          type: integer
      type: object
    immerle.ProviderDTO:
      properties:
        active:
          example: true
          type: boolean
        builtin:
          example: false
          type: boolean
        config:
          example: '{"headers":{"Authorization":"Bearer x"}}'
          type: string
        deletable:
          example: true
          type: boolean
        enabled:
          example: true
          type: boolean
        endpoint:
          example: https://my-service.internal
          type: string
        kind:
          example: http
          type: string
        name:
          example: manual
          type: string
        sortOrder:
          example: 3
          type: integer
        version:
          description: >-
            Version is the remote's live protocol version (HTTP providers), null
            when

            unknown or for built-ins.
          example: 1
          type: integer
      type: object
    immerle.ProviderLogDTO:
      properties:
        action:
          example: download
          type: string
        createdAt:
          example: '2026-06-18T20:00:00Z'
          type: string
        id:
          type: string
        level:
          example: error
          type: string
        message:
          example: unexpected status 404
          type: string
        provider:
          example: free-music-archive
          type: string
      type: object
    immerle.PublicPlaylistDTO:
      properties:
        comment:
          type: string
        coverArt:
          type: string
        coverArts:
          items:
            type: string
          type: array
          uniqueItems: false
        duration:
          type: integer
        id:
          type: string
        name:
          example: Editorial Picks
          type: string
        owner:
          example: alice
          type: string
        songCount:
          type: integer
        subscribed:
          type: boolean
      type: object
    immerle.RuntimeSettingsDTO:
      properties:
        auth:
          properties:
            deviceTokenTtlSeconds:
              example: 2592000
              type: integer
          type: object
        cleanup:
          properties:
            enabled:
              example: true
              type: boolean
            intervalSeconds:
              example: 21600
              type: integer
            maxAgeSeconds:
              example: 2592000
              type: integer
          type: object
        federation:
          properties:
            exportScrobbles:
              type: boolean
            instanceId:
              description: >-
                InstanceID is the hub-assigned fixed UUID (read-only). Sqid is
                the

                editable handle. PrivateKey is never returned (redacted
                server-side).
              example: 3f1c2d4e-5a6b-7c8d-9e0f-1a2b3c4d5e6f
              type: string
            instanceName:
              example: My living-room immerle
              type: string
            sqid:
              example: my-node
              type: string
            syncPlaylists:
              type: boolean
            userId:
              description: >-
                Federation is active whenever linked (instanceId set); no enable
                flag.
              example: 6f1c2b8e-1f0a-4f9b-9c3a-1e2d3c4b5a6f
              type: string
          type: object
        ldap:
          properties:
            bindDnTemplate:
              example: uid=%s,ou=people,dc=example,dc=com
              type: string
            enabled:
              example: false
              type: boolean
            url:
              example: ldaps://ldap.example.com:636
              type: string
          type: object
        logs:
          properties:
            retentionDays:
              example: 30
              type: integer
          type: object
        providers:
          properties:
            autoDownloadOnPlay:
              example: true
              type: boolean
            searchTimeoutSeconds:
              example: 3
              type: integer
          type: object
        scan:
          properties:
            intervalSeconds:
              example: 3600
              type: integer
            watch:
              example: true
              type: boolean
          type: object
        server:
          properties:
            corsAllowedOrigins:
              example:
                - '*'
              items:
                type: string
              type: array
              uniqueItems: false
          type: object
        transcode:
          properties:
            ffmpegPath:
              example: ffmpeg
              type: string
            ffprobePath:
              example: ffprobe
              type: string
            profiles:
              items:
                properties:
                  bitRate:
                    example: 192
                    type: integer
                  ffmpegArgs:
                    type: string
                  format:
                    example: mp3
                    type: string
                  name:
                    example: mp3
                    type: string
                type: object
              type: array
              uniqueItems: false
          type: object
      type: object
    immerle.SettingsDTO:
      properties:
        pendingRestart:
          items:
            type: string
          type: array
          uniqueItems: false
        restartRequired:
          example: false
          type: boolean
        settings:
          $ref: '#/components/schemas/immerle.RuntimeSettingsDTO'
      type: object
    immerle.SetupInitRequest:
      properties:
        displayName:
          description: >-
            DisplayName is an optional free-text name shown in the UI (falls
            back to

            the username when empty).
          example: Kilian Smiti
          type: string
        email:
          example: me@example.com
          type: string
        password:
          example: a-strong-password
          type: string
        setupToken:
          description: |-
            SetupToken is required only when the server was started with
            auth.require_setup_token=true.
          type: string
        username:
          example: kilian
          type: string
      type: object
    immerle.SetupStatusDTO:
      properties:
        initialized:
          example: false
          type: boolean
        needsSetup:
          example: true
          type: boolean
        setupTokenRequired:
          example: false
          type: boolean
        userCount:
          example: 0
          type: integer
      type: object
    immerle.SmartConditionDTO:
      properties:
        field:
          example: genre
          type: string
        op:
          example: is
          type: string
        value:
          example: House
          type: string
      type: object
    immerle.SmartPlaylistDTO:
      properties:
        id:
          type: string
        name:
          example: Heavy rotation
          type: string
        rules:
          $ref: '#/components/schemas/immerle.SmartRulesDTO'
      type: object
    immerle.SmartPlaylistRequestDTO:
      properties:
        name:
          example: Heavy rotation
          type: string
        rules:
          $ref: '#/components/schemas/immerle.SmartRulesDTO'
      type: object
    immerle.SmartRulesDTO:
      properties:
        conditions:
          items:
            $ref: '#/components/schemas/immerle.SmartConditionDTO'
          type: array
          uniqueItems: false
        limit:
          example: 100
          type: integer
        match:
          example: all
          type: string
        order:
          example: desc
          type: string
        sort:
          example: playCount
          type: string
      type: object
    immerle.SubscribeRequestDTO:
      properties:
        instanceId:
          example: 3f1c2d4e-5a6b-7c8d-9e0f-1a2b3c4d5e6f
          type: string
        sqid:
          example: other-node
          type: string
      type: object
    immerle.ThemeDTO:
      properties:
        accentColor:
          example: '#3b82f6'
          type: string
      type: object
    immerle.TrackDTO:
      properties:
        album:
          example: Discovery
          type: string
        albumId:
          type: string
        artist:
          example: Daft Punk
          type: string
        artistId:
          type: string
        contentType:
          example: audio/flac
          type: string
        coverArt:
          type: string
        duration:
          example: 320
          type: integer
        genre:
          example: House
          type: string
        id:
          type: string
        size:
          example: 8123456
          type: integer
        suffix:
          example: flac
          type: string
        title:
          example: One More Time
          type: string
        track:
          example: 1
          type: integer
        year:
          example: 2001
          type: integer
      type: object
    immerle.TrackListDTO:
      properties:
        limit:
          example: 50
          type: integer
        offset:
          example: 0
          type: integer
        total:
          example: 1560
          type: integer
        tracks:
          items:
            $ref: '#/components/schemas/immerle.TrackDTO'
          type: array
          uniqueItems: false
      type: object
    immerle.UserDTO:
      properties:
        displayName:
          example: Kilian Smiti
          type: string
        id:
          type: string
        isAdmin:
          example: true
          type: boolean
        username:
          example: kilian
          type: string
      type: object
    immerle.WrappedCountDTO:
      properties:
        name:
          example: Daft Punk
          type: string
        plays:
          example: 120
          type: integer
      type: object
    immerle.WrappedDTO:
      properties:
        byMonth:
          items:
            type: integer
          type: array
          uniqueItems: false
        topArtists:
          items:
            $ref: '#/components/schemas/immerle.WrappedCountDTO'
          type: array
          uniqueItems: false
        topGenres:
          items:
            $ref: '#/components/schemas/immerle.WrappedCountDTO'
          type: array
          uniqueItems: false
        topTracks:
          items:
            $ref: '#/components/schemas/immerle.WrappedTrackDTO'
          type: array
          uniqueItems: false
        totalPlays:
          example: 1234
          type: integer
        totalSeconds:
          example: 987654
          type: integer
        year:
          example: 2025
          type: integer
      type: object
    immerle.WrappedTrackDTO:
      properties:
        artist:
          example: Daft Punk
          type: string
        id:
          type: string
        plays:
          example: 42
          type: integer
        title:
          example: One More Time
          type: string
      type: object
    immerle.addCollaboratorBody:
      properties:
        username:
          type: string
      type: object
    immerle.adminUserCreateRequest:
      properties:
        admin:
          type: boolean
        displayName:
          type: string
        email:
          type: string
        password:
          type: string
        username:
          type: string
      type: object
    immerle.adminUserUpdateRequest:
      properties:
        admin:
          type: boolean
        displayName:
          type: string
        email:
          type: string
        password:
          type: string
        scrobblingEnabled:
          type: boolean
      type: object
    immerle.adminUserView:
      properties:
        admin:
          type: boolean
        displayName:
          type: string
        email:
          type: string
        id:
          type: string
        scrobblingEnabled:
          type: boolean
        username:
          type: string
      type: object
    immerle.albumView:
      properties:
        artist:
          type: string
        artistId:
          type: string
        coverArt:
          type: string
        duration:
          type: integer
        genre:
          type: string
        id:
          type: string
        name:
          type: string
        songCount:
          type: integer
        starred:
          type: string
        tracks:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
        year:
          type: integer
      type: object
    immerle.apiError:
      properties:
        code:
          type: string
        fields:
          items:
            $ref: '#/components/schemas/immerle.fieldError'
          type: array
          uniqueItems: false
        message:
          type: string
        params:
          additionalProperties: {}
          description: i18n interpolation values; always present ({} when none)
          type: object
      type: object
    immerle.artistView:
      properties:
        albumCount:
          type: integer
        albums:
          items:
            $ref: '#/components/schemas/immerle.albumView'
          type: array
          uniqueItems: false
        coverArt:
          type: string
        id:
          type: string
        name:
          type: string
        starred:
          type: string
      type: object
    immerle.bandcampConnectRequest:
      properties:
        cookie:
          type: string
      type: object
    immerle.bandcampImportRequest:
      properties:
        artistName:
          type: string
        itemTitle:
          type: string
        itemType:
          type: string
      type: object
    immerle.channelView:
      properties:
        description:
          type: string
        episodes:
          items:
            $ref: '#/components/schemas/immerle.episodeView'
          type: array
          uniqueItems: false
        error:
          type: string
        id:
          type: string
        imageUrl:
          type: string
        status:
          type: string
        title:
          type: string
        url:
          type: string
      type: object
    immerle.cleanupUpdateRequest:
      properties:
        enabled:
          type: boolean
      type: object
    immerle.commandView:
      description: |-
        PendingCommand is a spectator's remote-control command for the active
        device to apply (see POST /play-queue/commands). CommandSeq increases
        on every new command, so a receiver can tell a new one from one it
        already applied.
      properties:
        forTarget:
          type: string
        issuedBy:
          type: string
        positionMs:
          type: integer
        queueIndex:
          type: integer
        trackId:
          type: string
        type:
          type: string
      type: object
    immerle.concertsUpdateRequest:
      properties:
        country:
          description: >-
            Country is an ISO 3166-1 alpha-2 code (e.g. "FR") from the admin
            UI's

            fixed dropdown — the single instance-wide location concert discovery

            searches near (there is no per-user location).
          type: string
        enabled:
          type: boolean
        skiddleApiKey:
          type: string
        ticketmasterApiKey:
          type: string
      type: object
    immerle.createJamRequest:
      properties:
        name:
          type: string
        trackIds:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.createTokenRequest:
      properties:
        device:
          description: |-
            Device marks this token as an app login session (one per installed
            client) rather than a manually-created personal/CLI token — only
            device tokens are offered as playback-transfer targets.
          type: boolean
        expiresAt:
          description: >-
            ExpiresAt is an optional RFC3339 timestamp; omit or null for a token
            that

            never expires.
          type: string
        name:
          type: string
      type: object
    immerle.episodeView:
      properties:
        channelId:
          type: string
        contentType:
          type: string
        description:
          type: string
        duration:
          type: integer
        id:
          type: string
        publishDate:
          type: string
        size:
          type: integer
        status:
          type: string
        streamable:
          description: Streamable is true once the episode audio has been downloaded.
          type: boolean
        suffix:
          type: string
        title:
          type: string
      type: object
    immerle.errorResponse:
      properties:
        error:
          $ref: '#/components/schemas/immerle.apiError'
      type: object
    immerle.favoritesView:
      properties:
        albums:
          items:
            $ref: '#/components/schemas/immerle.albumView'
          type: array
          uniqueItems: false
        artists:
          items:
            $ref: '#/components/schemas/immerle.artistView'
          type: array
          uniqueItems: false
        songs:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
      type: object
    immerle.fieldError:
      properties:
        field:
          type: string
        message:
          type: string
      type: object
    immerle.genreView:
      properties:
        albumCount:
          type: integer
        name:
          type: string
        songCount:
          type: integer
      type: object
    immerle.hallOfFameAddRequest:
      properties:
        id:
          type: string
      type: object
    immerle.hallOfFameNoteRequest:
      properties:
        comment:
          type: string
      type: object
    immerle.hallOfFameOrderRequest:
      properties:
        ids:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.hallOfFameView:
      properties:
        changedAt:
          type: string
        createdAt:
          type: string
        tracks:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
      type: object
    immerle.jamInviteRequest:
      properties:
        username:
          type: string
      type: object
    immerle.loginRequest:
      properties:
        device:
          description: >-
            Device is an optional human label for the session (defaults to the
            username).
          type: string
        password:
          type: string
        salt:
          type: string
        token:
          description: >-
            Token + Salt allow Subsonic-style token auth instead of a raw
            password.
          type: string
        username:
          type: string
      type: object
    immerle.lyricLine:
      properties:
        startMs:
          type: integer
        text:
          type: string
      type: object
    immerle.lyricsView:
      properties:
        lines:
          items:
            $ref: '#/components/schemas/immerle.lyricLine'
          type: array
          uniqueItems: false
        synced:
          type: boolean
      type: object
    immerle.nowPlayingView:
      properties:
        minutesAgo:
          type: integer
        song:
          $ref: '#/components/schemas/immerle.songView'
        username:
          type: string
      type: object
    immerle.participantView:
      properties:
        name:
          type: string
        role:
          type: string
      type: object
    immerle.passwordRequest:
      properties:
        password:
          type: string
      type: object
    immerle.playQueueCommandRequest:
      properties:
        forTarget:
          description: >-
            ForTarget is the sender's view of the current active device id — the

            receiver ignores this command if it isn't (or is no longer) that
            device.
          type: string
        issuedBy:
          description: IssuedBy is the sending device's id.
          type: string
        positionMs:
          description: PositionMs is the target position for a "seekTo" command.
          type: integer
        queueIndex:
          description: >-
            QueueIndex disambiguates "skipTo" if TrackID appears more than once
            in

            the queue — a hint only, never the primary lookup.
          type: integer
        trackId:
          description: TrackID is the track to jump to for a "skipTo" command.
          type: string
        type:
          description: |-
            Type is one of "toggle", "next", "previous", "seekTo", "skipTo",
            "toggleShuffle", "cycleRepeat".
          type: string
      type: object
    immerle.playQueueRequest:
      properties:
        client:
          type: string
        current:
          type: string
        entries:
          description: |-
            Entries carries display metadata per id as a fallback when a track
            can't be resolved via the local catalog (typically a not-yet-
            downloaded remote track), so it still shows correctly when this
            queue is mirrored on another device. Optional.
          items:
            $ref: '#/components/schemas/immerle.queueEntryRequest'
          type: array
          uniqueItems: false
        ids:
          items:
            type: string
          type: array
          uniqueItems: false
        playing:
          description: |-
            Playing reports whether Current is playing (vs paused). A spectator
            device (see TargetDeviceID) can also use this write to push a
            play/pause/skip command — the active device applies it once it
            notices Current/Playing changed on its next poll.
          type: boolean
        position:
          type: integer
        repeat:
          type: string
        shuffle:
          description: |-
            Shuffle/Repeat mirror this device's transport mode (see
            models.PlayQueue.Shuffle/Repeat) — carried through so another device
            that mirrors or takes over this queue shows/resumes the same mode.
          type: boolean
      type: object
    immerle.playQueueView:
      properties:
        changedAt:
          type: string
        changedBy:
          type: string
        commandSeq:
          type: integer
        current:
          type: string
        entries:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
        pendingCommand:
          $ref: '#/components/schemas/immerle.commandView'
        playing:
          description: |-
            Playing reports whether Current was playing (vs paused) as of
            ChangedAt — see playQueueRequest.Playing.
          type: boolean
        position:
          type: integer
        repeat:
          type: string
        shuffle:
          description: >-
            Shuffle/Repeat mirror the saving device's transport mode, so any
            device

            that mirrors or takes over this queue shows/resumes the same mode.
          type: boolean
        targetDeviceId:
          description: |-
            TargetDeviceID, when set, is the sole device that should be actively
            playing this queue — every other device should pause. Empty means
            unrestricted (default): each device plays independently.
          type: string
      type: object
    immerle.playbackTargetView:
      properties:
        id:
          type: string
        lastUsedAt:
          type: string
        name:
          type: string
      type: object
    immerle.playlistCreateRequest:
      properties:
        ids:
          items:
            type: string
          type: array
          uniqueItems: false
        name:
          type: string
      type: object
    immerle.playlistTracksRequest:
      properties:
        ids:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.playlistUpdateRequest:
      properties:
        addIds:
          items:
            type: string
          type: array
          uniqueItems: false
        comment:
          type: string
        name:
          type: string
        public:
          type: boolean
        removeIndexes:
          items:
            type: integer
          type: array
          uniqueItems: false
      type: object
    immerle.playlistView:
      properties:
        changedAt:
          type: string
        collaborative:
          type: boolean
        comment:
          type: string
        coverArt:
          type: string
        coverArts:
          items:
            type: string
          type: array
          uniqueItems: false
        createdAt:
          type: string
        duration:
          type: integer
        federated:
          description: |-
            Federated marks a read-only playlist synced from the hub: `owner` is
            attribution only, not real ownership — clients must offer
            subscribe/unsubscribe, not edit/delete/cover.
          type: boolean
        id:
          type: string
        name:
          type: string
        owner:
          type: string
        public:
          type: boolean
        songCount:
          type: integer
        subscribed:
          description: |-
            Subscribed reports whether the caller has favorited this playlist.
            Only set on the single-playlist resource (handleGetPlaylist), false
            elsewhere.
          type: boolean
        tracks:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
      type: object
    immerle.podcastCreateRequest:
      properties:
        url:
          type: string
      type: object
    immerle.podcastProviderRequest:
      properties:
        config:
          additionalProperties:
            type: string
          type: object
        enabled:
          type: boolean
      type: object
    immerle.queueEntryRequest:
      properties:
        album:
          type: string
        artist:
          type: string
        coverArt:
          type: string
        duration:
          type: integer
        id:
          type: string
        remote:
          type: boolean
        title:
          type: string
      type: object
    immerle.radioRequest:
      properties:
        coverUrl:
          description: >-
            CoverURL is the station logo source URL (fetched + cached
            server-side).
          type: string
        homepageUrl:
          type: string
        name:
          type: string
        streamUrl:
          type: string
      type: object
    immerle.radioToggleRequest:
      properties:
        enabled:
          type: boolean
      type: object
    immerle.ratingRequest:
      properties:
        rating:
          type: integer
      type: object
    immerle.reorderRequest:
      properties:
        order:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.resolveItemRequest:
      properties:
        query:
          type: string
      type: object
    immerle.scrobbleRequest:
      properties:
        ids:
          items:
            type: string
          type: array
          uniqueItems: false
        playedAt:
          type: integer
        submission:
          type: boolean
      type: object
    immerle.searchHitView:
      properties:
        album:
          $ref: '#/components/schemas/immerle.albumView'
        artist:
          $ref: '#/components/schemas/immerle.artistView'
        playlist:
          $ref: '#/components/schemas/immerle.playlistView'
        radio:
          $ref: '#/components/schemas/immerle.stationView'
        song:
          $ref: '#/components/schemas/immerle.songView'
        type:
          description: artist|album|song|playlist|radio
          type: string
      type: object
    immerle.searchView:
      properties:
        results:
          items:
            $ref: '#/components/schemas/immerle.searchHitView'
          type: array
          uniqueItems: false
      type: object
    immerle.setEnabledRequest:
      properties:
        enabled:
          type: boolean
      type: object
    immerle.setPlaybackTargetRequest:
      properties:
        deviceId:
          description: >-
            DeviceID is the device to make the sole active player; empty clears
            the

            restriction so every device plays independently again.
          type: string
      type: object
    immerle.shareCreateRequest:
      properties:
        description:
          type: string
        expiresAt:
          type: integer
        itemId:
          type: string
      type: object
    immerle.shareUpdateRequest:
      properties:
        description:
          type: string
        expiresAt:
          type: integer
      type: object
    immerle.shareView:
      properties:
        createdAt:
          type: string
        description:
          type: string
        entries:
          items:
            $ref: '#/components/schemas/immerle.songView'
          type: array
          uniqueItems: false
        expiresAt:
          type: string
        id:
          type: string
        url:
          type: string
        viewCount:
          type: integer
      type: object
    immerle.smartToggleRequest:
      properties:
        enabled:
          type: boolean
      type: object
    immerle.songLocalStatusView:
      properties:
        local:
          type: boolean
        song:
          $ref: '#/components/schemas/immerle.songView'
      type: object
    immerle.songView:
      properties:
        album:
          type: string
        albumId:
          type: string
        artist:
          type: string
        artistId:
          type: string
        bpm:
          type: integer
        comment:
          description: >-
            Comment is a personal nostalgia note on this track (e.g. "listened
            to

            this in college"). Only populated on Hall of Fame entries.
          type: string
        composer:
          type: string
        contentType:
          type: string
        coverArt:
          type: string
        duration:
          type: integer
        genre:
          type: string
        id:
          type: string
        lyrics:
          type: string
        movementName:
          type: string
        movementNumber:
          type: integer
        participants:
          items:
            $ref: '#/components/schemas/immerle.participantView'
          type: array
          uniqueItems: false
        playCount:
          type: integer
        rating:
          type: integer
        remote:
          description: >-
            Remote marks a track not yet downloaded (an on-demand provider
            result,

            not a row in the local catalog). Playing it streams progressively —
            the

            server can't yet serve byte ranges for it, so seeking isn't
            available

            until the background download finishes and it's replayed.
          type: boolean
        replayGainAlbum:
          type: number
        replayGainTrack:
          type: number
        size:
          type: integer
        starred:
          description: >-
            Per-user annotation state, populated only on catalog reads
            (album/artist/

            song/search/playlist). Absent on upload/admin views, where it is not
            loaded.
          type: string
        suffix:
          type: string
        title:
          type: string
        titleSort:
          type: string
        track:
          type: integer
        unresolved:
          description: >-
            Unresolved marks a federated-playlist entry not yet matched to a
            local

            track: id is empty and only title/artist/album are populated.
            Resolve it

            via POST /playlists/{id}/tracks/{position}/resolve before playing.
          type: boolean
        work:
          type: string
        year:
          type: integer
      type: object
    immerle.startImportRequest:
      properties:
        ref:
          type: string
        source:
          type: string
      type: object
    immerle.stationView:
      properties:
        builtin:
          type: boolean
        country:
          type: string
        coverUrl:
          description: >-
            CoverURL is the external logo source URL (for prefilling the admin
            edit

            form). Empty for built-ins whose logo is an embedded asset.
          type: string
        deletable:
          description: >-
            Deletable is false for built-in stations (they can be edited, not
            removed).
          type: boolean
        hasCover:
          description: HasCover tells clients whether to load the station cover endpoint.
          type: boolean
        homepageUrl:
          type: string
        id:
          type: string
        liked:
          description: Liked is true when the caller has favorited this station.
          type: boolean
        name:
          type: string
        streamUrl:
          type: string
      type: object
    immerle.streamURLs:
      properties:
        download:
          type: string
        stream:
          type: string
      type: object
    immerle.toggleRequest:
      properties:
        enabled:
          type: boolean
      type: object
    immerle.trackUpdateRequest:
      properties:
        discNo:
          type: integer
        genre:
          type: string
        title:
          type: string
        trackNo:
          type: integer
        year:
          type: integer
      type: object
    immerle.updateAccountRequest:
      properties:
        displayName:
          type: string
        email:
          type: string
        language:
          type: string
      type: object
    immerle.updateJamRequest:
      properties:
        currentTrackId:
          type: string
        position:
          type: integer
        state:
          type: string
        trackIds:
          items:
            type: string
          type: array
          uniqueItems: false
      type: object
    immerle.updateThemeRequest:
      properties:
        accentColor:
          type: string
      type: object
    immerle.upsertProviderRequest:
      properties:
        config:
          type: string
        enabled:
          type: boolean
        endpoint:
          type: string
        kind:
          type: string
        name:
          type: string
      type: object
  securitySchemes:
    BearerAuth:
      description: Type "Bearer <token>" where token is a device JWT or API token.
      in: header
      name: Authorization
      type: apiKey
info:
  contact:
    name: immerle
  description: |-
    Native immerle REST API (first-run setup, capability
    discovery, activity feed, collaborative playlists and
    synchronized Jam sessions) that complements the Subsonic /
    OpenSubsonic API served under /rest/.

    Served under /api/v1. Authenticated endpoints require a Bearer
    token in the Authorization header — a device JWT (obtained from
    POST /auth/sessions) or a personal API token. Setup, capability
    discovery and session creation are unauthenticated.
  license:
    name: See repository
  title: immerle extension API
  version: 1.0.0
externalDocs:
  description: ''
  url: ''
paths:
  /activity:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ActivityEventDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Activity feed
      tags:
        - activity
  /admin/autoplaylists/sync:
    post:
      description: >-
        Admin only. Rebuilds every genre and decade auto-playlist from the local
        catalog immediately, returning how many synced successfully.
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ChartsSyncDTO'
          description: Created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Sync genre/decade auto-playlists now
      tags:
        - admin
  /admin/charts/sync:
    post:
      description: >-
        Admin only. Fetches and upserts every curated chart playlist
        immediately, returning how many synced successfully.
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ChartsSyncDTO'
          description: Created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Sync curated chart playlists now
      tags:
        - admin
  /admin/cleanup:
    get:
      description: Admin only. Reports the eviction sweep state.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.CleanupStatusDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Get the cleanup sweep state
      tags:
        - admin
    put:
      description: >-
        Admin only. Enables or disables the background eviction sweep at runtime
        (persisted; hot).
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.cleanupUpdateRequest'
                  summary: body
                  description: Enable or disable the sweep
        description: Enable or disable the sweep
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.CleanupStatusDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Toggle the cleanup sweep
      tags:
        - admin
  /admin/cleanup/runs:
    post:
      description: >-
        Admin only. Runs one eviction pass immediately and returns how many
        provider downloads were removed. Works even when the background sweep is
        disabled.
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.CleanupRunDTO'
          description: Created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Run the cleanup sweep now
      tags:
        - admin
  /admin/concerts:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ConcertsStatusDTO'
          description: OK
      security:
        - BearerAuth: []
      summary: Get the concert-discovery feature state
      tags:
        - admin
    put:
      description: >-
        Admin only. Partial update — only fields present are changed. API keys
        are write-only: the response never echoes them back.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.concertsUpdateRequest'
                  summary: body
                  description: Fields to change
        description: Fields to change
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ConcertsStatusDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Update concert-discovery settings
      tags:
        - admin
  /admin/concerts/sync:
    post:
      description: >-
        Admin only. Searches every user's top-listened artists for upcoming
        shows near the configured country immediately, returning how many new
        matches were found.
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ChartsSyncDTO'
          description: Created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Sync concert discovery now
      tags:
        - admin
  /admin/federation:
    delete:
      description: >-
        Admin only. Deletes this instance's data on the hub (best-effort) and
        clears the locally stored hub identity, returning the instance to the
        unlinked state. Returns the refreshed runtime settings.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Unlink this instance from the hub
      tags:
        - admin
    get:
      description: >-
        Admin only. Fetches this instance's current name and sqid handle from
        the hub (the source of truth) and persists them, then returns the
        refreshed runtime settings. The HTTP exchange runs server-side.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Conflict
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Get the live hub instance profile
      tags:
        - admin
    patch:
      description: >-
        Admin only. Pushes the instance name and sqid (the editable, unique hub
        handle) to the hub, which validates sqid uniqueness, then persists the
        hub-canonical values. The HTTP exchange runs server-side. Returns the
        refreshed runtime settings.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.FederationUpdateDTO'
                  summary: body
                  description: Instance name and sqid
        description: Instance name and sqid
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Conflict
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Update this instance on the hub
      tags:
        - admin
  /admin/federation/instances:
    get:
      description: >-
        Admin only. Searches the hub for other instances by exact sqid or name
        (the hub excludes this instance and revoked ones). The HTTP exchange
        runs server-side.
      parameters:
        - description: Search query (sqid or name)
          in: query
          name: q
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.FederationInstancesDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Discover instances on the hub
      tags:
        - admin
  /admin/federation/register:
    post:
      description: >-
        Admin only. Bootstraps the instance under the configured hub user id
        (federation.userId) and persists the hub-issued identity (instance UUID,
        sqid handle and private key). The full HTTP exchange runs server-side.
        Returns the refreshed runtime settings.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Link with the hub
      tags:
        - admin
  /admin/federation/subscriptions:
    get:
      description: >-
        Admin only. Returns the instances this one follows on the hub. The HTTP
        exchange runs server-side.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.FederationSubscriptionsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: List hub subscriptions
      tags:
        - admin
    post:
      description: >-
        Admin only. Follows a target instance on the hub by instanceId (UUID) or
        sqid. The HTTP exchange runs server-side.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.SubscribeRequestDTO'
                  summary: body
                  description: Target instance id or sqid
        description: Target instance id or sqid
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.OkResponse'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Subscribe to an instance
      tags:
        - admin
  /admin/federation/subscriptions/{id}:
    delete:
      description: >-
        Admin only. Stops following the instance with the given hub id (UUID).
        The HTTP exchange runs server-side.
      parameters:
        - description: Target instance id (UUID)
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.OkResponse'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '502':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Gateway
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Unsubscribe from an instance
      tags:
        - admin
  /admin/hall-of-fame:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Get the Hall of Fame feature state
      tags:
        - admin
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.toggleRequest'
                  summary: body
                  description: Enable or disable
        description: Enable or disable
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Toggle the Hall of Fame feature
      tags:
        - admin
  /admin/logs/stream:
    get:
      description: >-
        Admin only. Server-Sent Events stream of structured JSON log lines.
        Sends recent history immediately, then every new line as it's logged.
      responses:
        '200':
          content:
            text/event-stream:
              schema:
                type: string
          description: SSE stream
        '401':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
      security:
        - BearerAuth: []
      summary: Stream server logs (SSE)
      tags:
        - admin
  /admin/offline:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Get the offline-downloads feature state
      tags:
        - admin
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.toggleRequest'
                  summary: body
                  description: Enable or disable
        description: Enable or disable
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Toggle the offline-downloads feature
      tags:
        - admin
  /admin/podcasts:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.podcastCreateRequest'
                  summary: body
                  description: Feed URL
        description: Feed URL
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.channelView'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Subscribe to a podcast feed
      tags:
        - admin
  /admin/podcasts/episodes/{id}:
    delete:
      parameters:
        - description: Episode id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Delete a podcast episode
      tags:
        - admin
  /admin/podcasts/providers:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: List podcast directory providers
      tags:
        - admin
  /admin/podcasts/providers/{name}:
    put:
      parameters:
        - description: Provider name
          in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.podcastProviderRequest'
                  summary: body
                  description: Enabled + config
        description: Enabled + config
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Configure a podcast directory provider
      tags:
        - admin
  /admin/podcasts/refresh:
    post:
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Refresh all podcast feeds
      tags:
        - admin
  /admin/podcasts/search:
    get:
      parameters:
        - description: Search terms
          in: query
          name: q
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Search podcast directories
      tags:
        - admin
  /admin/podcasts/{id}:
    delete:
      parameters:
        - description: Channel id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Delete a podcast channel
      tags:
        - admin
  /admin/providers:
    get:
      description: >-
        Admin only. Lists configured providers (built-in and dynamic) with their
        live status.
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ProviderDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: List on-demand providers
      tags:
        - admin
    post:
      description: >-
        Admin only. With only an endpoint (no name), creates an HTTP provider
        from its URL by probing /capabilities. With a name, updates it (HTTP
        config is validated against /capabilities).
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.upsertProviderRequest'
                  summary: body
                  description: Provider config
        description: Provider config
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ProviderDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Create or update an on-demand provider
      tags:
        - admin
  /admin/providers/order:
    put:
      description: >-
        Admin only. Sets the provider priority order (lower = higher priority).
        `order` lists every provider name, each exactly once. Order also decides
        which provider search falls back to when no explicit default is set.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.reorderRequest'
                  summary: body
                  description: Provider names in the desired order
        description: Provider names in the desired order
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ProviderDTO'
                type: array
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Reorder providers
      tags:
        - admin
  /admin/providers/{name}:
    delete:
      description: Admin only. Removes a provider config and unregisters it.
      parameters:
        - description: Provider name
          in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '204':
          description: deleted
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Delete a provider
      tags:
        - admin
  /admin/providers/{name}/enabled:
    put:
      description: >-
        Admin only. Toggles a provider on or off; the change is applied to the
        live registry immediately.
      parameters:
        - description: Provider name
          in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.setEnabledRequest'
                  summary: body
                  description: Enabled flag
        description: Enabled flag
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ProviderDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Enable or disable a provider
      tags:
        - admin
  /admin/providers/{name}/logs:
    get:
      description: >-
        Admin only. Returns the most recent provider action failures
        (search/resolve/download), newest first.
      parameters:
        - description: Provider name
          in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ProviderLogDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: List a provider's recent warn/error events
      tags:
        - admin
  /admin/radio:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Get the radio feature state
      tags:
        - admin
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.radioToggleRequest'
                  summary: body
                  description: Enable or disable
        description: Enable or disable
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Toggle the radio feature
      tags:
        - admin
  /admin/radio/stations:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.radioRequest'
                  summary: body
                  description: Station
        description: Station
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.stationView'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Create a radio station
      tags:
        - admin
  /admin/radio/stations/{id}:
    delete:
      parameters:
        - description: Station id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Delete a radio station
      tags:
        - admin
    put:
      parameters:
        - description: Station id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.radioRequest'
                  summary: body
                  description: Station
        description: Station
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.stationView'
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Update a radio station
      tags:
        - admin
  /admin/settings:
    get:
      description: >-
        Admin only. Returns the current runtime settings (provider behaviour,
        artist avatars, scan cadence, federation) plus whether a restart is
        pending.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Get runtime settings
      tags:
        - admin
    patch:
      description: >-
        Admin only. Partial update (send a JSON body with the fields to change;
        omitted fields keep their current value). Provider behaviour and the
        scan interval apply immediately (hot reload); avatars, the scan watcher
        and federation only take effect after a restart — the response sets
        restartRequired=true and lists the pending fields.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.RuntimeSettingsDTO'
                  summary: body
                  description: Settings fields to change (partial)
        description: Settings fields to change (partial)
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SettingsDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Update runtime settings
      tags:
        - admin
  /admin/smart-playlists:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Get the smart-playlists feature state
      tags:
        - admin
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.smartToggleRequest'
                  summary: body
                  description: Enable or disable
        description: Enable or disable
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Toggle the smart-playlists feature
      tags:
        - admin
  /admin/tracks:
    get:
      description: >-
        Admin only. Lists downloaded (local) tracks, newest first, with optional
        search and pagination.
      parameters:
        - description: Case-insensitive search over title/artist/album
          in: query
          name: query
          schema:
            type: string
        - description: Page size (default 50, max 200)
          in: query
          name: limit
          schema:
            type: integer
        - description: Offset for pagination
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.TrackListDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: List library tracks
      tags:
        - admin
  /admin/tracks/{id}:
    delete:
      description: >-
        Admin only. Removes the audio file and the track and all rows
        referencing it (annotations, shares, activity, downloads, playlist
        entries, scrobbles).
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Delete a track
      tags:
        - admin
    patch:
      description: >-
        Admin only. Edits a track's title, genre, year and track/disc number.
        Album and artist links are not changed.
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.trackUpdateRequest'
                  summary: body
                  description: Fields to update (all optional)
        description: Fields to update (all optional)
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.TrackDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Edit track metadata
      tags:
        - admin
  /admin/tracks/{id}/cover:
    put:
      description: >-
        Admin only. Replaces a single track's cover art with an uploaded image
        (multipart form field "file").
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              title: file
              type: file
          multipart/form-data:
            schema:
              type: object
        description: Cover image (jpeg/png/gif/webp)
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.TrackDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '415':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unsupported Media Type
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Upload track cover
      tags:
        - admin
  /admin/users:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.adminUserView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
      security:
        - BearerAuth: []
      summary: List users
      tags:
        - admin
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.adminUserCreateRequest'
                  summary: body
                  description: User
        description: User
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.adminUserView'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
      security:
        - BearerAuth: []
      summary: Create user
      tags:
        - admin
  /admin/users/{username}:
    delete:
      parameters:
        - description: Username
          in: path
          name: username
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Delete user
      tags:
        - admin
    get:
      parameters:
        - description: Username
          in: path
          name: username
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.adminUserView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get user
      tags:
        - admin
    patch:
      parameters:
        - description: Username
          in: path
          name: username
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.adminUserUpdateRequest'
                  summary: body
                  description: Changes
        description: Changes
        required: true
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Update user
      tags:
        - admin
  /admin/wrapped:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Get the Wrapped feature state
      tags:
        - admin
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.toggleRequest'
                  summary: body
                  description: Enable or disable
        description: Enable or disable
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  type: boolean
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Toggle the Wrapped feature
      tags:
        - admin
  /albums:
    get:
      description: >-
        Returns albums filtered/sorted by type (newest, recent, frequent,
        random, alphabeticalByName, byGenre, byYear, starred) with paging.
      parameters:
        - description: List type
          in: query
          name: type
          schema:
            default: alphabeticalByName
            type: string
        - description: Page size
          in: query
          name: size
          schema:
            default: 10
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
        - description: Genre (for byGenre)
          in: query
          name: genre
          schema:
            type: string
        - description: From year (for byYear)
          in: query
          name: fromYear
          schema:
            type: integer
        - description: To year (for byYear)
          in: query
          name: toYear
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.albumView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List albums
      tags:
        - catalog
  /albums/{id}:
    get:
      description: Returns an album with its merged (local + remote) tracklist.
      parameters:
        - description: Album id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.albumView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get album
      tags:
        - catalog
  /albums/{id}/star:
    delete:
      parameters:
        - description: Album id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Unfavorite an album
      tags:
        - favorites
    put:
      parameters:
        - description: Album id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Favorite an album
      tags:
        - favorites
  /artists:
    get:
      description: >-
        Returns every artist in the catalog with the caller's per-artist starred
        state.
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.artistView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List artists
      tags:
        - catalog
  /artists/{id}:
    get:
      description: >-
        Returns an artist with its (local + remote) albums. With ?songs=true
        each album's tracks are inlined.
      parameters:
        - description: Artist id
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Inline each album's tracks
          in: query
          name: songs
          schema:
            type: boolean
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.artistView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get artist
      tags:
        - catalog
  /artists/{id}/star:
    delete:
      parameters:
        - description: Artist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Unfavorite an artist
      tags:
        - favorites
    put:
      parameters:
        - description: Artist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Favorite an artist
      tags:
        - favorites
  /auth/sessions:
    post:
      description: >-
        Authenticates with username + password (or Subsonic token auth) and
        returns a device-session JWT carrying a unique id (jti). Use it as
        "Authorization: Bearer <jwt>". The session is tracked in the devices
        registry and can be revoked.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.loginRequest'
                  summary: body
                  description: Credentials
        description: Credentials
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.LoginDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      summary: Create a device session (issue a JWT)
      tags:
        - devices
  /capabilities:
    get:
      description: >-
        Unauthenticated. Lets clients detect supported immerle extensions and
        whether first-run setup is still needed.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.CapabilitiesDTO'
          description: OK
      summary: Capability discovery
      tags:
        - discovery
  /cover/{id}:
    get:
      description: >-
        Returns the cover image for a track or album id, optionally resized. The
        id "generator" instead builds a cover on the fly from its own query
        params (icon, title, subTitle, color, color2, angle); title/subTitle may
        be a known i18n key (e.g. "charts.top50") resolved via locale, or
        literal text.
      parameters:
        - description: Track/album id, or \
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Square size in pixels
          in: query
          name: size
          schema:
            type: integer
        - description: >-
            Label language for a generator cover's title/subTitle i18n keys
            (e.g. \
          in: query
          name: locale
          schema:
            type: string
        - description: 'Generator: Twemoji codepoint, e.g. \'
          in: query
          name: icon
          schema:
            type: string
        - description: 'Generator: title text or i18n key'
          in: query
          name: title
          schema:
            type: string
        - description: 'Generator: subtitle text or i18n key'
          in: query
          name: subTitle
          schema:
            type: string
        - description: 'Generator: background color, hex'
          in: query
          name: color
          schema:
            type: string
        - description: 'Generator: gradient end color, hex (empty = solid)'
          in: query
          name: color2
          schema:
            type: string
        - description: 'Generator: gradient angle, degrees'
          in: query
          name: angle
          schema:
            type: number
      responses:
        '200':
          content:
            image/jpeg:
              schema:
                format: binary
                type: string
          description: Image bytes
        '401':
          content:
            image/jpeg:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            image/jpeg:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Cover art
      tags:
        - media
  /devices:
    get:
      description: >-
        Lists the caller's active device sessions (one per issued JWT), with
        last-seen time, IP, user agent, and whether it's currently connected
        (seen recently).
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.DeviceDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: List devices
      tags:
        - devices
  /devices/{id}:
    delete:
      description: >-
        Revokes a device session by id — the associated JWT can no longer
        authenticate.
      parameters:
        - description: Device id (jti) to revoke
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: revoked
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Revoke a device
      tags:
        - devices
  /genres:
    get:
      description: Returns every genre with its song and album counts.
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.genreView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List genres
      tags:
        - catalog
  /hall-of-fame:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.hallOfFameView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get (or create) the caller's Hall of Fame
      tags:
        - playlists
  /hall-of-fame/tracks:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.hallOfFameAddRequest'
                  summary: body
                  description: Track id
        description: Track id
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Add a track to the Hall of Fame
      tags:
        - playlists
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.hallOfFameOrderRequest'
                  summary: body
                  description: Track ids, in rank order
        description: Track ids, in rank order
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.hallOfFameView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Set the Hall of Fame's track order
      tags:
        - playlists
  /hall-of-fame/tracks/{trackId}/note:
    patch:
      parameters:
        - description: Track id
          in: path
          name: trackId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.hallOfFameNoteRequest'
                  summary: body
                  description: Note
        description: Note
        required: true
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Set a Hall of Fame track's note
      tags:
        - playlists
  /imports:
    get:
      description: >-
        Lists the caller's playlist imports (most recent first), without
        per-track items.
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ImportDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: List imports
      tags:
        - imports
    post:
      description: >-
        Queues an import of an external playlist (by source + reference).
        Returns the import job; poll GET /imports/{id} for progress. The import
        creates a new immerle playlist and resolves each source track against
        the on-demand content providers.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.startImportRequest'
                  summary: body
                  description: Import source + reference
        description: Import source + reference
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ImportDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Start a playlist import
      tags:
        - imports
  /imports/sources:
    get:
      description: >-
        Lists the available playlist-import sources (e.g. spotify) and whether
        each is configured.
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.ImportSourceDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List import sources
      tags:
        - imports
  /imports/{id}:
    get:
      description: >-
        Returns one import with its per-track items and their status
        (matched/doubtful/missing/failed) for a progress page.
      parameters:
        - description: Import id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ImportDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Import status
      tags:
        - imports
  /imports/{id}/items/{itemId}/resolve:
    post:
      description: >-
        Resolves a doubtful/missing/failed import item: downloads a track and
        adds it to the import's playlist, flipping the item to "matched". With
        no `query`, it validates the flagged candidate as-is; with a `query`, it
        re-searches the content providers with that corrected text and uses the
        best result.
      parameters:
        - description: Import id
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Import item id
          in: path
          name: itemId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.resolveItemRequest'
                  summary: body
                  description: Optional corrected 'artist title' search
        description: Optional corrected 'artist title' search
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ImportItemDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Validate or modify an import item
      tags:
        - imports
  /jam:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.createJamRequest'
                  summary: body
                  description: Session name and initial track ids
        description: Session name and initial track ids
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.JamDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Create a Jam session
      tags:
        - jam
  /jam/invites/mine:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.JamInviteDTO'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: List the caller's pending Jam invites
      tags:
        - jam
  /jam/invites/{id}:
    delete:
      parameters:
        - description: Invite id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: dismissed
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Dismiss a pending Jam invite
      tags:
        - jam
  /jam/mine:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.JamDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get the caller's hosted Jam session
      tags:
        - jam
  /jam/{id}:
    delete:
      description: Ends and removes the session. Host only.
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: ended
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: End a Jam session
      tags:
        - jam
    get:
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.JamDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get Jam session state
      tags:
        - jam
    patch:
      description: >-
        Host-only. Updates the shared track/position/state (partial) and
        broadcasts it to participants over SSE.
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.updateJamRequest'
                  summary: body
                  description: Playback fields to change
        description: Playback fields to change
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.JamDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Update Jam playback (host only)
      tags:
        - jam
  /jam/{id}/events:
    get:
      description: >-
        Server-Sent Events stream. Emits the current state immediately, then a
        "state"/"participants"/"closed" event on every change.
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            text/event-stream:
              schema:
                type: string
          description: SSE stream
        '401':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Stream Jam events (SSE)
      tags:
        - jam
  /jam/{id}/invites:
    post:
      description: >-
        Invites a user to the session. Host only; re-inviting just refreshes the
        invite.
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.jamInviteRequest'
                  summary: body
                  description: Invitee username
        description: Invitee username
        required: true
      responses:
        '204':
          description: invited
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Invite a user to a Jam session
      tags:
        - jam
  /jam/{id}/participants:
    post:
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.JamDTO'
          description: Created
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Join a Jam session
      tags:
        - jam
  /jam/{id}/participants/me:
    delete:
      parameters:
        - description: Jam session id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: left
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Leave a Jam session
      tags:
        - jam
  /library/stats:
    get:
      description: >-
        Returns library-wide analytics: artist/album/track counts, total on-disk
        size (bytes) and total duration (seconds). Cached and refreshed at each
        scan.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.LibraryStatsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Library analytics
      tags:
        - library
  /me:
    get:
      description: >-
        Returns the authenticated user's own account, including the private
        email.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.AccountDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Get your account
      tags:
        - users
    patch:
      description: >-
        Partial update — only fields present are changed. Lets a user set their
        display name and email themselves.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.updateAccountRequest'
                  summary: body
                  description: Account fields to change
        description: Account fields to change
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.AccountDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Update your account
      tags:
        - users
  /me/concerts:
    get:
      description: >-
        Concert discovery matches your top-listened artists against
        Ticketmaster/Skiddle/Eventim near the admin-configured country,
        refreshed daily. Returns upcoming, non-dismissed matches, soonest first.
        Empty (not an error) when the feature is disabled, no country is
        configured, or nothing matched yet.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ConcertsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Your upcoming concert matches
      tags:
        - concerts
  /me/concerts/{id}/dismiss:
    put:
      parameters:
        - description: Concert id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: dismissed
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Dismiss a concert match
      tags:
        - concerts
  /me/custom-playlists:
    get:
      description: >-
        Returns the caller's personal auto-generated playlists (top of the
        month, on repeat, forgotten favorites, random, recommended) that
        currently have at least one track.
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.playlistView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Custom auto-generated playlists
      tags:
        - playlists
  /me/favorites:
    get:
      description: Returns the artists, albums and songs the caller has starred.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.favoritesView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List favorites
      tags:
        - catalog
  /me/password:
    put:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.passwordRequest'
                  summary: body
                  description: New password
        description: New password
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Change own password
      tags:
        - account
  /me/purchases/bandcamp:
    delete:
      responses:
        '204':
          description: disconnected
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Disconnect Bandcamp
      tags:
        - purchases
    get:
      description: >-
        Reports whether the caller has connected their Bandcamp account, and
        whether the stored cookie needs to be refreshed.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.BandcampStatusDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Bandcamp connection status
      tags:
        - purchases
  /me/purchases/bandcamp/collection:
    get:
      description: >-
        Fetches the caller's purchase collection live from Bandcamp. Each item
        is annotated with its import job status, if any.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.BandcampCollectionDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: List Bandcamp purchases
      tags:
        - purchases
  /me/purchases/bandcamp/connect:
    post:
      description: >-
        Validates the pasted session cookie against Bandcamp and stores it
        (encrypted), replacing any previous connection.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.bandcampConnectRequest'
                  summary: body
                  description: Bandcamp session cookie
        description: Bandcamp session cookie
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.BandcampStatusDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Connect a Bandcamp account
      tags:
        - purchases
  /me/purchases/bandcamp/items/{saleItemType}/{saleItemId}/import:
    post:
      parameters:
        - description: Bandcamp sale item type
          in: path
          name: saleItemType
          required: true
          schema:
            type: string
        - description: Bandcamp sale item id
          in: path
          name: saleItemId
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.bandcampImportRequest'
                  summary: body
                  description: Item display fields
        description: Item display fields
        required: true
      responses:
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.BandcampJobDTO'
          description: Accepted
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Service Unavailable
      security:
        - BearerAuth: []
      summary: Import a Bandcamp purchase
      tags:
        - purchases
  /me/purchases/bandcamp/jobs:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.BandcampJobsDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List Bandcamp import jobs
      tags:
        - purchases
  /now-playing:
    get:
      description: Returns the tracks every user is currently playing, newest first.
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.nowPlayingView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Now playing
      tags:
        - playback
  /play-queue:
    get:
      description: Returns the caller's saved cross-device play queue with its tracks.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.playQueueView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Get play queue
      tags:
        - playback
    put:
      description: >-
        Replaces the caller's saved play queue (tracks, current track, position
        and playing state).
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.playQueueRequest'
                  summary: body
                  description: Play queue
        description: Play queue
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Save play queue
      tags:
        - playback
  /play-queue/commands:
    post:
      description: >-
        Sends a remote-control command (toggle, next, previous, seekTo, skipTo,
        toggleShuffle, cycleRepeat) for the active device (see targetDeviceId)
        to apply. Does not modify the saved queue state directly.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.playQueueCommandRequest'
                  summary: body
                  description: Command
        description: Command
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Send a play-queue command
      tags:
        - playback
  /play-queue/events:
    get:
      description: >-
        Server-Sent Events stream. Emits the current queue immediately, then
        again on every change (save, target change); also emits the caller's
        pending Jam invites (event "invites") on connect and whenever they
        change.
      responses:
        '200':
          content:
            text/event-stream:
              schema:
                type: string
          description: SSE stream
        '401':
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Stream play-queue events (SSE)
      tags:
        - playback
  /play-queue/target:
    put:
      description: >-
        Assigns (or, with an empty deviceId, clears) the sole device that should
        be actively playing the caller's queue.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.setPlaybackTargetRequest'
                  summary: body
                  description: Target device
        description: Target device
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Set the active playback device
      tags:
        - playback
  /play-queue/targets:
    get:
      description: >-
        Lists the caller's recently-active app installs (device-kind API
        tokens), for the "cast to device" picker.
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.playbackTargetView'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List playback targets
      tags:
        - playback
  /playlists:
    get:
      description: Returns the playlists the caller owns, subscribes to or collaborates on.
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.playlistView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List playlists
      tags:
        - playlists
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.playlistCreateRequest'
                  summary: body
                  description: Playlist
        description: Playlist
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.playlistView'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Create playlist
      tags:
        - playlists
  /playlists/public:
    get:
      description: >-
        Lists public playlists (not owned by the caller) available to subscribe
        to. Each entry includes whether the caller is already subscribed.
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.PublicPlaylistDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Browse public playlists
      tags:
        - playlists
  /playlists/{id}:
    delete:
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Delete playlist
      tags:
        - playlists
    get:
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.playlistView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get playlist
      tags:
        - playlists
    patch:
      description: >-
        Edits metadata and appends/removes tracks. Owner/admin/collaborator
        only.
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.playlistUpdateRequest'
                  summary: body
                  description: Changes
        description: Changes
        required: true
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Update playlist
      tags:
        - playlists
  /playlists/{id}/collaborators:
    post:
      description: >-
        Owner-only. Marks the playlist collaborative and grants edit rights to
        another user.
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.addCollaboratorBody'
                  summary: body
                  description: User to grant edit rights
        description: User to grant edit rights
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.apiError'
          description: added
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Add a playlist collaborator
      tags:
        - playlists
  /playlists/{id}/subscription:
    delete:
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: unsubscribed
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Unsubscribe from a playlist
      tags:
        - playlists
    put:
      description: >-
        Adds a public playlist to the caller's library (read-only). It then
        appears in getPlaylists like a normal playlist. Idempotent.
      parameters:
        - description: Playlist id to subscribe to
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: subscribed
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Subscribe to a public playlist
      tags:
        - playlists
  /playlists/{id}/tracks:
    put:
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.playlistTracksRequest'
                  summary: body
                  description: Track ids
        description: Track ids
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.playlistView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Replace playlist tracks
      tags:
        - playlists
  /playlists/{id}/tracks/{position}/resolve:
    post:
      description: >-
        Resolves an unresolved federated-playlist entry (checks the local
        catalog first, then the on-demand providers if enabled). 404 if it can't
        be resolved.
      parameters:
        - description: Playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Track position (0-based)
          in: path
          name: position
          required: true
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.songView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Forbidden
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Resolve a federated playlist track for playback
      tags:
        - playlists
  /podcasts:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: List podcast channels
      tags:
        - podcasts
  /podcasts/episodes/newest:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Newest podcast episodes
      tags:
        - podcasts
  /podcasts/episodes/{id}:
    get:
      parameters:
        - description: Episode id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.episodeView'
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get a podcast episode
      tags:
        - podcasts
  /podcasts/episodes/{id}/download:
    post:
      parameters:
        - description: Episode id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.episodeView'
          description: Accepted
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Download a podcast episode
      tags:
        - podcasts
  /podcasts/episodes/{id}/stream:
    get:
      parameters:
        - description: Episode id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Stream a podcast episode
      tags:
        - podcasts
  /podcasts/{id}:
    get:
      parameters:
        - description: Channel id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.channelView'
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get a podcast channel
      tags:
        - podcasts
  /radio:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: List internet radio stations
      tags:
        - radio
  /radio/stations/{id}/cover:
    get:
      parameters:
        - description: Station id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            image/png:
              schema:
                format: binary
                type: string
          description: OK
        '404':
          content:
            image/png:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      summary: Station logo
      tags:
        - radio
  /radio/stations/{id}/like:
    delete:
      parameters:
        - description: Station id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Unlike a radio station
      tags:
        - radio
    put:
      parameters:
        - description: Station id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Like a radio station
      tags:
        - radio
  /scrobbles:
    post:
      description: >-
        Records playback for one or more tracks (now-playing + optional
        submission).
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.scrobbleRequest'
                  summary: body
                  description: Scrobble
        description: Scrobble
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Scrobble plays
      tags:
        - plays
  /search:
    get:
      description: >-
        Searches artists, albums, songs, public playlists and radio stations
        (merging remote-provider results when enabled), returned as one list
        ranked by relevance to the query. `type` scopes the search server-side
        to just that result type.
      parameters:
        - description: Search query
          in: query
          name: q
          required: true
          schema:
            type: string
        - description: >-
            Scope to one result type: artist, album, song, playlist or radio
            (default: all)
          in: query
          name: type
          schema:
            type: string
        - description: Max artists
          in: query
          name: artistCount
          schema:
            default: 20
            type: integer
        - description: Max albums
          in: query
          name: albumCount
          schema:
            default: 20
            type: integer
        - description: Max songs
          in: query
          name: songCount
          schema:
            default: 20
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.searchView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Search the catalog
      tags:
        - catalog
  /setup:
    get:
      description: >-
        Unauthenticated. Reports whether the server still needs its first admin
        and whether a setup token is required.
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SetupStatusDTO'
          description: OK
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      summary: First-run setup status
      tags:
        - setup
    post:
      description: >-
        Unauthenticated, one-shot. Creates the initial admin. Self-locks once
        any user exists (also true when the admin was instead bootstrapped from
        ADMIN_USERNAME/ADMIN_PASSWORD at startup).
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.SetupInitRequest'
                  summary: body
                  description: Initial admin credentials
        description: Initial admin credentials
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.UserDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: validation
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: invalid_setup_token
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: already_initialized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      summary: Create the first administrator
      tags:
        - setup
  /shares:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.shareView'
                  type: array
                type: object
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List shares
      tags:
        - shares
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.shareCreateRequest'
                  summary: body
                  description: Share
        description: Share
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.shareView'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Create share
      tags:
        - shares
  /shares/{id}:
    delete:
      parameters:
        - description: Share id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Delete share
      tags:
        - shares
    patch:
      parameters:
        - description: Share id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.shareUpdateRequest'
                  summary: body
                  description: Changes
        description: Changes
        required: true
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Update share
      tags:
        - shares
  /smart-playlists:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: List smart playlists
      tags:
        - smartPlaylists
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.SmartPlaylistRequestDTO'
                  summary: body
                  description: Name + rules
        description: Name + rules
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SmartPlaylistDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
      security:
        - BearerAuth: []
      summary: Create a smart playlist
      tags:
        - smartPlaylists
  /smart-playlists/preview:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.SmartPlaylistRequestDTO'
                  summary: body
                  description: Rules to preview
        description: Rules to preview
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
      security:
        - BearerAuth: []
      summary: Preview smart-playlist rules
      tags:
        - smartPlaylists
  /smart-playlists/{id}:
    delete:
      parameters:
        - description: Smart playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
      security:
        - BearerAuth: []
      summary: Delete a smart playlist
      tags:
        - smartPlaylists
    put:
      parameters:
        - description: Smart playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.SmartPlaylistRequestDTO'
                  summary: body
                  description: Name + rules
        description: Name + rules
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.SmartPlaylistDTO'
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Update a smart playlist
      tags:
        - smartPlaylists
  /smart-playlists/{id}/tracks:
    get:
      parameters:
        - description: Smart playlist id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: {}
                type: object
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Resolve a smart playlist's tracks
      tags:
        - smartPlaylists
  /songs:
    get:
      description: Returns songs tagged with the given genre (paged).
      parameters:
        - description: Genre name
          in: query
          name: genre
          required: true
          schema:
            type: string
        - description: Page size
          in: query
          name: count
          schema:
            default: 200
            type: integer
        - description: Offset
          in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties:
                  items:
                    $ref: '#/components/schemas/immerle.songView'
                  type: array
                type: object
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: List songs by genre
      tags:
        - catalog
  /songs/{id}:
    get:
      description: Returns a single track by id.
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.songView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get song
      tags:
        - catalog
  /songs/{id}/download:
    get:
      description: Serves a track's original audio bytes (no transcoding).
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Audio bytes
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Download a track
      tags:
        - media
  /songs/{id}/local:
    get:
      description: >-
        Read-only, never downloads. Poll this while playing a remote
        (not-yet-local) track to know when it's safe to seek.
      parameters:
        - description: Track id (remote or local)
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.songLocalStatusView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Check whether a remote track has finished downloading
      tags:
        - catalog
  /songs/{id}/lyrics:
    get:
      description: >-
        Returns a track's lyrics. When the stored tags carry [mm:ss.xx]
        timestamps the document is "synced" and every line has a startMs (ms).
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.lyricsView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get song lyrics
      tags:
        - catalog
  /songs/{id}/rating:
    delete:
      parameters:
        - description: Item id (song, album or artist)
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Clear an item's rating
      tags:
        - ratings
    put:
      description: >-
        Sets the caller's rating (0–5) on the item. The item type is detected
        from the id.
      parameters:
        - description: Item id (song, album or artist)
          in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.ratingRequest'
                  summary: body
                  description: Rating
        description: Rating
        required: true
      responses:
        '204':
          description: No Content
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Rate an item
      tags:
        - ratings
  /songs/{id}/star:
    delete:
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Unfavorite a song
      tags:
        - favorites
    put:
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No Content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Favorite a song
      tags:
        - favorites
  /songs/{id}/stream:
    get:
      description: >-
        Streams a track's audio. Supports HTTP range requests; maxBitRate/format
        transcode when set. Remote tracks are streamed progressively on first
        listen.
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
        - description: Transcode to at most this bit rate (kbps)
          in: query
          name: maxBitRate
          schema:
            type: integer
        - description: Transcode format (or 'raw' for original)
          in: query
          name: format
          schema:
            type: string
      responses:
        '200':
          description: Audio stream
        '206':
          description: Partial content
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Stream a track
      tags:
        - media
  /songs/{id}/stream-url:
    get:
      description: >-
        Returns short-lived signed stream and download URLs for the track
        (usable as a plain media src). They expire after a few minutes.
      parameters:
        - description: Track id
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.streamURLs'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
      security:
        - BearerAuth: []
      summary: Mint signed media URLs
      tags:
        - media
  /theme:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ThemeDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Get the UI theme
      tags:
        - theme
    patch:
      description: >-
        Partial update — omitted fields keep their stored value; pass an empty
        accentColor to clear it.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.updateThemeRequest'
                  summary: body
                  description: Theme fields to change
        description: Theme fields to change
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ThemeDTO'
          description: OK
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Update the UI theme
      tags:
        - theme
  /tokens:
    get:
      description: Lists the caller's active personal access tokens (no secrets).
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/immerle.APITokenDTO'
                type: array
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: List API tokens
      tags:
        - tokens
    post:
      description: >-
        Creates a personal access token scoped to the caller. The secret is
        returned ONCE — store it now. Use it as "Authorization: Bearer <token>".
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                - $ref: '#/components/schemas/immerle.createTokenRequest'
                  summary: body
                  description: Token name and optional expiry
        description: Token name and optional expiry
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.CreateTokenDTO'
          description: Created
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Create an API token
      tags:
        - tokens
  /tokens/{id}:
    delete:
      parameters:
        - description: Token id to revoke
          in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '204':
          description: revoked
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Revoke an API token
      tags:
        - tokens
  /users/{username}:
    get:
      description: >-
        Returns a user's profile — identity, recent activity visible to the
        caller (honoring privacy), their public playlists, all-time listening
        stats, and the top of their Hall of Fame (omitted when empty). Use "me"
        for the caller.
      parameters:
        - description: Target username, or 'me' for the caller
          in: path
          name: username
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.ProfileDTO'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: User profile
      tags:
        - users
  /users/{username}/hall-of-fame:
    get:
      parameters:
        - description: Target username, or 'me' for the caller
          in: path
          name: username
          required: true
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.hallOfFameView'
          description: OK
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
      security:
        - BearerAuth: []
      summary: Get a user's Hall of Fame
      tags:
        - users
  /wrapped:
    get:
      description: >-
        Returns the caller's listening stats for a calendar year: totals, top
        tracks/artists/genres and a per-month histogram. 404 when the feature is
        disabled.
      parameters:
        - description: 'Calendar year (default: current)'
          in: query
          name: year
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.WrappedDTO'
          description: OK
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Not Found
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/immerle.errorResponse'
          description: Internal Server Error
      security:
        - BearerAuth: []
      summary: Year-in-review ("Wrapped")
      tags:
        - wrapped
openapi: 3.1.0
servers:
  - url: /api/v1
