openapi: 3.1.0
info:
  title: DiArMaqAr API
  version: 1.0.0
  description: |
    Digital Arabic Maqām Archive (DiArMaqAr) — programmatic access to Arabic maqāmāt, ajnās, and tuning systems data

    Key features:
    - Maqāmāt and ajnās database with bibliographic source attribution
    - Historical tuning systems with multiple starting note contexts
    - Multiple pitch class data formats (fractions, ratios, decimal ratio, string length, fret divisions, frequency, cents, MIDI notes, and more)
    - Tuning system sensitive transpositions
    - Modulations via al-Shawwā's 1946 modulation guidelines
    - Interactive API documentation with live endpoint testing capabilities 

  contact:
    name: Khyam Allami
    email: ka109@aub.edu.lb
    url: https://musicintelligencelab.com
  license:
    name: CC-BY-NC-SA 4.0
    url: https://creativecommons.org/licenses/by-nc-sa/4.0/
servers:
  - url: https://diarmaqar.netlify.app/api
    description: Production server
  - url: http://localhost:3000/api
    description: Development server
tags:
  - name: Maqāmāt
    description: |
      Maqām (pl. maqāmāt) — complete modal framework in Arabic music comprising
      an ascending tonal structure (ṣuʿūd) and descending counterpart (hubūṭ),
      typically built from two or more ajnās, along with characteristic sayr
      (melodic-development path). Each maqām realises differently across
      tuning systems and starting notes.
  - name: Ajnās
    description: |
      Jins (pl. ajnās) — fundamental building block of a maqām, a tetrachord
      (four notes), pentachord (five), or trichord (three) with a specific
      interval pattern. Multiple ajnās combine to form a maqām.
  - name: Tuning Systems
    description: |
      Tanāghīm (tuning systems) — historically documented pitch-class
      collections, from al-Kindī (874 CE) through al-Fārābī, Ibn Sīnā,
      Ṣafī al-Dīn, and into contemporary approaches. Each system defines
      which maqāmāt and ajnās can be realised and from which starting notes.
  - name: Intervals
    description: |
      Pitch-class intervals expressed in multiple formats (cents, ratios,
      fractions, string-length, frequency) between consecutive or arbitrary
      pitches within a tuning system.
  - name: Pitch Classes
    description: |
      Individual pitches identified by culturally-specific note names
      (e.g. yegāh, ʿushayrān, dūgāh, sīkāh) carried across tuning systems.
      Each note name maps to a different frequency depending on the tuning
      system in use.
  - name: Sources
    description: |
      Bibliographic sources — historical treatises and contemporary
      publications from which the archive's maqāmāt, ajnās, and tuning
      systems are drawn, with page-level citations.
  - name: Modulations
    description: |
      Intiqāl — modulation routes between maqāmāt, generated per al-Shawwā's
      1946 modulation guidelines. Modulations depend on the source maqām,
      tuning system, and starting note.
paths:
  /maqamat:
    get:
      tags:
        - Maqāmāt
      summary: List all maqāmāt
      description: |
        Retrieve all available maqāmāt with optional filtering and sorting.
        
        **Returns:** All maqāmāt with their tuning system availability information. This endpoint does **not** include note names or pitch class data—only metadata about which tuning systems can realize each maqām.
        
        Returns concise metadata including:
        - URL-safe identifiers and display names for each maqām
        - Maqām family and maqām tonic identifiers (both URL-safe and display formats)
        - Availability counts across tuning systems with their valid starting note names (e.g., "ʿushayrān", "yegāh" etc...)
        - Optional bibliographic source references (sourceId and page) when `includeSources=true`
        - Filter values must be URL-safe; see parameter enums for complete list of valid options
        
        **Note:** For detailed pitch class data, note names, and ajnās information, use the `/maqamat/{idName}` endpoint.
      operationId: listMaqamat
      parameters:
        - name: filterByFamily
          in: query
          description: |
            Filter by maqām family name (URL-safe, case-insensitive, diacritics-insensitive).
            Use GET /maqamat/families to retrieve valid family values.
          schema:
            type: string
            enum:
              - ajam
              - athar_kurd
              - awj_ara
              - bayyat
              - buselik
              - chahargah
              - hijaz
              - iraq
              - kurd
              - nahawand
              - nikriz
              - no_jins
              - rast
              - saba
              - saba_zamzam
              - segah
          example: rast
        - name: filterByTonic
          in: query
          description: Filter by maqām tonic/first note (URL-safe, case-insensitive, diacritics-insensitive)
          schema:
            type: string
            enum:
              - ajam_ushayran
              - chahargah
              - dugah
              - iraq
              - nawa
              - rast
              - segah
              - ushayran
              - yegah
            example: rast
        - name: sortBy
          in: query
          description: |
            Sort order for results. Invalid values (anything other than `alphabetical` or `tonic`,
            including empty string) return HTTP 400.
          schema:
            type: string
            enum: [alphabetical, tonic]
            default: alphabetical
          examples:
            alphabetical:
              value: alphabetical
              summary: Sort alphabetically by display name (default)
            tonic:
              value: tonic
              summary: Sort by tonic note priority across the full NoteName.ts octave-stratified order, alphabetical tiebreak
        - name: includeSources
          in: query
          required: false
          description: Include bibliographic source references (sourceId and page) for each maqām
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.
            
            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: List of maqāmāt retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaqamatListResponse'
              examples:
                default:
                  summary: Default response (includeArabic=false)
                  value:
                    count: 60
                    data:
                      - id: "29"
                        idName: "maqam_yegah"
                        displayName: "maqām yegāh"
                        version: "2025-10-18T19:41:17.132Z"
                        familyId: "no_jins"
                        familyDisplay: "no jins"
                        tonicId: "yegah"
                        tonicDisplay: "yegāh"
                        numberOfPitchClasses: 7
                        isOctaveRepeating: true
                        hasAsymmetricDescending: true
                        hasSuyur: true
                        availableInTuningSystems: 4
                        tuningSystemsAvailability:
                          - tuningSystemId: "alfarabi_950g"
                            tuningSystemStartingNoteNames: ["ʿushayrān"]
                          - tuningSystemId: "cairocongresstuningcommittee_1929"
                            tuningSystemStartingNoteNames: ["yegāh"]
                          - tuningSystemId: "alsabbagh_1954"
                            tuningSystemStartingNoteNames: ["rāst"]
                          - tuningSystemId: "allami_2025"
                            tuningSystemStartingNoteNames: ["rāst"]
                        maqamDetailsUrl: "/api/maqamat/maqam_yegah"
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "49"
                      - id: "30"
                        idName: "maqam_farahfazza"
                        displayName: "maqām faraḥfazzā"
                        version: "2025-10-18T19:41:17.132Z"
                        familyId: "no_jins"
                        familyDisplay: "no jins"
                        tonicId: "yegah"
                        tonicDisplay: "yegāh"
                        numberOfPitchClasses: 7
                        isOctaveRepeating: true
                        hasAsymmetricDescending: true
                        hasSuyur: true
                        availableInTuningSystems: 35
                        tuningSystemsAvailability:
                          - tuningSystemId: "ronzevalle_1904"
                            tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          - tuningSystemId: "alfarabi_950g"
                            tuningSystemStartingNoteNames: ["ʿushayrān"]
                        maqamDetailsUrl: "/api/maqamat/maqam_farahfazza"
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "50"
                bilingual:
                  summary: Bilingual response (includeArabic=true)
                  value:
                    count: 60
                    data:
                      - id: "29"
                        idName: "maqam_yegah"
                        displayName: "maqām yegāh"
                        displayNameAr: "مقام يگاه"
                        version: "2025-10-18T19:41:17.132Z"
                        familyId: "no_jins"
                        familyDisplay: "no jins"
                        familyDisplayAr: "لا جنس"
                        tonicId: "yegah"
                        tonicDisplay: "yegāh"
                        tonicDisplayAr: "يگاه"
                        numberOfPitchClasses: 7
                        isOctaveRepeating: true
                        hasAsymmetricDescending: true
                        hasSuyur: true
                        availableInTuningSystems: 4
                        tuningSystemsAvailability:
                          - tuningSystemId: "alfarabi_950g"
                            tuningSystemStartingNoteNames: ["ʿushayrān"]
                            tuningSystemStartingNoteNamesAr: ["عشيران"]
                          - tuningSystemId: "cairocongresstuningcommittee_1929"
                            tuningSystemStartingNoteNames: ["yegāh"]
                            tuningSystemStartingNoteNamesAr: ["يگاه"]
                        maqamDetailsUrl: "/api/maqamat/maqam_yegah"
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "49"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                emptyFamily:
                  summary: Empty filterByFamily parameter
                  value:
                    error: "Invalid parameter: filterByFamily"
                    message: "The 'filterByFamily' parameter cannot be empty. Either omit it or provide a valid maqām family name."
                    hint: "Remove '?filterByFamily=' from your URL, specify a family like '?filterByFamily=rast', or use GET /maqamat/families?tuningSystem=ibnsina_1037&startingNote=yegah for valid values"
                emptyTonic:
                  summary: Empty filterByTonic parameter
                  value:
                    error: "Invalid parameter: filterByTonic"
                    message: "The 'filterByTonic' parameter cannot be empty. Either omit it or provide a valid tonic note name."
                    hint: "Remove '?filterByTonic=' from your URL or specify a tonic like '?filterByTonic=rast'"
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/families:
    get:
      tags:
        - Maqāmāt
      summary: List maqām families
      description: |
        Retrieve maqām families for maqāmāt available in the specified tuning system.
        Families are classified by the first jins at scale degree 1. Use the `maqamat`
        link to filter maqāmāt by family via GET /maqamat?filterByFamily={idName}.
      operationId: listMaqamFamilies
      parameters:
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., ibnsina_1037, alsabbagh_1954)
          schema:
            type: string
          example: ibnsina_1037
        - name: startingNote
          in: query
          required: true
          description: Starting note name (URL-safe, diacritics-insensitive)
          schema:
            type: string
          example: yegah
        - name: includeArabic
          in: query
          required: false
          description: Include Arabic display names in family objects
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
      responses:
        '400':
          description: Missing required parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Tuning system or starting note not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '200':
          description: Maqām families retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required: [count, data]
                properties:
                  count:
                    type: integer
                    description: Number of families
                  data:
                    type: array
                    items:
                      type: object
                      required: [family, links]
                      properties:
                        family:
                          $ref: '#/components/schemas/IdentifierRef'
                        links:
                          type: object
                          properties:
                            maqamat:
                              type: string
                              description: URL to filter maqāmāt by this family
                              example: "/api/maqamat?filterByFamily=rast"
              example:
                count: 16
                data:
                  - family:
                      idName: "rast"
                      displayName: "rāst"
                  - family:
                      idName: "bayyat"
                      displayName: "bayyāt"
                  - family:
                      idName: "hijaz"
                      displayName: "ḥijāz"
                  - family:
                      idName: "ajam"
                      displayName: "ʿajam"
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/{idName}:
    get:
      tags:
        - Maqāmāt
      summary: Get detailed maqām data
      description: |
        Retrieve comprehensive data for a specific maqām on its conventional or a transposed tonic, in a specific tuning system on a specific starting note name.
        
        **Special Path Parameter Value:**
        - The `{idName}` path parameter accepts the special value `"all"` to retrieve data for all maqāmāt in a specific tuning system and starting note
        - When using `"all"`, both `tuningSystem` and `startingNote` query parameters are required
        
        Returns:
        - Comprehensive pitch class data in the requested tuning system and starting note name
        - All ajnās present at each maqām degree in the ascending and descending maqām structure, organized by their maqām degree note names
        - The maqām's primary, secondary, and (when defined) tertiary jins positions as transposed per-degree entries — each carrying the note name, maqām degree (roman numeral), and the resolved jins entity `{jinsId, jinsIdName, jinsDisplayName}`
        - The maqām's ghammāz (modulation emphasis note) as a note-name + maqām-degree entry (no jins fields — the ghammāz is a melodic-emphasis note, not a jins)
        - All four of `primaryJins`, `secondaryJins`, `tertiaryJins`, and `ghammaz` are transposed in sync with the selected transposition (e.g. when `transposeTo=nawa` is used, every entry reflects the new tonic and the new maqām-degree position of each note). They are returned as arrays of per-degree objects to accommodate alternatives, or `null` when a maqām has no entry for that slot (e.g. rāst has no tertiary).
        - Optional includeIntervals data, transposition to different tonics, tuning-system-specific modulations (including lower octave), and suyūr
        
        Parameter Discovery:
        - Use options=true to discover valid values for all parameters including dynamically calculated transposition options
        - Especially useful for finding valid tuning system starting notes and transposition tonics for a specific maqām
        
        Important:
        - When options=true, the endpoint returns parameter discovery metadata instead of maqām data
        - This mode is mutually exclusive with data-returning parameters (transpose to, include modulations, include lower octave modulations, include suyūr, pitch class data type, includeIntervals)
        - If data-returning parameters are provided with options=true, the API returns a 400 Bad Request error with details about conflicting parameters
        
        Requirements:
        - Tuning system and starting note are required for all requests (both data retrieval and discovery mode)
        - These are fundamental to all pitch class calculations
      operationId: getMaqam
      parameters:
        - name: idName
          in: path
          required: true
          description: |
            URL-safe maqām identifier.
            
            **Special value**: Use `"all"` to retrieve data for all maqāmāt in a specific tuning system and starting note. When using `"all"`, both `tuningSystem` and `startingNote` query parameters are required.
          schema:
            type: string
          examples:
            rast:
              value: maqam_rast
              summary: Maqām Rāst (representative example)
            bayyat:
              value: maqam_bayyat
              summary: Maqām Bayyāt (representative example)
            hijaz:
              value: maqam_hijaz
              summary: Maqām Ḥijāz (representative example)
            all:
              value: "all"
              summary: All maqāmāt (requires tuningSystem and startingNote query parameters)
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          schema:
            type: string
          examples:
            ibnsina:
              value: "ibnsina_1037"
              summary: Ibn Sīnā (1037) - 7-Fret Oud 17-Tone (representative example)
            farabi:
              value: "alfarabi_950g"
              summary: al-Fārābī (950g) - First Oud Tuning 27-Tone (representative example)
            meshshaqa:
              value: "meshshaqa_1899"
              summary: Meshshāqa (1899) - Arabic Octave According to the Modernists (representative example)
        - name: startingNote
          in: query
          required: true
          description: |
            Tuning system starting note name (URL-safe, diacritics-insensitive).
            
            - Mandatory for all pitch class calculations
            - Different starting notes represent different theoretical frameworks within the same tuning system
            - Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          examples:
            yegah:
              value: "yegah"
              summary: yegāh (for IbnSina, Meshshaqa)
            ushayran:
              value: "ushayran"
              summary: ʿushayrān (for al-Farabi, al-Kindi)
            rast:
              value: "rast"
              summary: rāst (for CairoCongress, al-Sabbagh)
        - name: options
          in: query
          description: |
            When true, returns available parameter options instead of maqām data.
            
            - Useful for discovering valid values for tuning systems, starting notes, and other parameters
            - Tuning system and starting note are required for all requests (both data retrieval and discovery mode)
            - These are fundamental to all pitch class calculations and calculate valid starting note options and transposition tonics
            - Mutually exclusive with data-returning parameters (transpose to, include modulations, include lower octave modulations, include suyūr, pitch class data type, includeIntervals)
            - Transposition options are dynamically calculated based on the specific maqām, tuning system, and starting note combination
            - Only tonics where the maqām can be validly transposed (preserving interval pattern) are included, not all possible pitch classes
            - If data-returning parameters are provided, returns 400 Bad Request error with details about conflicting parameters
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: pitchClassDataType
          in: query
          required: false
          description: |
            Specifies which pitch class data type to return for each pitch class.
            
            - Optional parameter. When omitted, returns only minimal pitch class fields: `pitchClassIndex`, `octave`, `scaleDegree`, `noteName`, `noteNameDisplay`, and `noteNameDisplayAr` (if includeArabic is enabled)
            - When provided, returns the specified data type for all pitch classes in the maqām
            - In discovery mode (when options=true), this parameter is optional since the response returns parameter metadata instead of formatted data
            - Use 'all' for complete pitch class data across all available formats
          schema:
            type: string
            enum: [all, abjadName, englishName, solfege, fraction, cents, centsDeviation, decimalRatio, stringLength, fretDivision, midiNoteNumber, midiNoteDeviation, frequency, referenceNoteName]
          example: "cents"
        - name: transposeTo
          in: query
          description: |
            Transpose the maqām to a new tonic by preserving the interval patterns (URL-safe, diacritics-insensitive).
            
            - Only tonics where the maqām can be validly transposed in the specified tuning system and starting note are valid
            - To see all valid transposition options, request available parameter options instead of maqām data
          schema:
            type: string
          example: nawa
        - name: includeIntervals
          in: query
          description: Include interval data between maqām degrees
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeModulations
          in: query
          description: Include modulation possibilities to other maqāmāt and ajnās
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeModulations8vb
          in: query
          description: Include available modulations an octave below
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeSuyur
          in: query
          description: Include suyūr (melodic paths) data
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        # Note: ajnas are included by default and are not controlled by a query parameter
      responses:
        '200':
          description: |
            Maqām data retrieved successfully.
            
            - When options is true, the response structure differs and returns available parameter options instead of maqām data
            - In that case, transposition options contain only valid transposition tonics for this specific maqām, tuning system, and starting note combination
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaqamDetailResponse'
              examples:
                basic:
                  summary: Default maqām detail response (no optional includes)
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "maqām rāst"
                      version: "2025-12-10T17:03:34.565Z"
                    family:
                      idName: "rast"
                      displayName: "rāst"
                    tonic:
                      idName: "rast"
                      displayName: "rāst"
                    stats:
                      numberOfTranspositions: 3
                      numberOfPitchClasses: 7
                      numberOfMaqamModulations: 49
                      numberOfAjnasModulations: 47
                    characteristics:
                      isOctaveRepeating: true
                      hasAsymmetricDescending: true
                      hasSuyur: true
                    availableTranspositions:
                      - idName: "maqam_rast_al-qarar_chahargah"
                        displayName: "maqām rāst al-qarār chahārgāh"
                        tonic:
                          idName: "qarar_chahargah"
                          displayName: "qarār chahārgāh"
                      - idName: "maqam_rast_al-chahargah"
                        displayName: "maqām rāst al-chahārgāh"
                        tonic:
                          idName: "chahargah"
                          displayName: "chahārgāh"
                      - idName: "maqam_rast_al-mahuran"
                        displayName: "maqām rāst al-māhūrān"
                        tonic:
                          idName: "mahuran"
                          displayName: "māhūrān"
                    comments:
                      english: null
                      arabic: null
                    sources:
                      - sourceId: "alshawwa_1946"
                        page: "42"
                    parameters:
                      pitchClassDataType: null
                      includeIntervals: false
                      includeModulations: false
                      includeModulations8vb: false
                      includeSuyur: false
                      transposeTo: null
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        idName: "ibnsina_1037"
                        displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                        version: "2025-11-09T00:44:40.275Z"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                        selectedStartingNote:
                          idName: "yegah"
                          displayName: "yegāh"
                        pitchClassCounts:
                          singleOctave: 17
                          allOctaves: 68
                        originalValueType: "fraction"
                        referenceFrequency: 97.999
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah"
                      detail: "/api/maqamat/maqam_rast"
                      availability: "/api/maqamat/maqam_rast/availability"
                      compare: "/api/maqamat/maqam_rast/compare"
                    pitchData:
                      ascending:
                        - pitchClassIndex: 7
                          octave: 1
                          scaleDegree: "I"
                          noteName: "rast"
                          noteNameDisplay: "rāst"
                        - pitchClassIndex: 10
                          octave: 1
                          scaleDegree: "II"
                          noteName: "dugah"
                          noteNameDisplay: "dūgāh"
                        - pitchClassIndex: 12
                          octave: 1
                          scaleDegree: "III"
                          noteName: "segah"
                          noteNameDisplay: "segāh"
                        - pitchClassIndex: 14
                          octave: 1
                          scaleDegree: "IV"
                          noteName: "chahargah"
                          noteNameDisplay: "chahārgāh"
                        - pitchClassIndex: 0
                          octave: 2
                          scaleDegree: "V"
                          noteName: "nawa"
                          noteNameDisplay: "nawā"
                        - pitchClassIndex: 3
                          octave: 2
                          scaleDegree: "VI"
                          noteName: "husayni"
                          noteNameDisplay: "ḥusaynī"
                        - pitchClassIndex: 5
                          octave: 2
                          scaleDegree: "VII"
                          noteName: "awj"
                          noteNameDisplay: "awj"
                      descending:
                        - pitchClassIndex: 4
                          octave: 2
                          scaleDegree: "VII"
                          noteName: "ajam"
                          noteNameDisplay: "ʿajam"
                        - pitchClassIndex: 3
                          octave: 2
                          scaleDegree: "VI"
                          noteName: "husayni"
                          noteNameDisplay: "ḥusaynī"
                        - pitchClassIndex: 0
                          octave: 2
                          scaleDegree: "V"
                          noteName: "nawa"
                          noteNameDisplay: "nawā"
                        - pitchClassIndex: 14
                          octave: 1
                          scaleDegree: "IV"
                          noteName: "chahargah"
                          noteNameDisplay: "chahārgāh"
                        - pitchClassIndex: 12
                          octave: 1
                          scaleDegree: "III"
                          noteName: "segah"
                          noteNameDisplay: "segāh"
                        - pitchClassIndex: 10
                          octave: 1
                          scaleDegree: "II"
                          noteName: "dugah"
                          noteNameDisplay: "dūgāh"
                        - pitchClassIndex: 7
                          octave: 1
                          scaleDegree: "I"
                          noteName: "rast"
                          noteNameDisplay: "rāst"
                    ajnas:
                      ascending:
                        rāst: { id: "19", idName: "jins_rast", displayName: "jins rāst" }
                        dūgāh: { id: "2", idName: "jins_bayyat", displayName: "jins bayyāt" }
                        segāh: { id: "3", idName: "jins_segah", displayName: "jins segāh" }
                        chahārgāh: null
                        nawā: { id: "19", idName: "jins_rast_al-nawa", displayName: "jins rāst al-nawā" }
                        ḥusaynī: { id: "2", idName: "jins_bayyat_al-husayni", displayName: "jins bayyāt al-ḥusaynī" }
                        awj: { id: "3", idName: "jins_segah_al-awj", displayName: "jins segāh al-awj" }
                      descending:
                        ʿajam: null
                        ḥusaynī: { id: "7", idName: "jins_kurd_al-husayni", displayName: "jins kurd al-ḥusaynī" }
                        nawā: { id: "6", idName: "jins_nahawand_al-nawa", displayName: "jins nahāwand al-nawā" }
                        chahārgāh: { id: "5", idName: "jins_ajam_ushayran_al-chahargah", displayName: "jins ʿajam ʿushayrān al-chahārgāh" }
                        segāh: { id: "3", idName: "jins_segah", displayName: "jins segāh" }
                        dūgāh: { id: "2", idName: "jins_bayyat", displayName: "jins bayyāt" }
                        rāst: { id: "19", idName: "jins_rast", displayName: "jins rāst" }
                    primaryJins:
                      - noteName: "rast"
                        noteNameDisplay: "rāst"
                        maqamDegree: "I"
                        jinsId: "19"
                        jinsIdName: "jins_rast"
                        jinsDisplayName: "jins rāst"
                    secondaryJins:
                      - noteName: "nawa"
                        noteNameDisplay: "nawā"
                        maqamDegree: "V"
                        jinsId: "19"
                        jinsIdName: "jins_rast_al-nawa"
                        jinsDisplayName: "jins rāst al-nawā"
                    tertiaryJins: null
                    ghammaz:
                      - noteName: "nawa"
                        noteNameDisplay: "nawā"
                        maqamDegree: "V"
                transposed:
                  summary: Transposed maqām (taṣwīr), e.g. transposeTo=chahargah
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast_al-chahargah"
                      displayName: "maqām rāst al-chahārgāh"
                      version: "2025-12-10T17:03:34.565Z"
                    family:
                      idName: "rast"
                      displayName: "rāst"
                    tonic:
                      idName: "chahargah"
                      displayName: "chahārgāh"
                    transposition:
                      original:
                        idName: "rast"
                        displayName: "rāst"
                      resolved:
                        idName: "chahargah"
                        displayName: "chahārgāh"
                    stats:
                      numberOfTranspositions: 3
                      numberOfPitchClasses: 7
                      numberOfMaqamModulations: 49
                      numberOfAjnasModulations: 47
                    characteristics:
                      isOctaveRepeating: true
                      hasAsymmetricDescending: true
                      hasSuyur: true
                    availableTranspositions:
                      - idName: "maqam_rast_al-qarar_chahargah"
                        displayName: "maqām rāst al-qarār chahārgāh"
                        tonic:
                          idName: "qarar_chahargah"
                          displayName: "qarār chahārgāh"
                      - idName: "maqam_rast_al-chahargah"
                        displayName: "maqām rāst al-chahārgāh"
                        tonic:
                          idName: "chahargah"
                          displayName: "chahārgāh"
                      - idName: "maqam_rast_al-mahuran"
                        displayName: "maqām rāst al-māhūrān"
                        tonic:
                          idName: "mahuran"
                          displayName: "māhūrān"
                    comments:
                      english: null
                      arabic: null
                    sources:
                      - sourceId: "alshawwa_1946"
                        page: "42"
                    parameters:
                      pitchClassDataType: null
                      includeIntervals: false
                      includeModulations: false
                      includeModulations8vb: false
                      includeSuyur: false
                      transposeTo: "chahargah"
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        idName: "ibnsina_1037"
                        displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                        version: "2025-11-09T00:44:40.275Z"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                        selectedStartingNote:
                          idName: "yegah"
                          displayName: "yegāh"
                        pitchClassCounts:
                          singleOctave: 17
                          allOctaves: 68
                        originalValueType: "fraction"
                        referenceFrequency: 97.999
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=chahargah"
                      detail: "/api/maqamat/maqam_rast"
                      availability: "/api/maqamat/maqam_rast/availability"
                      compare: "/api/maqamat/maqam_rast/compare"
                    pitchData:
                      ascending:
                        - pitchClassIndex: 14
                          octave: 1
                          scaleDegree: "I"
                          noteName: "chahargah"
                          noteNameDisplay: "chahārgāh"
                        - pitchClassIndex: 0
                          octave: 2
                          scaleDegree: "II"
                          noteName: "nawa"
                          noteNameDisplay: "nawā"
                        - pitchClassIndex: 2
                          octave: 2
                          scaleDegree: "III"
                          noteName: "tik_hisar"
                          noteNameDisplay: "tīk ḥiṣār"
                        - pitchClassIndex: 4
                          octave: 2
                          scaleDegree: "IV"
                          noteName: "ajam"
                          noteNameDisplay: "ʿajam"
                        - pitchClassIndex: 7
                          octave: 2
                          scaleDegree: "V"
                          noteName: "kurdan"
                          noteNameDisplay: "kurdān"
                        - pitchClassIndex: 10
                          octave: 2
                          scaleDegree: "VI"
                          noteName: "muhayyar"
                          noteNameDisplay: "muḥayyar"
                        - pitchClassIndex: 12
                          octave: 2
                          scaleDegree: "VII"
                          noteName: "buzurk"
                          noteNameDisplay: "buzurk"
                      descending:
                        - pitchClassIndex: 11
                          octave: 2
                          scaleDegree: "VII"
                          noteName: "sunbuleh/zawal"
                          noteNameDisplay: "sunbuleh/zawāl"
                        - pitchClassIndex: 10
                          octave: 2
                          scaleDegree: "VI"
                          noteName: "muhayyar"
                          noteNameDisplay: "muḥayyar"
                        - pitchClassIndex: 7
                          octave: 2
                          scaleDegree: "V"
                          noteName: "kurdan"
                          noteNameDisplay: "kurdān"
                        - pitchClassIndex: 4
                          octave: 2
                          scaleDegree: "IV"
                          noteName: "ajam"
                          noteNameDisplay: "ʿajam"
                        - pitchClassIndex: 2
                          octave: 2
                          scaleDegree: "III"
                          noteName: "tik_hisar"
                          noteNameDisplay: "tīk ḥiṣār"
                        - pitchClassIndex: 0
                          octave: 2
                          scaleDegree: "II"
                          noteName: "nawa"
                          noteNameDisplay: "nawā"
                        - pitchClassIndex: 14
                          octave: 1
                          scaleDegree: "I"
                          noteName: "chahargah"
                          noteNameDisplay: "chahārgāh"
                    ajnas:
                      ascending:
                        chahārgāh: { id: "19", idName: "jins_rast_al-chahargah", displayName: "jins rāst al-chahārgāh" }
                        nawā: { id: "2", idName: "jins_bayyat_al-nawa", displayName: "jins bayyāt al-nawā" }
                        tīk ḥiṣār: { id: "3", idName: "jins_segah_al-tik_hisar", displayName: "jins segāh al-tīk ḥiṣār" }
                        ʿajam: null
                        kurdān: { id: "19", idName: "jins_rast", displayName: "jins rāst" }
                        muḥayyar: { id: "2", idName: "jins_bayyat", displayName: "jins bayyāt" }
                        buzurk: { id: "3", idName: "jins_segah", displayName: "jins segāh" }
                      descending:
                        sunbuleh/zawāl: null
                        muḥayyar: { id: "7", idName: "jins_kurd", displayName: "jins kurd" }
                        kurdān: { id: "6", idName: "jins_nahawand", displayName: "jins nahāwand" }
                        ʿajam: { id: "5", idName: "jins_ajam_ushayran", displayName: "jins ʿajam ʿushayrān" }
                        tīk ḥiṣār: { id: "3", idName: "jins_segah_al-tik_hisar", displayName: "jins segāh al-tīk ḥiṣār" }
                        nawā: { id: "2", idName: "jins_bayyat_al-nawa", displayName: "jins bayyāt al-nawā" }
                        chahārgāh: { id: "19", idName: "jins_rast_al-chahargah", displayName: "jins rāst al-chahārgāh" }
                    primaryJins:
                      - noteName: "chahargah"
                        noteNameDisplay: "chahārgāh"
                        maqamDegree: "I"
                        jinsId: "19"
                        jinsIdName: "jins_rast_al-chahargah"
                        jinsDisplayName: "jins rāst al-chahārgāh"
                    secondaryJins:
                      - noteName: "kurdan"
                        noteNameDisplay: "kurdān"
                        maqamDegree: "V"
                        jinsId: "19"
                        jinsIdName: "jins_rast"
                        jinsDisplayName: "jins rāst"
                    tertiaryJins: null
                    ghammaz:
                      - noteName: "kurdan"
                        noteNameDisplay: "kurdān"
                        maqamDegree: "V"
                withModulations:
                  summary: With modulation networks (includeModulations=true)
                  description: |
                    The `modulations` object contains `maqamat` and `ajnas`,
                    each keyed by modulation-degree slot (firstDegree,
                    thirdDegree, altThirdDegree, fourthDegree, fifthDegree,
                    sixthDegreeAsc, sixthDegreeDesc, sixthDegreeIfNoThird,
                    plus the auxiliary `noteName2pBelowThird` note name).
                    Values are arrays of candidate targets; each target is
                    a full entity reference with its own tonic. For maqāmāt,
                    `isTransposition: true` marks targets that are
                    al-taṣwīr forms rather than tahlīl forms.

                    All other top-level fields (maqam, family, tonic, stats,
                    characteristics, availableTranspositions, comments,
                    sources, parameters, context, links, pitchData, ajnas,
                    primaryJins, secondaryJins, tertiaryJins, ghammaz) are
                    unchanged from the `basic` example — only `modulations`
                    is new, and `parameters.includeModulations` is `true`.
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "maqām rāst"
                      version: "2025-12-10T17:03:34.565Z"
                    family:
                      idName: "rast"
                      displayName: "rāst"
                    tonic:
                      idName: "rast"
                      displayName: "rāst"
                    parameters:
                      pitchClassDataType: null
                      includeIntervals: false
                      includeModulations: true
                      includeModulations8vb: false
                      includeSuyur: false
                      transposeTo: null
                    modulations:
                      maqamat:
                        firstDegree:
                          - id: "4"
                            idName: "maqam_hijaz_kar_kurd"
                            displayName: "maqām ḥijāz kār kurd"
                            tonicIdName: "rast"
                            tonicDisplayName: "rāst"
                            isTransposition: false
                          - id: "5"
                            idName: "maqam_nikriz"
                            displayName: "maqām nikrīz"
                            tonicIdName: "rast"
                            tonicDisplayName: "rāst"
                            isTransposition: false
                          - id: "13"
                            idName: "maqam_bayyat_shuri_al-rast"
                            displayName: "maqām bayyāt shūrī al-rāst"
                            tonicIdName: "rast"
                            tonicDisplayName: "rāst"
                            isTransposition: true
                        thirdDegree:
                          - id: "19"
                            idName: "maqam_segah"
                            displayName: "maqām segāh"
                            tonicIdName: "segah"
                            tonicDisplayName: "segāh"
                            isTransposition: false
                        altThirdDegree: []
                        fourthDegree: []
                        fifthDegree:
                          - id: "10"
                            idName: "maqam_nahawand_al-nawa"
                            displayName: "maqām nahāwand al-nawā"
                            tonicIdName: "nawa"
                            tonicDisplayName: "nawā"
                            isTransposition: true
                        sixthDegreeAsc: []
                        sixthDegreeDesc: []
                        sixthDegreeIfNoThird: []
                        noteName2pBelowThird: "kurdī"
                      ajnas:
                        firstDegree:
                          - id: "2"
                            idName: "jins_bayyat_al-rast"
                            displayName: "jins bayyāt al-rāst"
                            tonicIdName: "rast"
                            tonicDisplayName: "rāst"
                          - id: "6"
                            idName: "jins_nahawand"
                            displayName: "jins nahāwand"
                            tonicIdName: "rast"
                            tonicDisplayName: "rāst"
                        thirdDegree:
                          - id: "3"
                            idName: "jins_segah"
                            displayName: "jins segāh"
                            tonicIdName: "segah"
                            tonicDisplayName: "segāh"
                        altThirdDegree: []
                        fourthDegree: []
                        fifthDegree: []
                        sixthDegreeAsc: []
                        sixthDegreeDesc: []
                        sixthDegreeIfNoThird: []
                        noteName2pBelowThird: "kurdī"
                withOptions:
                  summary: Options response (when options=true)
                  value:
                    maqam: "maqām rāst"
                    tuningSystem: "ibnsina_1037"
                    availableParameters:
                      tuningSystem:
                        required: true
                        description: "ID of tuning system (see /availability for options)"
                      startingNote:
                        options: ["ushayran", "yegah"]
                        default: "ushayran"
                        description: "Theoretical framework for note naming"
                      pitchClassDataType:
                        options: ["all", "frequency", "cents", "fraction", "decimalRatio", "stringLength", "abjadName", "fretDivision", "midiNoteNumber", "midiNoteDeviation", "centsDeviation", "referenceNoteName", "englishName"]
                        default: null
                        description: "Output format for pitch data. When omitted, returns only minimal fields (pitchClassIndex, octave, scaleDegree, noteName, noteNameDisplay, noteNameDisplayAr). Use 'all' for complete pitch class information."
                      includeIntervals:
                        type: "boolean"
                        default: false
                        description: "Include interval data between pitch classes"
                      transposeTo:
                        options: ["rast", "nawa", "dugah"]
                        default: null
                        description: "Transpose to specific tonic (taṣwīr) - only valid transpositions shown"
                      includeModulations:
                        type: "boolean"
                        default: false
                        description: "Include modulation analysis (maqāmāt and ajnās)"
                      includeModulations8vb:
                        type: "boolean"
                        default: false
                        description: "Include available modulations an octave below"
                      includeSuyur:
                        type: "boolean"
                        default: false
                        description: "Include documented performance practices (suyūr)"
                    notes:
                      ajnasData: "Ajnās (constituent melodic structures) are always included in the response for maqām analysis"
                      formatOptions: "The 'pitchClassDataType' parameter controls which pitch class properties are returned. When omitted, only minimal fields are returned. Use 'all' for comprehensive data or specific formats like 'cents', 'fraction', etc. for targeted data."
                    examples:
                      - "/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&pitchClassDataType=cents&includeIntervals=true"
                      - "/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=nawa&includeModulations=true"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                conflictingParametersWithOptions:
                  summary: Conflicting parameters with options=true
                  description: When options=true is used with data-returning parameters, the API returns a 400 Bad Request error
                  value:
                    error: "Conflicting parameters with options=true"
                    message: "The following data-returning parameters cannot be used with options=true (parameter discovery mode): includeIntervals"
                    hint: "Use options=true for parameter discovery. Contextual parameters (tuningSystem, startingNote) are optional but enhance discovery (e.g., show actual transposition options). Remove all data-returning parameters to use discovery mode, or remove options=true to retrieve maqām data."
                    conflictingParameters: ["includeIntervals"]
                    contextualParametersAllowed: ["tuningSystem", "startingNote"]
                missingTuningSystem:
                  summary: Missing required tuningSystem parameter
                  value:
                    error: "tuningSystem parameter is required"
                    hint: "Use /api/maqamat/{idName}/availability to see available tuning systems"
                    availabilityUrl: "/api/maqamat/{idName}/availability"
                missingStartingNote:
                  summary: Missing required startingNote parameter
                  value:
                    error: "startingNote parameter is required"
                    message: "A tuning system starting note must be specified. This is mandatory for all pitch class calculations."
                    validOptions: ["ʿushayrān", "yegāh"]
                    hint: "Add &startingNote=ʿushayrān to your request"
                invalidStartingNote:
                  summary: Invalid startingNote value
                  value:
                    error: "Invalid startingNote 'invalid_note'"
                    validOptions: ["ʿushayrān", "yegāh"]
                    hint: "Valid starting notes for this tuning system: ʿushayrān, yegāh"
        '404':
          description: Maqām not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Maqām 'maqam_invalid' not found"
                hint: "Use /api/maqamat to see all available maqāmāt"
        '422':
          description: Maqām cannot be realized in the specified tuning system
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Maqām 'maqam_bestenegar' cannot be realized in tuning system 'alkindi_874' with starting note 'ushayran'"
                hint: "Use /api/maqamat/{idName}/availability to see compatible tuning systems"
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/{idName}/availability:
    get:
      tags:
        - Maqāmāt
      summary: Check maqām availability across tuning systems
      description: |
        Return tuning-system availability for a maqām.
        
        - Shows which tuning systems on which starting note names can realize the specified maqām
        - If `transpositionNoteName` is provided, filters results to show only tuning systems where the maqām can be transposed to that specific note
        - Validity is computed against each tuning system's note name sets and the maqām's pitch class intervals
        - Tuning system starting note names are returned as both display values (with diacritics) and URL-safe identifiers
      operationId: getMaqamAvailability
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe maqām identifier
          schema:
            type: string
          example: "maqam_bayyat"
        - name: transpositionNoteName
          in: query
          required: false
          description: Filter results to show only tuning systems where the maqām can be transposed to this specific note (URL-safe, diacritics-insensitive)
          schema:
            type: string
          example: "nawa"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Availability information retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - maqam
                  - availability
                properties:
                  maqam:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Numeric identifier for the maqām
                      idName:
                        type: string
                        description: URL-safe identifier for the maqām (e.g., "maqam_rast")
                      displayName:
                        type: string
                        description: Display name with diacritics (e.g., "Maqam Rāst")
                      transpositionNoteName:
                        type: string
                        description: The transposition note name filter applied (present only when transpositionNoteName parameter is provided)
                        example: "nawa"
                      version:
                        type: string
                        description: ISO 8601 timestamp of last modification
                  availability:
                    type: object
                    properties:
                      count:
                        type: integer
                        description: Number of tuning systems supporting this maqām (or supporting the specified transposition if transpositionNoteName is provided)
                      tuningSystems:
                        type: array
                        description: Array of tuning systems where this maqām is available (filtered by transposition if transpositionNoteName is provided)
                        items:
                          $ref: '#/components/schemas/TuningSystemAvailability'
              examples:
                withoutTransposition:
                  summary: Without transposition filter
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "Maqam Rāst"
                      version: "2025-10-18T19:41:17.132Z"
                    availability:
                      count: 29
                      tuningSystems:
                        - tuningSystemId: "ibnsina_1037"
                          tuningSystemDisplayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
                        - tuningSystemId: "alfarabi_950g"
                          tuningSystemDisplayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave)"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
                withTransposition:
                  summary: With transposition filter (transpositionNoteName=nawa)
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "Maqam Rāst"
                      transpositionNoteName: "nawa"
                      version: "2025-10-18T19:41:17.132Z"
                    availability:
                      count: 15
                      tuningSystems:
                        - tuningSystemId: "ibnsina_1037"
                          tuningSystemDisplayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          tuningSystemStartingNoteNames: ["ʿushayrān"]
                          tuningSystemStartingNoteNamesIds: ["ushayran"]
                        - tuningSystemId: "alfarabi_950g"
                          tuningSystemDisplayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave)"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                emptyTranspositionNoteName:
                  summary: Empty transpositionNoteName parameter
                  value:
                    error: "Invalid parameter: transpositionNoteName"
                    message: "The 'transpositionNoteName' parameter cannot be empty."
                    hint: "Provide a valid note name like ?transpositionNoteName=nawa or remove the parameter"
        '404':
          description: Maqām not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Maqām 'maqam_invalid' not found"
                hint: "Use /api/maqamat to see all available maqāmāt"
        '500':
          $ref: '#/components/responses/ServerError'

  /tuning-systems:
    get:
      tags:
        - Tuning Systems
      summary: List all tuning systems
      description: |
        Retrieve metadata for all available tuning systems.
        
        - Includes identifiers, display names, version information, primary value type, and pitch class counts per octave
        - Responses include available starting note names for each tuning system
        - Different starting notes represent different theoretical frameworks (e.g., ʿushayrān for oud tuning, yegāh for monochord measurements)
        - Optional bibliographic source references (sourceId and page) when `includeSources=true`
      operationId: listTuningSystems
      parameters:
        - name: includeSources
          in: query
          required: false
          description: Include bibliographic source references (sourceId and page) for each tuning system
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: List of tuning systems
          content:
            application/json:
              schema:
                type: object
                required:
                  - count
                  - data
                properties:
                  count:
                    type: integer
                    description: Total number of tuning systems in the dataset
                  data:
                    type: array
                    description: Array of tuning system objects
                    items:
                      type: object
                      required:
                        - tuningSystem
                        - startingNotes
                        - stats
                        - sources
                      properties:
                        tuningSystem:
                          $ref: '#/components/schemas/EntityRef'
                        startingNotes:
                          type: object
                          properties:
                            idNames:
                              type: array
                              items:
                                type: string
                            displayNames:
                              type: array
                              items:
                                type: string
                        stats:
                          type: object
                          properties:
                            numberOfPitchClassesSingleOctave:
                              type: integer
                              description: Number of pitch classes per octave
                        sources:
                          type: array
                          description: Bibliographic source references for this tuning system
                          items:
                            type: object
                            properties:
                              sourceId:
                                type: string
                                description: URL-safe source identifier
                                example: "al-Shawwa-(1946)"
                              page:
                                type: string
                                description: Page number or range where the tuning system is documented
                                example: "49"
              examples:
                english:
                  summary: English display names (default, includeArabic=false)
                  value:
                    count: 35
                    data:
                      - tuningSystem:
                          id: "ibnsina_1037"
                          idName: "ibnsina_1037"
                          displayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          version: "2025-10-18T19:42:23.643Z"
                          year: "1037"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                        stats:
                          numberOfPitchClassesSingleOctave: 17
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "49"
                      - tuningSystem:
                          id: "alfarabi_950g"
                          idName: "alfarabi_950g"
                          displayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave) 27-Tone"
                          version: "2025-10-18T19:42:23.643Z"
                          year: "950g"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                        stats:
                          numberOfPitchClassesSingleOctave: 27
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "50"
                arabic:
                  summary: Arabic display names (includeArabic=true)
                  value:
                    count: 35
                    data:
                      - tuningSystem:
                          id: "ibnsina_1037"
                          idName: "ibnsina_1037"
                          displayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          displayNameAr: "ابن سينا (1037) 7-Fret Oud 17-Tone"
                          version: "2025-10-18T19:42:23.643Z"
                          year: "1037"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                          displayNamesAr: ["عشيران", "يگاه"]
                        stats:
                          numberOfPitchClassesSingleOctave: 17
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "49"
                      - tuningSystem:
                          id: "alfarabi_950g"
                          idName: "alfarabi_950g"
                          displayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave) 27-Tone"
                          displayNameAr: "الفارابي (950g) تنغيم العود الأول (الديوان الأول الكامل) ٢٧-نغمة"
                          version: "2025-10-18T19:42:23.643Z"
                          year: "950g"
                        startingNotes:
                          idNames: ["ushayran", "yegah"]
                          displayNames: ["ʿushayrān", "yegāh"]
                          displayNamesAr: ["عشيران", "يگاه"]
                        stats:
                          numberOfPitchClassesSingleOctave: 27
                        sources:
                          - sourceId: "al-Shawwa-(1946)"
                            page: "50"
        '500':
          $ref: '#/components/responses/ServerError'

  /tuning-systems/{id}/{startingNote}/pitch-classes:
    get:
      tags:
        - Tuning Systems
      summary: Get Tuning System Details
      description: |
        Get Tuning System Details - Returns all pitch classes for a specific tuning system and starting note.
        
        This endpoint is essential for tuning system operations, providing comprehensive pitch class data
        across all octaves with full formatting options.
      operationId: getTuningSystemPitchClasses
      parameters:
        - name: id
          in: path
          required: true
          description: Tuning system identifier
          schema:
            type: string
          example: "cairocongresstuningcommittee_1929"
        - name: startingNote
          in: path
          required: true
          description: Tuning system starting note name (URL-safe, diacritics-insensitive). Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "rast"
        - name: pitchClassDataType
          in: query
          required: false
          description: |
            Pitch class data format. When omitted, defaults to "all".
            - `all` returns all available fields including englishName, solfege, abjadName, fraction, cents, decimalRatio, stringLength, frequency, fretDivision, midiNoteDecimal, midiNotePlusCentsDeviation, centsDeviation, ipnReferenceNoteName
            - Use a specific value to return only that field plus minimal identifiers
          schema:
            type: string
            enum:
              - all
              - englishName
              - solfege
              - fraction
              - cents
              - decimalRatio
              - stringLength
              - frequency
              - abjadName
              - fretDivision
              - midiNoteNumber
              - midiNoteDeviation
              - centsDeviation
              - referenceNoteName
            default: "all"
          example: "all"
        - name: octave
          in: query
          required: false
          description: |
            Filter by octave number. Use "all" to return pitch classes from all octaves (default).
            Use a specific octave number (0, 1, 2, 3) to filter to that octave only.
          schema:
            type: string
            enum: ["all", "0", "1", "2", "3"]
            default: "all"
          example: "all"
        - name: includeSources
          in: query
          required: false
          description: Include bibliographic source references (sourceId and page) for the tuning system
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Tuning system pitch classes retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - tuningSystem
                  - startingNotes
                  - selectedStartingNote
                  - stats
                  - pitchClasses
                  - context
                  - links
                properties:
                  tuningSystem:
                    $ref: '#/components/schemas/EntityRef'
                  startingNotes:
                    type: object
                    properties:
                      idNames:
                        type: array
                        items:
                          type: string
                      displayNames:
                        type: array
                        items:
                          type: string
                  selectedStartingNote:
                    type: object
                    properties:
                      idName:
                        type: string
                      displayName:
                        type: string
                  stats:
                    type: object
                    properties:
                      totalPitchClasses:
                        type: integer
                      numberOfPitchClassesSingleOctave:
                        type: integer
                      referenceFrequency:
                        type: number
                      octaves:
                        type: array
                        items:
                          type: integer
                  pitchClasses:
                    type: array
                    description: |
                      Array of formatted pitch class data. Fields vary by pitchClassDataType.
                      When pitchClassDataType=all, each item includes: pitchClassIndex, octave, noteName, noteNameDisplay, englishName, solfege, abjadName, fraction, cents, decimalRatio, stringLength, frequency, fretDivision, midiNoteDecimal, midiNotePlusCentsDeviation, centsDeviation, ipnReferenceNoteName.
                    items:
                      type: object
                      description: Pitch class object; properties depend on pitchClassDataType
                      properties:
                        pitchClassIndex:
                          type: integer
                          description: Index of the pitch class within the tuning system
                        octave:
                          type: integer
                          description: Octave number (0-3)
                        noteName:
                          type: string
                          description: URL-safe note name identifier
                        noteNameDisplay:
                          type: string
                          description: Display form of the note name
                        englishName:
                          type: string
                          description: International Pitch Notation name (e.g. G3, Bb3). Included when pitchClassDataType is all or englishName.
                        solfege:
                          type: string
                          description: Fixed-do solfege syllable (Do, Re, Mi, Fa, Sol, La, Si) with accidentals and octave appended (no spaces, e.g. Mi-b3, Sol2). Included when pitchClassDataType is all or solfege.
                        abjadName:
                          type: string
                          description: Arabic abjad name. Included when pitchClassDataType is all or abjadName.
                        fraction:
                          type: string
                          description: Frequency ratio as fraction. Included when pitchClassDataType is all or fraction.
                        cents:
                          type: number
                          description: Pitch in cents. Included when pitchClassDataType is all or cents.
                        decimalRatio:
                          type: number
                          description: Frequency ratio as decimal. Included when pitchClassDataType is all or decimalRatio.
                        stringLength:
                          type: number
                          description: Relative string length. Included when pitchClassDataType is all or stringLength.
                        frequency:
                          type: number
                          description: Frequency in Hz. Included when pitchClassDataType is all or frequency.
                        fretDivision:
                          type: string
                          description: Fret position. Included when pitchClassDataType is all or fretDivision.
                        midiNoteDecimal:
                          type: number
                          description: MIDI note with fractional precision. Included when pitchClassDataType is all or midiNoteNumber.
                        midiNotePlusCentsDeviation:
                          type: string
                          description: IPN reference note with cents deviation. Included when pitchClassDataType is all or midiNoteDeviation.
                        centsDeviation:
                          type: number
                          description: Deviation in cents from nearest equal-tempered pitch. Included when pitchClassDataType is all or centsDeviation.
                        ipnReferenceNoteName:
                          type: string
                          description: IPN reference note name. Included when pitchClassDataType is all or referenceNoteName.
                  sources:
                    type: array
                    description: Bibliographic source references for this tuning system
                    items:
                      type: object
                      properties:
                        sourceId:
                          type: string
                          description: URL-safe source identifier
                          example: "al-Shawwa-(1946)"
                        page:
                          type: string
                          description: Page number or range where the tuning system is documented
                          example: "49"
                  context:
                    type: object
                    properties:
                      pitchClassDataType:
                        type: string
                      octave:
                        type: string
                        nullable: true
                        description: '"all" if all octaves, or specific octave number'
                  links:
                    type: object
                    properties:
                      self:
                        type: string
                      tuningSystem:
                        type: string
              example:
                {
                  "tuningSystem": {
                    "id": "ibnsina_1037",
                    "idName": "ibnsina_1037",
                    "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                    "version": "2025-11-09T00:44:40.275Z",
                    "year": "1037",
                    "numberOfPitchClassesSingleOctave": 17
                  },
                  "startingNotes": {
                    "idNames": [
                      "ushayran",
                      "yegah"
                    ],
                    "displayNames": [
                      "ʿushayrān",
                      "yegāh"
                    ]
                  },
                  "selectedStartingNote": {
                    "idName": "yegah",
                    "displayName": "yegāh"
                  },
                  "stats": {
                    "totalPitchClasses": 17,
                    "numberOfPitchClassesSingleOctave": 17,
                    "referenceFrequency": 97.999,
                    "octaves": [
                      0,
                      1,
                      2,
                      3
                    ]
                  },
                  "pitchClasses": [
                    {
                      "pitchClassIndex": 0,
                      "octave": 0,
                      "noteName": "qarar_yegah",
                      "noteNameDisplay": "qarār yegāh",
                      "cents": -1200
                    },
                    {
                      "pitchClassIndex": 1,
                      "octave": 0,
                      "noteName": "qarar_qarar_hisar",
                      "noteNameDisplay": "qarār qarār ḥiṣār",
                      "cents": -1088.691430896177
                    },
                    {
                      "pitchClassIndex": 2,
                      "octave": 0,
                      "noteName": "qarar_qarar_tik_hisar/shuri",
                      "noteNameDisplay": "qarār qarār tīk ḥiṣār/shūrī",
                      "cents": -1061.427339096077
                    },
                    {
                      "pitchClassIndex": 3,
                      "octave": 0,
                      "noteName": "qarar_ushayran",
                      "noteNameDisplay": "qarār ʿushayrān",
                      "cents": -996.0899982692251
                    },
                    {
                      "pitchClassIndex": 4,
                      "octave": 0,
                      "noteName": "qarar_ajam_ushayran",
                      "noteNameDisplay": "qarār ʿajam ʿushayrān",
                      "cents": -905.8650025961624
                    },
                    {
                      "…": "truncated — 63 more items"
                    }
                  ],
                  "context": {
                    "pitchClassDataType": "cents",
                    "octave": "all"
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/tuning-systems/ibnsina_1037/yegah/pitch-classes",
                    "tuningSystem": "/api/tuning-systems/ibnsina_1037"
                  },
                  "sources": [
                    {
                      "sourceId": "farmer_1937",
                      "page": "249"
                    }
                  ]
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/{idName}/transpositions:
    get:
      tags:
        - Maqāmāt
      summary: List transpositions for a maqām
      description: |
        Lists distinct transpositions (taswīr) for a maqām within a specific tuning system and starting note.
        The analytical form (tahlīl) is excluded. Octave/register variants of the same modal degree
        (e.g. qarār dūgāh vs dūgāh vs muḥayyar) are not treated as distinct taswīr.

        If there are no distinct taswīr for this tuning-system + starting-note combination, this endpoint returns `422`
        with an `{ error, hint }` payload (it does not return a 200 with an empty list).
      operationId: listMaqamTranspositions
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe maqām identifier
          schema:
            type: string
          example: "maqam_hijaz"
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          schema:
            type: string
          example: "alsabbagh_1954"
        - name: startingNote
          in: query
          required: true
          description: Tuning system starting note name (URL-safe, diacritics-insensitive). Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "rast"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Transpositions retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  maqam:
                    $ref: '#/components/schemas/EntityReference'
                  tuningSystem:
                    $ref: '#/components/schemas/EntityReference'
                  startingNote:
                    $ref: '#/components/schemas/IdentifierReference'
                  transpositions:
                    type: object
                    properties:
                      total:
                        type: integer
                        description: Count of distinct taswīr options (no tahlīl; no register variants)
                      options:
                        type: object
                        description: URL-safe tonic identifiers and display names
                        properties:
                          idNames:
                            type: array
                            items: { type: string }
                          displayNames:
                            type: array
                            items: { type: string }
                      detailed:
                        type: array
                        description: Detailed transposition options — each entry carries its own transposed jins-degree and ghammāz fields (note name, maqām degree, resolved jins entity).
                        items:
                          type: object
                          properties:
                            tonic:
                              $ref: '#/components/schemas/IdentifierReference'
                            pitchClass:
                              type: object
                              properties:
                                pitchClassIndex: { type: integer }
                                octave: { type: integer }
                            primaryJins:
                              type: array
                              nullable: true
                              items:
                                $ref: '#/components/schemas/JinsDegreeEntry'
                            secondaryJins:
                              type: array
                              nullable: true
                              items:
                                $ref: '#/components/schemas/JinsDegreeEntry'
                            tertiaryJins:
                              type: array
                              nullable: true
                              items:
                                $ref: '#/components/schemas/JinsDegreeEntry'
                            ghammaz:
                              type: array
                              nullable: true
                              items:
                                $ref: '#/components/schemas/GhammazEntry'
                            links:
                              type: object
                              properties:
                                detail: { type: string }
                  links:
                    type: object
                    properties:
                      self: { type: string }
                      availability: { type: string }
                      detail: { type: string }
              example:
                {
                  "maqam": {
                    "id": "1",
                    "idName": "maqam_rast",
                    "displayName": "maqām rāst",
                    "version": "2025-12-10T17:03:34.565Z"
                  },
                  "tuningSystem": {
                    "id": "ibnsina_1037",
                    "idName": "ibnsina_1037",
                    "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                    "version": "2025-11-09T00:44:40.275Z",
                    "numberOfPitchClassesSingleOctave": 17
                  },
                  "startingNote": {
                    "idName": "yegah",
                    "displayName": "yegāh"
                  },
                  "transpositions": {
                    "total": 3,
                    "options": {
                      "idNames": [
                        "qarar_chahargah",
                        "chahargah",
                        "mahuran"
                      ],
                      "displayNames": [
                        "qarār chahārgāh",
                        "chahārgāh",
                        "māhūrān"
                      ]
                    },
                    "detailed": [
                      {
                        "tonic": {
                          "idName": "qarar_chahargah",
                          "displayName": "qarār chahārgāh"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 14,
                          "octave": 0
                        },
                        "primaryJins": [
                          {
                            "noteName": "qarar_chahargah",
                            "noteNameDisplay": "qarār chahārgāh",
                            "maqamDegree": "I",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast_al-qarar_chahargah",
                            "jinsDisplayName": "jins rāst al-qarār chahārgāh"
                          }
                        ],
                        "secondaryJins": [
                          {
                            "noteName": "rast",
                            "noteNameDisplay": "rāst",
                            "maqamDegree": "V",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast",
                            "jinsDisplayName": "jins rāst"
                          }
                        ],
                        "tertiaryJins": null,
                        "ghammaz": [
                          {
                            "noteName": "rast",
                            "noteNameDisplay": "rāst",
                            "maqamDegree": "V"
                          }
                        ],
                        "links": {
                          "detail": "/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=qarar_chahargah"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "chahargah",
                          "displayName": "chahārgāh"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 14,
                          "octave": 1
                        },
                        "primaryJins": [
                          {
                            "noteName": "chahargah",
                            "noteNameDisplay": "chahārgāh",
                            "maqamDegree": "I",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast_al-chahargah",
                            "jinsDisplayName": "jins rāst al-chahārgāh"
                          }
                        ],
                        "secondaryJins": [
                          {
                            "noteName": "kurdan",
                            "noteNameDisplay": "kurdān",
                            "maqamDegree": "V",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast",
                            "jinsDisplayName": "jins rāst"
                          }
                        ],
                        "tertiaryJins": null,
                        "ghammaz": [
                          {
                            "noteName": "kurdan",
                            "noteNameDisplay": "kurdān",
                            "maqamDegree": "V"
                          }
                        ],
                        "links": {
                          "detail": "/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=chahargah"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "mahuran",
                          "displayName": "māhūrān"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 14,
                          "octave": 2
                        },
                        "primaryJins": [
                          {
                            "noteName": "mahuran",
                            "noteNameDisplay": "māhūrān",
                            "maqamDegree": "I",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast_al-mahuran",
                            "jinsDisplayName": "jins rāst al-māhūrān"
                          }
                        ],
                        "secondaryJins": [
                          {
                            "noteName": "jawab_kurdan",
                            "noteNameDisplay": "jawāb kurdān",
                            "maqamDegree": "V",
                            "jinsId": "19",
                            "jinsIdName": "jins_rast",
                            "jinsDisplayName": "jins rāst"
                          }
                        ],
                        "tertiaryJins": null,
                        "ghammaz": [
                          {
                            "noteName": "jawab_kurdan",
                            "noteNameDisplay": "jawāb kurdān",
                            "maqamDegree": "V"
                          }
                        ],
                        "links": {
                          "detail": "/api/maqamat/maqam_rast?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=mahuran"
                        }
                      }
                    ]
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/maqamat/maqam_rast/transpositions?tuningSystem=ibnsina_1037&startingNote=yegah",
                    "availability": "/api/maqamat/maqam_rast/availability",
                    "detail": "/api/maqamat/maqam_rast"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Maqām or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Maqām cannot be realized with this tuning system and starting note, or no distinct taswīr options exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/{idName}/compare:
    get:
      tags:
        - Maqāmāt
      summary: Compare maqām data across multiple tuning systems and starting notes
      description: |
        Compare comprehensive maqām data across multiple tuning systems **and** multiple starting
        notes in a single request. Given `N` tuning systems and `M` starting notes, the response
        contains the Cartesian product of `N × M` comparison cells in user-supplied order
        (outer loop = `tuningSystems`, inner loop = `startingNotes`).

        Each successful cell includes ascending/descending pitch class sequences, optional
        interval data, ajnās mapping, family classification, and transposition details. When a
        requested starting note is not available in a given tuning system the cell carries an
        informational `note` field (not `error`) together with `validStartingNotes`. When a hard
        failure occurs (tuning system not found, maqām not realizable in this configuration, or a
        `transposeTo` target that is unreachable) the cell carries an `error` field. The overall
        response is always HTTP 200 — per-cell outcomes do not affect the HTTP status.

        The following invariant always holds:

        ```
        successfulComparisons + unavailableStartingNotes + failedComparisons === totalComparisons
        ```

        This endpoint is ideal for comparative musicological analysis across historical tuning
        systems and/or multiple modal transpositions.
      operationId: compareMaqam
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe maqām identifier
          schema:
            type: string
          example: "maqam_bayyat"
        - name: tuningSystems
          in: query
          required: true
          description: |
            Comma-separated list of tuning system IDs. The response computes the Cartesian
            product of these tuning systems with the requested `startingNotes` (outer loop =
            `tuningSystems`, inner loop = `startingNotes`).
          schema:
            type: string
          example: "alsabbagh_1954,ibnsina_1037"
        - name: startingNotes
          in: query
          required: true
          description: |
            Comma-separated list of starting note names (URL-safe, diacritics-insensitive). The
            response contains `N × M` cells for `N` tuning systems × `M` starting notes, in
            user-supplied order. If a starting note is not available in a given tuning system
            that cell carries an informational `note` field (with a `validStartingNotes`
            namespace) rather than pitch data.
          schema:
            type: string
          example: "rast,yegah,ushayran"
        - name: pitchClassDataType
          in: query
          required: false
          description: Output format for pitch class data. Defaults to `all` when omitted.
          schema:
            type: string
            enum:
              - all
              - englishName
              - fraction
              - cents
              - decimalRatio
              - stringLength
              - frequency
              - abjadName
              - fretDivision
              - midiNoteNumber
              - midiNoteDeviation
              - centsDeviation
              - referenceNoteName
            default: "all"
          example: "cents"
        - name: includeIntervals
          in: query
          required: false
          description: |
            Whether to include interval data between adjacent pitch classes on each cell.
            Defaults to `"false"` when omitted.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: transposeTo
          in: query
          required: false
          description: |
            Transpose every successful cell to the given tonic note (applies to every cell in the
            Cartesian product). If the requested target is not reachable in a given cell the cell
            carries an `error` plus an `availableTranspositions` list.
          schema:
            type: string
          example: "nawa"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with `Ar` suffix (e.g., `displayNameAr`, `noteNameDisplayAr`)
            - Note names, maqām names, and jins names get Arabic versions in `*Ar` fields
            - Tuning system display names get Arabic versions in `displayNameAr` if available
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: |
            Comparison data retrieved successfully. Note that the response is always HTTP 200
            regardless of per-cell outcomes; individual failures (missing starting note, tuning
            system not found, maqām not realizable, unreachable transposeTo) are reported on the
            respective cell via `note` or `error` fields. The counters
            `successfulComparisons + unavailableStartingNotes + failedComparisons` always equal
            `totalComparisons`.
          content:
            application/json:
              schema:
                type: object
                required:
                  - maqam
                  - comparisons
                  - parameters
                  - links
                properties:
                  maqam:
                    type: object
                    description: Canonical maqām identifier namespace (the maqām being compared)
                    required:
                      - id
                      - idName
                      - displayName
                    properties:
                      id:
                        type: string
                        description: Numeric identifier for the maqām
                      idName:
                        type: string
                        description: URL-safe identifier for the maqām
                      displayName:
                        type: string
                        description: Display name with diacritics
                      displayNameAr:
                        type: string
                        description: Arabic-script display name (present only when `includeArabic=true`)
                      version:
                        type: string
                        description: ISO 8601 timestamp of last modification
                  comparisons:
                    type: object
                    description: |
                      List response object containing every comparison cell in the Cartesian
                      product. Meta counters are hoisted to the top level of this object (siblings
                      of `count` and `data`), not nested under a separate `meta` key.
                    required:
                      - count
                      - totalComparisons
                      - successfulComparisons
                      - unavailableStartingNotes
                      - failedComparisons
                      - data
                    properties:
                      count:
                        type: integer
                        description: Number of items in `data` (equals `totalComparisons`)
                      totalComparisons:
                        type: integer
                        description: Total number of comparison cells in the Cartesian product (`N × M`)
                      successfulComparisons:
                        type: integer
                        description: Number of cells that returned full pitch/interval data
                      unavailableStartingNotes:
                        type: integer
                        description: |
                          Number of cells whose `startingNote` was not available in the cell's
                          `tuningSystem`. These cells carry a `note` field (informational — NOT
                          an error) and are distinct from hard failures.
                      failedComparisons:
                        type: integer
                        description: |
                          Number of cells that hit a hard failure (tuning system not found,
                          maqām not realizable, or unreachable `transposeTo`). These cells carry
                          an `error` field.
                      data:
                        type: array
                        description: |
                          Array of comparison cells in user-supplied order (outer loop =
                          `tuningSystems`, inner loop = `startingNotes`). Each cell has one of
                          three mutually exclusive shapes — a successful result, an informational
                          `note` (unavailable starting note), or an `error` (hard failure). All
                          non-common fields below are optional and present only for the
                          corresponding outcome.
                        items:
                          type: object
                          required:
                            - tuningSystem
                            - startingNote
                          properties:
                            tuningSystem:
                              type: object
                              description: |
                                Tuning system entity namespace for this cell. For the
                                "tuning system not found" failure this is a minimal fallback
                                namespace built from the raw ID (no `version`, no `displayNameAr`).
                                In every other outcome this is the full entity namespace.
                              properties:
                                id:
                                  type: string
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                version:
                                  type: string
                                displayNameAr:
                                  type: string
                                  description: Present only when `includeArabic=true`
                            startingNote:
                              type: object
                              description: Identifier namespace for the starting note of this cell
                              properties:
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                displayNameAr:
                                  type: string
                                  description: Present only when `includeArabic=true`
                            maqam:
                              type: object
                              description: |
                                Per-cell maqām entity namespace with family classification.
                                **Present on successful cells only.** When the cell represents a
                                transposition, `idName` and `displayName` reflect the transposed
                                name (e.g. `maqam_rast_al-nawa`); the canonical `id` is preserved.
                              properties:
                                id:
                                  type: string
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                version:
                                  type: string
                                displayNameAr:
                                  type: string
                                maqamFamilyId:
                                  type: string
                                  description: URL-safe maqām family identifier
                                maqamFamilyDisplayName:
                                  type: string
                                  description: Maqām family display name with diacritics
                                maqamFamilyDisplayNameAr:
                                  type: string
                                  description: Present only when `includeArabic=true`
                            tonic:
                              type: object
                              description: |
                                Identifier namespace for the resolved tonic note. **Present on
                                successful cells only.**
                              properties:
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                displayNameAr:
                                  type: string
                            transposition:
                              type: object
                              description: |
                                Transposition details. **Present only when the tahlil for this
                                cell is a taswīr (tonic differs from the canonical tonic).** Omitted
                                entirely when the cell resolves to the canonical-octave tahlil.
                              properties:
                                original:
                                  type: object
                                  description: Identifier namespace for the canonical tonic
                                  properties:
                                    idName:
                                      type: string
                                    displayName:
                                      type: string
                                    displayNameAr:
                                      type: string
                                resolved:
                                  type: object
                                  description: Identifier namespace for the transposed tonic (same as top-level `tonic`)
                                  properties:
                                    idName:
                                      type: string
                                    displayName:
                                      type: string
                                    displayNameAr:
                                      type: string
                            stats:
                              type: object
                              description: Per-cell statistics. **Present on successful cells only.**
                              properties:
                                tuningSystem:
                                  type: object
                                  properties:
                                    numberOfPitchClassesSingleOctave:
                                      type: integer
                                      description: Number of pitch classes per octave in this tuning system
                                    referenceFrequency:
                                      type: number
                                      description: Reference frequency in Hz for the cell's starting note
                                maqam:
                                  type: object
                                  properties:
                                    numberOfPitchClasses:
                                      type: integer
                                      description: Number of pitch classes in this cell's tahlil
                                    isOctaveRepeating:
                                      type: boolean
                                      description: Whether the tahlil repeats within an octave
                            availableTranspositions:
                              type: array
                              description: |
                                Taswīr-only available transpositions for this cell. **Present on
                                successful cells and on `transposeTo`-unreachable error cells**
                                (in the latter case, this lists the alternatives the caller could
                                try). May be an empty array.
                              items:
                                type: object
                                properties:
                                  idName:
                                    type: string
                                  displayName:
                                    type: string
                                  displayNameAr:
                                    type: string
                                  tonic:
                                    type: object
                                    properties:
                                      idName:
                                        type: string
                                      displayName:
                                        type: string
                                      displayNameAr:
                                        type: string
                            pitchData:
                              type: object
                              description: |
                                Pitch class sequences. **Present on successful cells only.** The
                                field set of each pitch class item depends on `pitchClassDataType`.
                              properties:
                                ascending:
                                  type: array
                                  items:
                                    type: object
                                descending:
                                  type: array
                                  items:
                                    type: object
                            ajnas:
                              type: object
                              description: |
                                Ajnās mapping keyed by note name. **Present on successful cells
                                only.** Each value is either a jins entity namespace
                                (`{id, idName, displayName, displayNameAr?}`) or `null` if no
                                jins begins at that note.
                              properties:
                                ascending:
                                  type: object
                                  additionalProperties:
                                    oneOf:
                                      - type: object
                                      - type: "null"
                                descending:
                                  type: object
                                  additionalProperties:
                                    oneOf:
                                      - type: object
                                      - type: "null"
                            intervals:
                              type: object
                              description: |
                                Interval data between adjacent pitch classes. **Present on
                                successful cells only and only when `includeIntervals=true`.**
                              properties:
                                ascending:
                                  type: array
                                  items:
                                    type: object
                                descending:
                                  type: array
                                  items:
                                    type: object
                            parameters:
                              type: object
                              description: Per-cell echo of the effective parameters. **Present on successful cells only.**
                              properties:
                                pitchClassDataType:
                                  type: string
                                includeIntervals:
                                  type: boolean
                                transposeTo:
                                  type: string
                                  nullable: true
                            links:
                              type: object
                              description: Per-cell links namespace. **Present on successful cells only.**
                              properties:
                                detail:
                                  type: string
                                availability:
                                  type: string
                            note:
                              type: string
                              description: |
                                Informational message for cells whose `startingNote` is not
                                available in the cell's `tuningSystem`. **Present only for
                                unavailable-starting-note cells** (counted by
                                `unavailableStartingNotes`). Mutually exclusive with `error`.
                            validStartingNotes:
                              type: object
                              description: |
                                Valid starting notes for the cell's tuning system. Present
                                alongside `note`. A string-array namespace.
                              properties:
                                idNames:
                                  type: array
                                  items:
                                    type: string
                                displayNames:
                                  type: array
                                  items:
                                    type: string
                                displayNamesAr:
                                  type: array
                                  items:
                                    type: string
                                  description: Present only when `includeArabic=true`
                            error:
                              type: string
                              description: |
                                Error message for cells that hit a hard failure (tuning system
                                not found, maqām not realizable, or unreachable `transposeTo`).
                                **Present only for failed cells** (counted by `failedComparisons`).
                                Mutually exclusive with `note`.
                            transposeTo:
                              type: string
                              description: |
                                Present only on the `transposeTo`-unreachable error variant:
                                echoes the unreachable target note name.
                  parameters:
                    type: object
                    description: Echo of the effective request parameters at the top level
                    properties:
                      tuningSystems:
                        type: object
                        description: String-array namespace of requested tuning systems
                        properties:
                          idNames:
                            type: array
                            items:
                              type: string
                          displayNames:
                            type: array
                            items:
                              type: string
                          displayNamesAr:
                            type: array
                            items:
                              type: string
                            description: Present only when `includeArabic=true`
                      startingNotes:
                        type: object
                        description: String-array namespace of requested starting notes
                        properties:
                          idNames:
                            type: array
                            items:
                              type: string
                          displayNames:
                            type: array
                            items:
                              type: string
                          displayNamesAr:
                            type: array
                            items:
                              type: string
                            description: Present only when `includeArabic=true`
                      pitchClassDataType:
                        type: string
                      includeIntervals:
                        type: boolean
                      transposeTo:
                        type: string
                        nullable: true
                  links:
                    type: object
                    description: Top-level links namespace
                    properties:
                      self:
                        type: string
                        description: Canonical URL for this comparison request
                      detail:
                        type: string
                        description: Link to the maqām detail endpoint
                      availability:
                        type: string
                        description: Link to the maqām availability endpoint
              examples:
                twoTuningSystems:
                  summary: Compare across two tuning systems (single starting note)
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "maqām rāst"
                      version: "2025-12-10T17:03:34.565Z"
                    comparisons:
                      count: 1
                      totalComparisons: 1
                      successfulComparisons: 1
                      unavailableStartingNotes: 0
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          maqam:
                            id: "1"
                            idName: "maqam_rast"
                            displayName: "maqām rāst"
                            version: "2025-12-10T17:03:34.565Z"
                            maqamFamilyId: "rast"
                            maqamFamilyDisplayName: "rāst"
                          tonic:
                            idName: "rast"
                            displayName: "rāst"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            maqam:
                              numberOfPitchClasses: 7
                              isOctaveRepeating: true
                          availableTranspositions: []
                          pitchData:
                            ascending:
                              - pitchClassIndex: 0
                                octave: 1
                                scaleDegree: "I"
                                noteName: "rast"
                                noteNameDisplay: "rāst"
                                cents: 0
                              - pitchClassIndex: 4
                                octave: 1
                                scaleDegree: "II"
                                noteName: "dugah"
                                noteNameDisplay: "dūgāh"
                                cents: 203.91000173077484
                            descending:
                              - pitchClassIndex: 20
                                octave: 1
                                scaleDegree: "VII"
                                noteName: "ajam"
                                noteNameDisplay: "ʿajam"
                                cents: 1019.5580263750745
                              - pitchClassIndex: 18
                                octave: 1
                                scaleDegree: "VI"
                                noteName: "husayni"
                                noteNameDisplay: "ḥusaynī"
                                cents: 905.8650025961623
                          ajnas:
                            ascending:
                              rāst:
                                id: "19"
                                idName: "jins_rast"
                                displayName: "jins rāst"
                              dūgāh:
                                id: "2"
                                idName: "jins_bayyat"
                                displayName: "jins bayyāt"
                              chahārgāh: null
                            descending:
                              ʿajam: null
                              ḥusaynī:
                                id: "30"
                                idName: "jins_kurd_ushayran"
                                displayName: "jins kurd ʿushayrān"
                          intervals:
                            ascending:
                              - cents: 203.91000173077484
                              - cents: 152.95309101871007
                            descending:
                              - cents: 113.69302377891222
                              - cents: 203.91000173077487
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/maqamat/maqam_rast?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/maqamat/maqam_rast/availability"
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954"]
                        displayNames: ["alsabbagh_1954"]
                      startingNotes:
                        idNames: ["rast"]
                        displayNames: ["rast"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_rast/compare?tuningSystems=alsabbagh_1954&startingNotes=rast&pitchClassDataType=cents"
                      detail: "/api/maqamat/maqam_rast"
                      availability: "/api/maqamat/maqam_rast/availability"
                multipleStartingNotes:
                  summary: Cartesian product — 2 tuning systems × 3 starting notes
                  description: |
                    Requests `maqam_bayyat` against 2 tuning systems × 3 starting notes, yielding
                    6 cells in user-supplied order (outer = `tuningSystems`, inner =
                    `startingNotes`). Three cells are successful; three cells carry a `note`
                    because the requested starting note isn't available in that tuning system.
                    `pitchData` is trimmed to 2 entries per direction for brevity.
                  value:
                    maqam:
                      id: "7"
                      idName: "maqam_bayyat"
                      displayName: "maqām bayyāt"
                      version: "2025-10-18T19:42:22.369Z"
                    comparisons:
                      count: 6
                      totalComparisons: 6
                      successfulComparisons: 3
                      unavailableStartingNotes: 3
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          maqam:
                            id: "7"
                            idName: "maqam_bayyat"
                            displayName: "maqām bayyāt"
                            version: "2025-10-18T19:42:22.369Z"
                            maqamFamilyId: "bayyat"
                            maqamFamilyDisplayName: "bayyāt"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            maqam:
                              numberOfPitchClasses: 7
                              isOctaveRepeating: true
                          availableTranspositions: []
                          pitchData:
                            ascending:
                              - pitchClassIndex: 4
                                octave: 1
                                scaleDegree: "I"
                                noteName: "dugah"
                                noteNameDisplay: "dūgāh"
                                cents: 203.91000173077484
                              - pitchClassIndex: 7
                                octave: 1
                                scaleDegree: "II"
                                noteName: "segah"
                                noteNameDisplay: "segāh"
                                cents: 356.8630927494849
                            descending:
                              - pitchClassIndex: 0
                                octave: 2
                                scaleDegree: "VII"
                                noteName: "kurdan"
                                noteNameDisplay: "kurdān"
                                cents: 1200
                              - pitchClassIndex: 20
                                octave: 1
                                scaleDegree: "VI"
                                noteName: "ajam"
                                noteNameDisplay: "ʿajam"
                                cents: 1019.5580263750745
                          ajnas:
                            ascending:
                              dūgāh:
                                id: "2"
                                idName: "jins_bayyat"
                                displayName: "jins bayyāt"
                              chahārgāh: null
                            descending:
                              kurdān:
                                id: "19"
                                idName: "jins_rast"
                                displayName: "jins rāst"
                              ʿajam: null
                          intervals:
                            ascending:
                              - cents: 152.95309101871007
                              - cents: 141.1819063851276
                            descending:
                              - cents: 180.44197362492548
                              - cents: 113.69302377891222
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/maqamat/maqam_bayyat?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/maqamat/maqam_bayyat/availability"
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "yegah"
                            displayName: "yegah"
                          note: "Starting note 'yegah' is not available in tuning system 'al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone'"
                          validStartingNotes:
                            idNames: ["rast"]
                            displayNames: ["rāst"]
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "ushayran"
                            displayName: "ushayran"
                          note: "Starting note 'ushayran' is not available in tuning system 'al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone'"
                          validStartingNotes:
                            idNames: ["rast"]
                            displayNames: ["rāst"]
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rast"
                          note: "Starting note 'rast' is not available in tuning system 'Ibn Sīnā  (1037) 7-Fret Oud 17-Tone'"
                          validStartingNotes:
                            idNames: ["ushayran", "yegah"]
                            displayNames: ["ʿushayrān", "yegāh"]
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "yegah"
                            displayName: "yegāh"
                          maqam:
                            id: "7"
                            idName: "maqam_bayyat"
                            displayName: "maqām bayyāt"
                            version: "2025-10-18T19:42:22.369Z"
                            maqamFamilyId: "bayyat"
                            maqamFamilyDisplayName: "bayyāt"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 17
                              referenceFrequency: 97.999
                            maqam:
                              numberOfPitchClasses: 7
                              isOctaveRepeating: true
                          availableTranspositions:
                            - idName: "maqam_bayyat_al-yegah"
                              displayName: "maqām bayyāt al-yegāh"
                              tonic:
                                idName: "yegah"
                                displayName: "yegāh"
                            - idName: "maqam_bayyat_al-nawa"
                              displayName: "maqām bayyāt al-nawā"
                              tonic:
                                idName: "nawa"
                                displayName: "nawā"
                          pitchData:
                            ascending:
                              - pitchClassIndex: 10
                                octave: 1
                                scaleDegree: "I"
                                noteName: "dugah"
                                noteNameDisplay: "dūgāh"
                                cents: 701.9550008653874
                              - pitchClassIndex: 12
                                octave: 1
                                scaleDegree: "II"
                                noteName: "segah"
                                noteNameDisplay: "segāh"
                                cents: 840.5276617693106
                            descending:
                              - pitchClassIndex: 7
                                octave: 2
                                scaleDegree: "VII"
                                noteName: "kurdan"
                                noteNameDisplay: "kurdān"
                                cents: 1698.0449991346125
                              - pitchClassIndex: 4
                                octave: 2
                                scaleDegree: "VI"
                                noteName: "ajam"
                                noteNameDisplay: "ʿajam"
                                cents: 1494.1349974038376
                          ajnas:
                            ascending:
                              dūgāh:
                                id: "2"
                                idName: "jins_bayyat"
                                displayName: "jins bayyāt"
                              nawā:
                                id: "19"
                                idName: "jins_rast_al-nawa"
                                displayName: "jins rāst al-nawā"
                            descending:
                              kurdān:
                                id: "19"
                                idName: "jins_rast"
                                displayName: "jins rāst"
                              ʿajam: null
                          intervals:
                            ascending:
                              - cents: 138.57266090392318
                              - cents: 155.5623364999144
                            descending:
                              - cents: 203.91000173077487
                              - cents: 90.22499567306272
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/maqamat/maqam_bayyat?tuningSystem=ibnsina_1037&startingNote=yeg%C4%81h&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/maqamat/maqam_bayyat/availability"
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "ushayran"
                            displayName: "ʿushayrān"
                          maqam:
                            id: "7"
                            idName: "maqam_bayyat"
                            displayName: "maqām bayyāt"
                            version: "2025-10-18T19:42:22.369Z"
                            maqamFamilyId: "bayyat"
                            maqamFamilyDisplayName: "bayyāt"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 17
                              referenceFrequency: 97.999
                            maqam:
                              numberOfPitchClasses: 7
                              isOctaveRepeating: true
                          availableTranspositions:
                            - idName: "maqam_bayyat_al-ushayran"
                              displayName: "maqām bayyāt al-ʿushayrān"
                              tonic:
                                idName: "ushayran"
                                displayName: "ʿushayrān"
                            - idName: "maqam_bayyat_al-nawa"
                              displayName: "maqām bayyāt al-nawā"
                              tonic:
                                idName: "nawa"
                                displayName: "nawā"
                          pitchData:
                            ascending:
                              - pitchClassIndex: 5
                                octave: 1
                                scaleDegree: "I"
                                noteName: "dugah"
                                noteNameDisplay: "dūgāh"
                                cents: 498.0449991346125
                              - pitchClassIndex: 7
                                octave: 1
                                scaleDegree: "II"
                                noteName: "segah"
                                noteNameDisplay: "segāh"
                                cents: 636.6176600385357
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/maqamat/maqam_bayyat?tuningSystem=ibnsina_1037&startingNote=%CA%BFushayr%C4%81n&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/maqamat/maqam_bayyat/availability"
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954", "ibnsina_1037"]
                        displayNames: ["alsabbagh_1954", "ibnsina_1037"]
                      startingNotes:
                        idNames: ["rast", "yegah", "ushayran"]
                        displayNames: ["rast", "yegah", "ushayran"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_bayyat/compare?tuningSystems=alsabbagh_1954,ibnsina_1037&startingNotes=rast,yegah,ushayran&pitchClassDataType=cents"
                      detail: "/api/maqamat/maqam_bayyat"
                      availability: "/api/maqamat/maqam_bayyat/availability"
                unavailableStartingNote:
                  summary: Single cell whose starting note isn't available (`note` field)
                  description: |
                    Demonstrates the `note` (informational, not error) per-cell shape when the
                    requested starting note is not available in the given tuning system. The
                    response is HTTP 200 and `unavailableStartingNotes` is incremented.
                  value:
                    maqam:
                      id: "7"
                      idName: "maqam_bayyat"
                      displayName: "maqām bayyāt"
                      version: "2025-10-18T19:42:22.369Z"
                    comparisons:
                      count: 1
                      totalComparisons: 1
                      successfulComparisons: 0
                      unavailableStartingNotes: 1
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "bogus_note"
                            displayName: "bogus_note"
                          note: "Starting note 'bogus_note' is not available in tuning system 'Ibn Sīnā  (1037) 7-Fret Oud 17-Tone'"
                          validStartingNotes:
                            idNames: ["ushayran", "yegah"]
                            displayNames: ["ʿushayrān", "yegāh"]
                    parameters:
                      tuningSystems:
                        idNames: ["ibnsina_1037"]
                        displayNames: ["ibnsina_1037"]
                      startingNotes:
                        idNames: ["bogus_note"]
                        displayNames: ["bogus_note"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_bayyat/compare?tuningSystems=ibnsina_1037&startingNotes=bogus_note&pitchClassDataType=cents"
                      detail: "/api/maqamat/maqam_bayyat"
                      availability: "/api/maqamat/maqam_bayyat/availability"
                withErrors:
                  summary: Mixed success and hard-failure cells
                  description: |
                    Demonstrates a cell with `error` (hard failure — tuning system not found)
                    alongside a successful cell. Hard failures use a minimal fallback
                    `tuningSystem` namespace built from the raw request ID.
                  value:
                    maqam:
                      id: "1"
                      idName: "maqam_rast"
                      displayName: "maqām rāst"
                      version: "2025-12-10T17:03:34.565Z"
                    comparisons:
                      count: 2
                      totalComparisons: 2
                      successfulComparisons: 1
                      unavailableStartingNotes: 0
                      failedComparisons: 1
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          maqam:
                            id: "1"
                            idName: "maqam_rast"
                            displayName: "maqām rāst"
                            version: "2025-12-10T17:03:34.565Z"
                            maqamFamilyId: "rast"
                            maqamFamilyDisplayName: "rāst"
                          tonic:
                            idName: "rast"
                            displayName: "rāst"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            maqam:
                              numberOfPitchClasses: 7
                              isOctaveRepeating: true
                          availableTranspositions: []
                          pitchData:
                            ascending:
                              - pitchClassIndex: 0
                                octave: 1
                                scaleDegree: "I"
                                noteName: "rast"
                                noteNameDisplay: "rāst"
                                cents: 0
                            descending:
                              - pitchClassIndex: 0
                                octave: 1
                                scaleDegree: "I"
                                noteName: "rast"
                                noteNameDisplay: "rāst"
                                cents: 0
                          ajnas:
                            ascending:
                              rāst:
                                id: "19"
                                idName: "jins_rast"
                                displayName: "jins rāst"
                            descending:
                              rāst:
                                id: "19"
                                idName: "jins_rast"
                                displayName: "jins rāst"
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/maqamat/maqam_rast?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/maqamat/maqam_rast/availability"
                        - tuningSystem:
                            id: "InvalidSystem"
                            idName: "invalidsystem"
                            displayName: "InvalidSystem"
                          startingNote:
                            idName: "rast"
                            displayName: "rast"
                          error: "Tuning system 'InvalidSystem' not found"
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954", "invalidsystem"]
                        displayNames: ["alsabbagh_1954", "InvalidSystem"]
                      startingNotes:
                        idNames: ["rast"]
                        displayNames: ["rast"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/maqamat/maqam_rast/compare?tuningSystems=alsabbagh_1954,InvalidSystem&startingNotes=rast&pitchClassDataType=cents"
                      detail: "/api/maqamat/maqam_rast"
                      availability: "/api/maqamat/maqam_rast/availability"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingTuningSystems:
                  summary: Missing tuningSystems parameter
                  value:
                    error: "tuningSystems parameter is required"
                    message: "Provide comma-separated tuning system IDs"
                    hint: 'Format: ?tuningSystems=ibnsina_1037,alfarabi_950g'
                    example: "/api/maqamat/maqam_rast/compare?tuningSystems=ibnsina_1037,alfarabi_950g&startingNotes=yegah&pitchClassDataType=cents"
                missingStartingNotes:
                  summary: Missing startingNotes parameter
                  value:
                    error: "startingNotes parameter is required"
                    message: "At least one starting note must be specified"
                    hint: "Add &startingNotes=yegah,rast to your request (comma-separated list)"
                emptyStartingNotes:
                  summary: startingNotes parameter is empty or whitespace-only
                  value:
                    error: "Invalid parameter: startingNotes"
                    message: "The 'startingNotes' parameter must contain at least one starting note name."
                    hint: "Use ?startingNotes=yegah or ?startingNotes=yegah,rast,ushayran"
                invalidIntervals:
                  summary: Invalid includeIntervals parameter
                  value:
                    error: "Invalid parameter: includeIntervals"
                    message: "The 'includeIntervals' parameter must be 'true' or 'false'."
                    validOptions: ["true", "false"]
                    hint: "Use ?includeIntervals=true or ?includeIntervals=false"
        '404':
          description: Maqām not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Maqām 'maqam_invalid' not found"
                hint: "Use /api/maqamat to see all available maqāmāt"
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/classification/12-pitch-class-sets:
    get:
      tags:
        - Maqāmāt
      summary: Classify maqāmāt by 12-pitch-class sets
      description: |
        Groups maqāmāt according to sets of 12 pitch classes suitable for MIDI keyboard tuning
        and Scala file export. Each set is created by merging pitch classes from the specified
        tuning system with alkindi_874 filler pitch classes, based on matching IPN reference
        note names.

        **Critical Design Principles:**
        - **Octave Alignment**: Both the maqām tuning system and alkindi_874 use the same
          `startingNote` to ensure octaves align correctly
        - **Chromatic Order**: Each set contains exactly 12 pitch classes ordered chromatically
          starting from the maqām's tonic (e.g., C, C#, D, D#... for Rāst)
        - **Arabic Musicological Logic**: IPN references respect Arabic maqām theory where
          microtonal modifiers indicate what the pitch is a variant OF, not mathematical proximity
          to 12-EDO semitones
        - **Direct Tuning System Values**: All cent values come directly from tuning systems
          without post-processing or calculation

        **The Algorithm:**
        1. Loads pitch classes from both the specified tuning system and alkindi_874 using
           the same `startingNote`
        2. For each maqām transposition:
           - Extracts IPN reference note names from ascending and descending sequences
           - Replaces matching pitch classes in al-Kindi base (ascending takes priority, then descending)
           - Selects pitch classes from correct octaves to ensure ascending chromatic order
           - Orders the 12 pitch classes chromatically starting from the maqām's tonic
        3. Groups compatible maqāmāt together
        4. Names each group after the source maqām (e.g., "maqām rāst set")

        **Compatibility:**
        - Maqāmāt with duplicate IPN references (same IPN appearing with different pitch values)
          are marked as incompatible and cannot form valid 12-pitch-class sets
        - Compatible maqāmāt can be performed entirely within a single 12-pitch-class set

        **Output Format:**
        - Pitch classes ordered chromatically (C, C#, D, D#, E, F, F#, G, G#, A, A#, B)
        - Starting from the maqām's tonic (default) OR from IPN "C" (if `startSetFromC=true`)
        - Relative cents from 0 (reference note) to ~1050-1200 (octave)
        - `maqamTonic` field indicates the actual maqām tonic position and note name
        - Suitable for MIDI keyboard tuning and Scala (.scl) file generation

        **Filtering Options:**
        - Use `setId` to retrieve a specific set and its compatible maqāmāt
        - Use `maqamId` to find all sets containing a specific maqām
      operationId: classifyMaqamat12PitchClassSets
      parameters:
        - name: tuningSystem
          in: query
          description: "Tuning system ID for maqāmāt (default: cairocongresstuningcommittee_1929)"
          schema:
            type: string
            default: "cairocongresstuningcommittee_1929"
          example: "cairocongresstuningcommittee_1929"
        - name: startingNote
          in: query
          description: |
            Starting note for both the maqām tuning system and alkindi_874 (default: yegah).
            IMPORTANT: Both tuning systems must use the same starting note to ensure octaves align
            correctly and pitch classes can be properly selected from matching octaves.

            Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
            default: "yegah"
          example: "yegah"
        - name: centsTolerance
          in: query
          description: "Tolerance in cents for pitch class comparison (default: 5)"
          schema:
            type: number
            default: 5
            minimum: 0
          example: 5
        - name: includeIncompatible
          in: query
          description: Include maqāmāt that cannot form valid 12-pitch-class sets
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: startSetFromC
          in: query
          description: |
            Start pitch class set from IPN reference "C" (degree 0) instead of from the maqām's tonic.
            When `true`, the set is reordered to start from C at 0.00 cents (relative), making it
            directly compatible with Scala (.scl) file format which maps degree 0 to middle C by default.

            **Technical Implementation:**
            1. **Octave Selection**: For maqāmāt starting mid-octave (e.g., D), pitch classes before the tonic in chromatic order (C, C#) are taken from octave 1, while those at or after the tonic use their original octaves
            2. **Array Rotation**: The 12 pitch classes are rotated to place C first
            3. **Relative Cents**: Calculated from C (0.00 cents), with octave wrap-around handling (negative values + 1200)
            4. **Note Names**: Arabic note names follow the NoteName model's octave conventions (e.g., C octave 1 = rāst, C octave 2 = kurdān)
            5. **Tonic Tracking**: The `maqamTonic` field indicates the actual maqām tonic's IPN, note names, and position in the reordered set

            **Use Cases:**
            - `false` (default): For understanding maqām structure starting from its tonic
            - `true`: For Scala .scl export (no .kbm file needed) or MIDI keyboard mapping where C = degree 0

            **Example:**
            - Maqām ḥijāz (tonic D/dūgāh at 702.13 cents):
              - Default mode: D at position 0 (0.00 cents relative)
              - Scala mode: C (rāst, 498.04 cents) at position 0 (0.00 cents relative), D at position 2 (204.08 cents relative)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: pitchClassDataType
          in: query
          required: false
          description: |
            Controls which pitch class data fields are returned in the response.

            **Available Options:**
            - `all`: Returns all available pitch class data fields
            - `englishName`: English note name with octave (e.g., "D2", "E-b3")
            - `fraction`: Frequency ratio as a fraction string
            - `cents`: Absolute cents value from the tuning system
            - `decimalRatio`: Frequency ratio as decimal number
            - `stringLength`: String length for oud/qanun (if available)
            - `frequency`: Frequency in Hz
            - `abjadName`: Arabic abjad notation (if available)
            - `fretDivision`: Fret position for oud (if available)
            - `midiNoteNumber`: MIDI note number as decimal
            - `midiNoteDeviation`: MIDI note with cents deviation string
            - `centsDeviation`: Cents deviation from 12-EDO
            - `referenceNoteName`: IPN reference note name
            - `relativeCents`: Cents relative to the first pitch class (0.00 for tonic or C)

            **Default Behavior (parameter omitted):**
            - Returns minimal fields: `ipnReferenceNoteName`, `noteName`, `relativeCents`, `octave`

            **Example:**
            - `?pitchClassDataType=cents` returns only IPN, note name, and cents values
            - `?pitchClassDataType=all` returns all available data fields
          schema:
            type: string
            enum:
              - all
              - abjadName
              - englishName
              - solfege
              - fraction
              - cents
              - centsDeviation
              - decimalRatio
              - stringLength
              - fretDivision
              - midiNoteNumber
              - midiNoteDeviation
              - frequency
              - referenceNoteName
              - relativeCents
          example: "cents"
        - name: setId
          in: query
          description: "Filter by specific set ID to retrieve that set and its compatible maqāmāt (e.g., 'maqam_rast_set')"
          schema:
            type: string
          example: "maqam_rast_set"
        - name: maqamId
          in: query
          description: "Filter by maqām ID to find all sets containing that maqām (e.g., 'maqam_rast')"
          schema:
            type: string
          example: "maqam_rast"
      responses:
        '200':
          description: Classification results with sets and compatible maqāmāt
          content:
            application/json:
              schema:
                type: object
                properties:
                  summary:
                    type: array
                    description: Summary of each set with compatible maqāmāt names
                    items:
                      type: object
                      properties:
                        setName:
                          type: string
                          example: "maqām rast set"
                        compatibleMaqamat:
                          type: array
                          items:
                            type: object
                            properties:
                              maqamName:
                                type: string
                              transpositionName:
                                type: string
                              isTransposed:
                                type: boolean
                  statistics:
                    type: object
                    description: Statistics about the classification
                    properties:
                      totalSets:
                        type: number
                      sets:
                        type: array
                        items:
                          type: object
                          properties:
                            setName:
                              type: string
                            compatibleMaqamatCount:
                              type: number
                      totalCompatibleMaqamat:
                        type: number
                      incompatibleMaqamatCount:
                        type: number
                  sets:
                    type: array
                    description: Classification sets grouping compatible maqāmāt
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: URL-safe identifier for the set
                          example: "maqam_rast_set"
                        setIdName:
                          type: string
                          description: Standardized identifier for the set
                          example: "maqam_rast_set"
                        setDisplayName:
                          type: string
                          description: Display name of the set
                          example: "maqām rāst set"
                        sourceMaqam:
                          $ref: '#/components/schemas/EntityRef'
                        sourceTransposition:
                          type: object
                          properties:
                            name:
                              type: string
                              example: "maqām rāst"
                            isTransposed:
                              type: boolean
                              example: false
                        pitchClassSet:
                          type: array
                          description: The 12 pitch classes in this set
                          items:
                            type: object
                            properties:
                              ipnReferenceNoteName:
                                type: string
                                example: "C"
                              noteName:
                                type: string
                                example: "rāst"
                              cents:
                                type: number
                                example: 0
                              fraction:
                                type: string
                                example: "1/1"
                              decimalRatio:
                                type: number
                                example: 1.0
                              frequency:
                                type: number
                                example: 130.665
                              octave:
                                type: number
                                example: 1
                        maqamTonic:
                          type: object
                          description: Information about the actual maqām tonic position in the set
                          properties:
                            ipnReferenceNoteName:
                              type: string
                              description: IPN reference name of the tonic (e.g., "C", "F", "G"). Uses pitch class equivalent for octave variants (qarār dūgāh → "D")
                              example: "C"
                            noteNameIdName:
                              type: string
                              description: Standardized identifier for the tonic note name (ASCII-safe, URL-friendly)
                              example: "rast"
                            noteNameDisplayName:
                              type: string
                              description: Display name of the tonic note (with diacritics)
                              example: "rāst"
                            positionInSet:
                              type: number
                              description: |
                                Index position (0-11) of the maqām tonic in the pitch class set.
                                - In default mode (startSetFromC=false): Always 0
                                - In Scala mode (startSetFromC=true): Varies based on tonic (e.g., 2 for D, 5 for F)
                                - Octave equivalents (qarār dūgāh, muḥayyar) map to same position as their pitch class (D → position 2)
                              example: 0
                        compatibleMaqamat:
                          type: array
                          description: Maqāmāt compatible with this set
                          items:
                            type: object
                            properties:
                              maqamIdName:
                                type: string
                                description: Standardized identifier for the maqām transposition
                                example: "maqam_rast"
                              maqamDisplayName:
                                type: string
                                description: Display name of the maqām transposition
                                example: "maqām rāst"
                              isTransposed:
                                type: boolean
                                description: Whether this is a transposition of the base maqām
                        compatibleMaqamatCount:
                          type: number
                          description: Number of compatible maqāmāt in this set
                  totalSets:
                    type: number
                    description: Total number of classification sets
                  totalCompatibleMaqamat:
                    type: number
                    description: Total number of compatible maqām transpositions across all sets
                  incompatibleMaqamat:
                    type: array
                    description: Maqāmāt that cannot form valid 12-pitch-class sets
                    items:
                      type: object
                      properties:
                        maqam:
                          $ref: '#/components/schemas/EntityRef'
                        transposition:
                          type: object
                          nullable: true
                          properties:
                            name:
                              type: string
                            isTransposed:
                              type: boolean
                        reason:
                          type: string
                          example: "duplicate_ipn_reference"
                  incompatibleMaqamatCount:
                    type: number
                    description: Number of incompatible maqāmāt
                  parameters:
                    type: object
                    description: Parameters used for classification
                    properties:
                      tuningSystem:
                        type: object
                        properties:
                          id:
                            type: string
                          idName:
                            type: string
                          displayName:
                            type: string
                      startingNote:
                        type: object
                        properties:
                          idName:
                            type: string
                          displayName:
                            type: string
                      alKindiTuningSystem:
                        type: object
                        properties:
                          id:
                            type: string
                          idName:
                            type: string
                          displayName:
                            type: string
                      alKindiStartingNote:
                        type: object
                        properties:
                          idName:
                            type: string
                          displayName:
                            type: string
                      centsTolerance:
                        type: number
                      startSetFromC:
                        type: boolean
                        description: Whether the set starts from IPN "C" (Scala mode) or from the maqām tonic (default)
                        example: false
                      setId:
                        type: string
                        nullable: true
                        description: Set ID filter if applied
                        example: "maqam_rast_set"
                      maqamId:
                        type: string
                        nullable: true
                        description: Maqām ID filter if applied
                        example: "maqam_rast"
              examples:
                default:
                  summary: Default response
                  value:
                    summary:
                      - setIdName: "maqam_rast_set"
                        setDisplayName: "maqām rāst set"
                        compatibleMaqamat:
                          - maqamIdName: "maqam_rast"
                            maqamDisplayName: "maqām rāst"
                            isTransposed: false
                    statistics:
                      totalSets: 1
                      sets:
                        - setIdName: "maqam_rast_set"
                          setDisplayName: "maqām rāst set"
                          compatibleMaqamatCount: 1
                      totalCompatibleMaqamat: 1
                      incompatibleMaqamatCount: 0
                    sets:
                      - id: "maqam_rast_set"
                        setIdName: "maqam_rast_set"
                        setDisplayName: "maqām rāst set"
                        sourceMaqam:
                          id: "1"
                          idName: "maqam_rast"
                          displayName: "maqām rāst"
                        sourceTransposition:
                          idName: "maqam_rast"
                          displayName: "maqām rāst"
                          isTransposed: false
                        pitchClassSet:
                          - ipnReferenceNoteName: "C"
                            noteName: "rāst"
                            cents: 0
                            fraction: "1/1"
                            decimalRatio: 1.0
                            frequency: 130.665
                            octave: 1
                        compatibleMaqamat:
                          - maqamIdName: "maqam_rast"
                            maqamDisplayName: "maqām rāst"
                            isTransposed: false
                        compatibleMaqamatCount: 1
                    totalSets: 1
                    totalCompatibleMaqamat: 1
                    incompatibleMaqamat: []
                    incompatibleMaqamatCount: 0
                    parameters:
                      tuningSystem:
                        id: "cairocongresstuningcommittee_1929"
                        idName: "cairocongresstuningcommittee-1929"
                        displayName: "Cairo Congress Tuning Committee (1929)"
                      startingNote:
                        idName: "yegah"
                        displayName: "yegāh"
                      alKindiTuningSystem:
                        id: "alkindi_874"
                        idName: "al-kindi-874"
                        displayName: "al-Kindī (874)"
                      alKindiStartingNote:
                        idName: "ushayran"
                        displayName: "ʿushayrān"
                      centsTolerance: 5
                      startSetFromC: false
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Set or maqām not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                setNotFound:
                  summary: Set not found
                  value:
                    error: "Set 'maqam_invalid_set' not found"
                    hint: "Use ?setId=<set-id> to filter by a specific set (e.g., ?setId=maqam_rast_set)"
                maqamNotFound:
                  summary: Maqām not found in any set
                  value:
                    error: "No sets found containing maqam 'maqam_invalid'"
                    hint: "Use ?maqamId=<maqam-id> to find sets containing a specific maqām (e.g., ?maqamId=maqam_rast)"
        '500':
          $ref: '#/components/responses/ServerError'

  /maqamat/classification/maqam-pitch-class-sets:
    get:
      tags:
        - Maqāmāt
      summary: Classify maqāmāt by maqam-based pitch class sets
      description: |
        Groups maqāmāt according to maqam-based pitch class sets. Each set contains all pitch
        classes from a single source maqam (union of ascending and descending), and identifies
        which other maqāmāt can be performed using only those pitch classes.

        **Musicological Purpose:**
        Answers the question: "Which maqāmāt can be performed using only the pitch classes of
        maqām X?" For example, maqām rāst on rāst includes pitch classes that can perform maqām
        bayyāt on dūgāh and maqām segāh on segāh.

        **Key Differences from 12-Pitch-Class-Sets:**
        - **No Chromatic Base**: Does not merge with alkindi_874 or chromatic filler pitch classes
        - **Variable Set Sizes**: Sets can contain any number of pitch classes (7, 8, 10, etc.), not fixed at 12
        - **Octave-Equivalent Matching**: Pitch classes in different octaves treated as equivalent (C in octave 1 = C in octave 2)
        - **Single Tuning System**: Uses only the specified tuning system (simpler than 12-pitch-class-sets)
        - **Subset Matching**: Compatible maqāmāt must use subset of source maqam's pitch classes

        **Critical Design Principles:**
        - **Octave Equivalence**: IPN references are compared octave-normalized (C at any octave = "C")
        - **Union of Melodic Paths**: Includes all pitch classes from both ascending AND descending sequences
        - **Exact Matching**: All pitch classes from a compatible maqām must exist in the source set (within tolerance)
        - **Direct Values**: All cent values come directly from tuning system without calculation
        - **Tahlil Priority**: Base (non-transposed) maqāmāt create canonical sets first, then transpositions

        **The Algorithm:**
        1. For each maqām transposition (tahlil first, then transpositions):
           - Extracts all unique pitch classes from ascending + descending sequences
           - Groups by IPN reference (octave-equivalent)
           - Validates no duplicate IPN references with different cents values
           - Creates a maqam-based pitch class set
        2. For each set, finds compatible maqāmāt:
           - Checks if all pitch classes needed by candidate maqām exist in source set
           - Compares cents values within tolerance (octave-normalized)
           - Marks candidate as compatible if all pitch classes match
        3. Groups compatible maqāmāt together
        4. Names each group after the source maqām (e.g., "maqām rāst set")

        **Compatibility:**
        - Maqāmāt with duplicate IPN references (same IPN with different cents values when octave-normalized)
          are marked as incompatible
        - Compatible maqāmāt can be performed entirely using the pitch classes of the source maqām
        - **Minimum Set Size**: Only sets with 2 or more maqāmāt are returned (source + at least one compatible)
        - Sets containing only the source maqām (no compatible maqāmāt) are filtered out

        **Output Format:**
        - Sets ordered by number of compatible maqāmāt (descending)
        - Each set includes: source maqām, all pitch classes (in ascending cents order), compatible maqāmāt, pitch class count
        - Pitch classes include: English name, note name, cents, frequency, fraction, etc.

        **Filtering Options:**
        - Use `setId` to retrieve a specific set and its compatible maqāmāt
        - Use `maqamId` to find all sets containing a specific maqām
      operationId: classifyMaqamatByMaqamPitchClassSets
      parameters:
        - name: tuningSystem
          in: query
          description: "Tuning system ID for all maqāmāt (default: cairocongresstuningcommittee_1929)"
          schema:
            type: string
            default: "cairocongresstuningcommittee_1929"
          example: "cairocongresstuningcommittee_1929"
        - name: startingNote
          in: query
          description: |
            Starting note for the tuning system (default: yegah).

            Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
            default: "yegah"
          example: "yegah"
        - name: centsTolerance
          in: query
          description: "Tolerance in cents for pitch class comparison (default: 5)"
          schema:
            type: number
            default: 5
            minimum: 0
          example: 5
        - name: includeIncompatible
          in: query
          description: Include maqāmāt that cannot form valid maqam-based pitch class sets
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includePitchClassData
          in: query
          description: |
            Include pitch class data in the response (default: false).

            When true: Returns pitchClassSet and pitchClassCount for each set
            When false: Omits pitch class data, returning only set metadata and compatible maqamat
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: setId
          in: query
          description: "Filter by specific set ID to retrieve that set and its compatible maqāmāt (e.g., 'maqam_rast_set')"
          schema:
            type: string
          example: "maqam_rast_set"
        - name: maqamId
          in: query
          description: "Filter by maqām ID to find all sets containing that maqām (e.g., 'maqam_rast')"
          schema:
            type: string
          example: "maqam_rast"
      responses:
        '200':
          description: Classification results with maqam-based pitch class sets and compatible maqāmāt
          content:
            application/json:
              schema:
                type: object
                properties:
                  statistics:
                    type: object
                    description: Statistics about the classification
                    properties:
                      totalSets:
                        type: number
                        description: Total number of sets
                      totalCompatibleMaqamat:
                        type: number
                        description: Total number of compatible maqāmāt across all sets
                      incompatibleMaqamatCount:
                        type: number
                        description: Total number of incompatible maqāmāt
                      totalProcessed:
                        type: number
                        description: Total number of maqāmāt processed (compatible + incompatible)
                  sets:
                    type: array
                    description: Classification sets grouping compatible maqāmāt
                    items:
                      type: object
                      properties:
                        setIdName:
                          type: string
                          description: Standardized identifier for the set
                          example: "maqam_rast_set"
                        setDisplayName:
                          type: string
                          description: Display name of the set
                          example: "maqām rāst set"
                        compatibleMaqamatCount:
                          type: number
                          description: Number of compatible maqāmāt in this set (deduplicated, no octave equivalents)
                        sourceMaqam:
                          $ref: '#/components/schemas/EntityRef'
                        pitchClassSet:
                          type: array
                          description: |
                            All pitch classes in this maqam-based set (variable number, not fixed at 12).
                            Includes all pitch classes from the source maqām's ascending and descending sequences.
                            Ordered by ascending cents values (from lowest to highest pitch).
                            Only included when includePitchClassData=true.
                          items:
                            type: object
                            properties:
                              englishName:
                                type: string
                                description: English note name with octave (e.g., "C3", "D#4")
                                example: "C3"
                              noteName:
                                type: string
                                description: Note name from the tuning system
                                example: "rāst"
                              cents:
                                type: number
                                description: Absolute cents value from the tuning system
                                example: 0
                              fraction:
                                type: string
                                description: Frequency ratio as fraction
                                example: "1/1"
                              decimalRatio:
                                type: number
                                description: Frequency ratio as decimal
                                example: 1.0
                              frequency:
                                type: number
                                description: Frequency in Hz
                                example: 130.665
                              octave:
                                type: number
                                description: Octave number
                                example: 1
                              noteNameDisplayAr:
                                type: string
                                description: Arabic display name (included when includeArabic=true)
                                example: "راست"
                        pitchClassCount:
                          type: number
                          description: |
                            Number of pitch classes in this set (variable, not fixed at 12).
                            Only included when includePitchClassData=true.
                          example: 7
                        compatibleMaqamat:
                          type: array
                          description: |
                            Maqāmāt compatible with this set (can be performed using only these pitch classes).
                            Octave equivalents are deduplicated (e.g., maqam rast on rast and maqam rast on muhayyar rast
                            are counted as one entry since they share the same base maqam and IPN reference)
                          items:
                            type: object
                            properties:
                              maqamIdName:
                                type: string
                                description: Standardized identifier for the maqām transposition
                                example: "maqam_rast"
                              maqamDisplayName:
                                type: string
                                description: Display name of the maqām transposition
                                example: "maqām rāst"
                              isTransposed:
                                type: boolean
                                description: Whether this is a transposition of the base maqām
                              tonic:
                                type: object
                                nullable: true
                                description: Information about the maqām's tonic note
                                properties:
                                  ipnReferenceNoteName:
                                    type: string
                                    description: IPN reference name of the tonic (e.g., "C", "D", "F")
                                    example: "C"
                                  noteNameIdName:
                                    type: string
                                    description: Standardized identifier for the tonic note name
                                    example: "rast"
                                  noteNameDisplayName:
                                    type: string
                                    description: Display name of the tonic note (with diacritics)
                                    example: "rāst"
                  incompatibleMaqamat:
                    type: array
                    description: Maqāmāt that cannot form valid maqam-based pitch class sets (only included when includeIncompatible=true)
                    items:
                      type: object
                      properties:
                        maqam:
                          $ref: '#/components/schemas/EntityRef'
                        transposition:
                          type: object
                          nullable: true
                          properties:
                            idName:
                              type: string
                              description: Standardized identifier for the transposition
                              example: "maqam_rast"
                            displayName:
                              type: string
                              description: Display name of the transposition
                              example: "maqām rāst"
                            isTransposed:
                              type: boolean
                              description: Whether this is a transposition
                              example: false
                        reason:
                          type: string
                          description: Reason why this maqām is incompatible
                          example: "duplicate_ipn_reference"
                  incompatibleMaqamatCount:
                    type: number
                    description: Number of incompatible maqāmāt (only included when includeIncompatible=true)
                  parameters:
                    type: object
                    description: Parameters used for classification
                    properties:
                      tuningSystem:
                        type: object
                        properties:
                          id:
                            type: string
                          idName:
                            type: string
                          displayName:
                            type: string
                      startingNote:
                        type: object
                        properties:
                          idName:
                            type: string
                          displayName:
                            type: string
                      centsTolerance:
                        type: number
                      setId:
                        type: string
                        nullable: true
                        description: Set ID filter if applied
                        example: "maqam_rast_set"
                      maqamId:
                        type: string
                        nullable: true
                        description: Maqām ID filter if applied
                        example: "maqam_rast"
              examples:
                default:
                  summary: Default response
                  value:
                    statistics:
                      totalSets: 1
                      totalCompatibleMaqamat: 3
                      incompatibleMaqamatCount: 0
                      totalProcessed: 3
                    sets:
                      - setIdName: "maqam_rast_set"
                        setDisplayName: "maqām rāst set"
                        compatibleMaqamatCount: 3
                        sourceMaqam:
                          id: "1"
                          idName: "maqam_rast"
                          displayName: "maqām rāst"
                        pitchClassSet:
                          - englishName: "C3"
                            noteName: "rāst"
                            cents: 0
                            fraction: "1/1"
                            decimalRatio: 1.0
                            frequency: 130.665
                            octave: 1
                          - englishName: "D3"
                            noteName: "dūgāh"
                            cents: 203.91
                            fraction: "9/8"
                            decimalRatio: 1.125
                            frequency: 146.998
                            octave: 1
                          - englishName: "E3"
                            noteName: "segāh"
                            cents: 407.82
                            fraction: "81/64"
                            decimalRatio: 1.266
                            frequency: 165.361
                            octave: 1
                        pitchClassCount: 7
                        compatibleMaqamat:
                          - maqamIdName: "maqam_rast"
                            maqamDisplayName: "maqām rāst"
                            isTransposed: false
                            tonic:
                              ipnReferenceNoteName: "C"
                              noteNameIdName: "rast"
                              noteNameDisplayName: "rāst"
                          - maqamIdName: "maqam_bayyat_dugah"
                            maqamDisplayName: "maqām bayyāt (dūgāh)"
                            isTransposed: true
                            tonic:
                              ipnReferenceNoteName: "D"
                              noteNameIdName: "dugah"
                              noteNameDisplayName: "dūgāh"
                          - maqamIdName: "maqam_segah_segah"
                            maqamDisplayName: "maqām segāh (segāh)"
                            isTransposed: true
                            tonic:
                              ipnReferenceNoteName: "E"
                              noteNameIdName: "segah"
                              noteNameDisplayName: "segāh"
                    parameters:
                      tuningSystem:
                        id: "cairocongresstuningcommittee_1929"
                        idName: "cairocongresstuningcommittee-1929"
                        displayName: "Cairo Congress Tuning Committee (1929)"
                      startingNote:
                        idName: "yegah"
                        displayName: "yegāh"
                      centsTolerance: 5
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Set or maqām not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                setNotFound:
                  summary: Set not found
                  value:
                    error: "Set 'maqam_invalid_set' not found"
                    hint: "Use ?setId=<set-id> to filter by a specific set (e.g., ?setId=maqam_rast_set)"
                maqamNotFound:
                  summary: Maqām not found in any set
                  value:
                    error: "No sets found containing maqam 'maqam_invalid'"
                    hint: "Use ?maqamId=<maqam-id> to find sets containing a specific maqām (e.g., ?maqamId=maqam_rast)"
        '500':
          $ref: '#/components/responses/ServerError'

  /tuning-systems/{id}/{startingNote}/maqamat:
    get:
      tags:
        - Tuning Systems
      summary: List maqāmāt available in a tuning system
      description: |
        Return all maqāmāt that can be realized in a given tuning system beginning on a specific starting note name.
        
        - Feasibility is determined by comparing maqām pitch classes to the tuning system's note name sets (including adjacent octave context)
      operationId: listTuningSystemMaqamat
      parameters:
        - name: id
          in: path
          required: true
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          schema:
                    type: string
          example: "alfarabi_950g"
        - name: startingNote
          in: path
          required: true
          description: Tuning system starting note name (URL-safe, diacritics-insensitive). Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "ushayran"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeMaqamDegrees
          in: query
          required: false
          description: |
            When true, each maqām in data includes a `maqamDegrees` object with ascending and descending arrays
            of PAO note name idNames (URL-safe identifiers for scale degrees).
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeDegreeDetails
          in: query
          required: false
          description: |
            When true (and when includeMaqamDegrees=true), the endpoint returns `maqamDegrees.ascending` /
            `maqamDegrees.descending` as arrays of objects `{ noteName, englishName, solfege }` instead of
            note-name strings. The same transformation applies to `transpositions[].maqamDegrees`.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeTranspositions
          in: query
          required: false
          description: |
            When true, each maqām in data includes a `transpositions` array with the other transpositions
            (excluding the base tonic). Each transposition has `tonic` and `maqamDegrees` with ascending/descending
            note name idNames for that transposed position.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: sortBy
          in: query
          required: false
          description: |
            Sort order for results. Defaults to `tonic` on this endpoint (different from `/maqamat`
            and `/ajnas`, which default to `alphabetical`) because the endpoint is intrinsically about
            a concrete pitch layout. Invalid values return HTTP 400.
          schema:
            type: string
            enum: [alphabetical, tonic]
            default: tonic
          examples:
            tonic:
              value: tonic
              summary: Sort by tonic note priority across the full NoteName.ts octave-stratified order, alphabetical tiebreak
            alphabetical:
              value: alphabetical
              summary: Sort alphabetically by display name
      responses:
        '200':
          description: Maqāmāt list retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - totalNumberOfMaqamat
                  - tuningSystem
                  - data
                properties:
                  totalNumberOfMaqamat:
                    type: integer
                    description: Total number of maqāmāt available in this tuning system with the specified starting note
                  tuningSystem:
                    type: object
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    type: object
                  links:
                    type: object
              example:
                {
                  "tuningSystem": {
                    "id": "ibnsina_1037",
                    "idName": "ibnsina_1037",
                    "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                    "version": "2025-11-09T00:44:40.275Z",
                    "year": "1037",
                    "numberOfPitchClassesSingleOctave": 17
                  },
                  "startingNotes": {
                    "idNames": [
                      "ushayran",
                      "yegah"
                    ],
                    "displayNames": [
                      "ʿushayrān",
                      "yegāh"
                    ]
                  },
                  "selectedStartingNote": {
                    "idName": "yegah",
                    "displayName": "yegāh"
                  },
                  "stats": {
                    "totalMaqamatForStartingNote": 43,
                    "totalMaqamatInLibrary": 63,
                    "uniqueTonicCount": 9,
                    "uniqueFamilyCount": 15,
                    "referenceFrequency": 97.999
                  },
                  "data": [
                    {
                      "maqam": {
                        "id": "34",
                        "idName": "maqam_dilkeshidah",
                        "displayName": "maqām dilkeshīdah",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "family": {
                        "idName": "nahawand",
                        "displayName": "nahāwand"
                      },
                      "tonic": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 7
                      },
                      "characteristics": {
                        "isOctaveRepeating": true,
                        "hasAsymmetricDescending": true,
                        "hasSuyur": true
                      },
                      "links": {
                        "detail": "/api/maqamat/maqam_dilkeshidah"
                      }
                    },
                    {
                      "maqam": {
                        "id": "30",
                        "idName": "maqam_farahfazza",
                        "displayName": "maqām faraḥfazzā",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "family": {
                        "idName": "nahawand",
                        "displayName": "nahāwand"
                      },
                      "tonic": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 7
                      },
                      "characteristics": {
                        "isOctaveRepeating": true,
                        "hasAsymmetricDescending": true,
                        "hasSuyur": true
                      },
                      "links": {
                        "detail": "/api/maqamat/maqam_farahfazza"
                      }
                    },
                    {
                      "maqam": {
                        "id": "53",
                        "idName": "maqam_rast_yegah",
                        "displayName": "maqām rāst yegāh",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "family": {
                        "idName": "rast",
                        "displayName": "rāst"
                      },
                      "tonic": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 7
                      },
                      "characteristics": {
                        "isOctaveRepeating": true,
                        "hasAsymmetricDescending": true,
                        "hasSuyur": false
                      },
                      "links": {
                        "detail": "/api/maqamat/maqam_rast_yegah"
                      }
                    },
                    {
                      "maqam": {
                        "id": "32",
                        "idName": "maqam_sultani_yegah",
                        "displayName": "maqām sulṭānī yegāh",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "family": {
                        "idName": "nahawand",
                        "displayName": "nahāwand"
                      },
                      "tonic": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 7
                      },
                      "characteristics": {
                        "isOctaveRepeating": true,
                        "hasAsymmetricDescending": true,
                        "hasSuyur": true
                      },
                      "links": {
                        "detail": "/api/maqamat/maqam_sultani_yegah"
                      }
                    },
                    {
                      "maqam": {
                        "id": "61",
                        "idName": "maqam_zanjaran",
                        "displayName": "maqām zanjarān",
                        "version": "2025-12-11T23:34:00.303Z"
                      },
                      "family": {
                        "idName": "hijaz",
                        "displayName": "ḥijāz"
                      },
                      "tonic": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 7
                      },
                      "characteristics": {
                        "isOctaveRepeating": true,
                        "hasAsymmetricDescending": true,
                        "hasSuyur": false
                      },
                      "links": {
                        "detail": "/api/maqamat/maqam_zanjaran"
                      }
                    },
                    {
                      "…": "truncated — 38 more items"
                    }
                  ],
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/tuning-systems/ibnsina_1037/yegah/maqamat",
                    "tuningSystem": "/api/tuning-systems/ibnsina_1037",
                    "collection": "/api/maqamat"
                  }
                }
        '400':
          description: Invalid path parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /modulation-routes:
    get:
      tags:
        - Modulations
      summary: Find modulation routes between maqāmāt
      description: |
        Calculate possible modulation routes between maqāmāt, similar to a navigation/maps application.

        Uses al-Shawwā's 1946 modulation rules to find shortest paths between a source and target maqām,
        with optional waypoints and return-to-start functionality.

        **Key Features:**
        - Finds shortest paths (fewest modulation steps) using BFS algorithm
        - Supports waypoints for multi-stop journeys
        - Optional return-to-start for round-trip routes (reflecting traditional maqām performance practice)
        - `limitToShortestHops=true` returns only the shortest-length
          routes; default (`false`) pads the result with progressively
          longer routes up to `maxRoutes` / `maxHops`
        - `allowOctaveJumps=true` lets BFS traverse register-shift
          (8va/8vb) edges between register-equivalent siblings; default
          (`false`) keeps routes strictly within al-Shawwā's
          scale-degree modulation rules
        - `allowDownwardModulation=true` lets BFS traverse direct
          downward-modulation edges (`*OctaveBelow` categories, e.g.
          `sixthDegreeAscOctaveBelow` / `VI-8vb`). These are single-hop
          modulations to a different maqām whose tonic sits one octave
          below the ascending rule's target — common in Arabic practice
          (e.g. saba:dūgāh → ajam ushayrān:ajam ushayrān as one move).
          Default (`false`) ignores those edges
        - Results cached per tuning system + starting note combination for performance
        - `maxHops` is required and capped at 20 to prevent combinatorial explosion

        **Graph Structure:**
        - Nodes: Each (maqamId, tonic) pair is a unique node
        - Edges: Modulation possibilities based on al-Shawwā's rules (scale degrees I, III, IV, V, VI)

        **Performance Notes:**
        - maxHops is required to prevent combinatorial explosion
        - First request for a tuning system + starting note builds the graph (may take longer)
        - Subsequent requests use cached graph data
      operationId: findModulationRoutes
      parameters:
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., 'alfarabi_950g', 'ibnsina_1037')
          schema:
            type: string
          example: "alfarabi_950g"
        - name: startingNote
          in: query
          required: true
          description: |
            Tuning system starting note name (URL-safe, diacritics-insensitive).
            This is MANDATORY as it affects which maqāmāt are available and their modulation possibilities.
          schema:
            type: string
          example: "ushayran"
        - name: fromMaqam
          in: query
          required: true
          description: |
            Source maqam base ID (the starting point of the journey).
            Use the base maqam identifier (e.g., "maqam_nahawand"), not the transposition display name.
            Combine with `fromTonic` to specify a particular transposition.
          schema:
            type: string
          example: "maqam_rast"
        - name: fromTonic
          in: query
          required: false
          description: |
            Optional specific tonic for the source maqam (URL-safe).
            If not specified, uses the canonical (taḥlīl) form.
          schema:
            type: string
          example: "rast"
        - name: toMaqam
          in: query
          required: true
          description: |
            Target maqam base ID (the destination of the journey).
            Use the base maqam identifier (e.g., "maqam_nahawand"), not the transposition display name.
            Combine with `toTonic` to specify a particular transposition.
          schema:
            type: string
          example: "maqam_segah"
        - name: toTonic
          in: query
          required: false
          description: |
            Optional specific tonic for the target maqam (URL-safe).
            If not specified, uses the canonical (taḥlīl) form.
          schema:
            type: string
          example: "nawa"
        - name: maxHops
          in: query
          required: true
          description: |
            Maximum number of modulation steps allowed (required safeguard).
            Prevents combinatorial explosion in route calculation.
            Most musical modulation sequences are 2-4 hops.
            Maximum allowed value is 20.
          schema:
            type: integer
            minimum: 1
            maximum: 20
          example: 5
        - name: waypoints
          in: query
          required: false
          description: |
            Optional comma-separated waypoints to pass through.
            Format: maqamId:tonicId or just maqamId (uses canonical tonic).
            Example: "maqam_bayyat:dugah,maqam_saba"
          schema:
            type: string
          example: "maqam_bayyat:dugah"
        - name: returnToStartingMaqam
          in: query
          required: false
          description: |
            Whether to calculate the return path back to the source (starting) maqam.
            Reflects traditional maqām performance practice where music returns to the starting mode.
            When true, response includes both outbound and return routes.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: maxRoutes
          in: query
          required: false
          description: Maximum number of routes to return
          schema:
            type: integer
            minimum: 1
            default: 10
          example: 10
        - name: limitToShortestHops
          in: query
          required: false
          description: |
            When `true`, only routes at the minimum hop count are
            returned — all routes in the response share the same `hops`
            value. When `false` (default), the response is filled out
            with progressively longer simple paths (still capped by
            `maxHops` and `maxRoutes`, ordered shortest-first).
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: allowOctaveJumps
          in: query
          required: false
          description: |
            When `true`, BFS may traverse register-shift (`8va` / `8vb`)
            edges between register-equivalent siblings of the same
            maqām (e.g. ḥijāz:muḥayyar → ḥijāz:dūgāh). These edges
            appear anywhere in a route — at the start, in the middle, or
            at the end — and let the algorithm reach register variants
            that aren't directly connected by scale-degree modulation.
            When `false` (default), those edges are ignored and every
            hop in every returned route must be a genuine al-Shawwā
            modulation (degrees I / III / IV / V / VI).

            Note: `allowOctaveJumps` controls PURE register shifts
            (same maqām, different octave). `allowDownwardModulation`
            controls DOWNWARD MODULATIONS (different maqām at a
            lower-octave tonic). They are independent flags.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: allowDownwardModulation
          in: query
          required: false
          description: |
            When `true`, BFS may traverse direct downward-modulation
            edges (`*OctaveBelow` categories, e.g.
            `sixthDegreeAscOctaveBelow` / `VI-8vb`). These represent a
            single-hop modulation to a different maqām whose tonic sits
            one octave below the corresponding ascending rule's target —
            common in Arabic practice (e.g. saba:dūgāh → ajam
            ushayrān:ajam ushayrān as one move rather than as VI↑ + 8vb).
            When `false` (default), those edges are ignored and such a
            move can only be reached via the equivalent two-hop
            ascending-modulation + register-shift combination.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.
            Adds Arabic versions with "Ar" suffix (e.g., maqamDisplayNameAr, tonicDisplayAr)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Modulation routes calculated successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - totalPossibleRoutes
                  - context
                properties:
                  totalPossibleRoutes:
                    type: object
                    description: |
                      Wraps the outbound routes returned for this query along
                      with the stats for both directions. Both counts live
                      here (before `data`) so clients see totals up-front
                      without having to walk into each collection. `data`
                      holds the outbound routes; the corresponding return
                      routes live in the sibling `possibleReturnRoutes`.
                    required:
                      - outboundRoutesCount
                      - data
                    properties:
                      outboundRoutesCount:
                        type: integer
                        description: Number of outbound routes returned in `data`
                      returnRoutesCount:
                        type: integer
                        description: |
                          Number of return routes returned in
                          `possibleReturnRoutes.data`. Present only when
                          `returnToStartingMaqam=true`.
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/ModulationJourney'
                  possibleReturnRoutes:
                    type: object
                    description: |
                      All shortest return routes from the canonical target back
                      to any register-equivalent of the source, capped at
                      `maxRoutes`. Present only when
                      `returnToStartingMaqam=true`. Returns are enumerated
                      once and shared across every outbound journey — they are
                      not nested inside each journey. The count is reported in
                      `totalPossibleRoutes.returnRoutesCount` (not duplicated
                      here).
                    required:
                      - data
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/NumberedReturnRoute'
                  context:
                    type: object
                    properties:
                      tuningSystem:
                        type: object
                        properties:
                          id:
                            type: string
                          displayName:
                            type: string
                          displayNameAr:
                            type: string
                      startingNote:
                        type: object
                        properties:
                          idName:
                            type: string
                          displayName:
                            type: string
                          displayNameAr:
                            type: string
                      sourceNode:
                        $ref: '#/components/schemas/MaqamNode'
                      targetNode:
                        $ref: '#/components/schemas/MaqamNode'
                      waypoints:
                        type: array
                        items:
                          $ref: '#/components/schemas/MaqamNode'
                      searchConstraints:
                        type: object
                        properties:
                          maxHops:
                            type: integer
                          maxRoutes:
                            type: integer
                          returnToStartingMaqam:
                            type: boolean
                          limitToShortestHops:
                            type: boolean
                          allowOctaveJumps:
                            type: boolean
                          allowDownwardModulation:
                            type: boolean
                  message:
                    type: string
                    description: Present when no routes found, explains why
              examples:
                routeFound:
                  summary: Successful route finding
                  value:
                    totalPossibleRoutes:
                      outboundRoutesCount: 1
                      data:
                        - routeNumber: 1
                          outboundRoute:
                            hops: 2
                            steps:
                              - from:
                                  maqamId: "1"
                                  maqamIdName: "maqam_rast"
                                  maqamDisplayName: "maqām rāst"
                                  tonicId: "rast"
                                  tonicDisplay: "rāst"
                                  isTransposition: false
                                to:
                                  maqamId: "2"
                                  maqamIdName: "maqam_bayyat"
                                  maqamDisplayName: "maqām bayyāt"
                                  tonicId: "dugah"
                                  tonicDisplay: "dūgāh"
                                  isTransposition: false
                                modulationDegree: "III"
                                modulationCategory: "thirdDegree"
                              - from:
                                  maqamId: "2"
                                  maqamIdName: "maqam_bayyat"
                                  maqamDisplayName: "maqām bayyāt"
                                  tonicId: "dugah"
                                  tonicDisplay: "dūgāh"
                                  isTransposition: false
                                to:
                                  maqamId: "5"
                                  maqamIdName: "maqam_hijaz"
                                  maqamDisplayName: "maqām ḥijāz"
                                  tonicId: "nawa"
                                  tonicDisplay: "nawā"
                                  isTransposition: false
                                modulationDegree: "IV"
                                modulationCategory: "fourthDegree"
                    context:
                      tuningSystem:
                        id: "alfarabi_950g"
                        displayName: "al-Fārābī (950) غ"
                      startingNote:
                        idName: "ushayran"
                        displayName: "ʿushayrān"
                      sourceNode:
                        maqamId: "maqam_rast"
                        maqamDisplayName: "Maqam Rast"
                        tonicId: "rast"
                        tonicDisplay: "rāst"
                        isTransposition: false
                      targetNode:
                        maqamId: "maqam_hijaz"
                        maqamDisplayName: "Maqam Hijaz"
                        tonicId: "nawa"
                        tonicDisplay: "nawā"
                        isTransposition: false
                      searchConstraints:
                        maxHops: 5
                        maxRoutes: 10
                        returnToStartingMaqam: false
                        limitToShortestHops: false
                        allowOctaveJumps: false
                        allowDownwardModulation: false
                noRouteFound:
                  summary: No path found within constraints
                  value:
                    totalPossibleRoutes:
                      outboundRoutesCount: 0
                      data: []
                    context:
                      tuningSystem:
                        id: "alfarabi_950g"
                        displayName: "al-Fārābī (950) غ"
                      startingNote:
                        idName: "ushayran"
                        displayName: "ʿushayrān"
                      sourceNode:
                        maqamId: "maqam_rast"
                        maqamDisplayName: "Maqam Rast"
                        tonicId: "rast"
                        tonicDisplay: "rāst"
                        isTransposition: false
                      targetNode:
                        maqamId: "maqam_sikah"
                        maqamDisplayName: "Maqam Sikah"
                        tonicId: "segah"
                        tonicDisplay: "segāh"
                        isTransposition: false
                      searchConstraints:
                        maxHops: 3
                        maxRoutes: 10
                        returnToStartingMaqam: false
                        limitToShortestHops: false
                        allowOctaveJumps: false
                        allowDownwardModulation: false
                    message: "No path found from 'maqam_rast' to 'maqam_sikah' within 3 hops"
        '400':
          description: Invalid or missing required parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingMaxHops:
                  value:
                    error: "maxHops parameter is required"
                    message: "maxHops is required to prevent combinatorial explosion in route calculation."
                    hint: "Add &maxHops=5 to your request. Recommended values: 3-7 for most use cases."
                maxHopsExceeded:
                  value:
                    error: "maxHops cannot exceed 20"
                    message: "Higher values may cause performance issues. Most musical modulation sequences are 2-4 hops."
                    hint: "Use maxHops=20 or lower"
        '404':
          description: Maqam or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /ajnas:
    get:
      tags:
        - Ajnās
      summary: List all ajnās
      description: |
        Return all ajnās (singular: jins) with metadata.
        
        - Includes URL-safe identifiers, display names, and structural information
        - Structural information includes note sequences needed for maqām construction and analysis
        - Optional bibliographic source references (sourceId and page) when `includeSources=true`
      operationId: listAjnas
      parameters:
        - name: filterByTonic
          in: query
          description: Filter by jins tonic/first note (URL-safe, case-insensitive, diacritics-insensitive)
          schema:
            type: string
          example: rast
        - name: sortBy
          in: query
          description: |
            Sort order for results. Invalid values (anything other than `alphabetical` or `tonic`,
            including empty string) return HTTP 400.
          schema:
            type: string
            enum: [alphabetical, tonic]
            default: alphabetical
          examples:
            alphabetical:
              value: alphabetical
              summary: Sort alphabetically by display name (default)
            tonic:
              value: tonic
              summary: Sort by tonic note priority across the full NoteName.ts octave-stratified order, alphabetical tiebreak
        - name: includeSources
          in: query
          required: false
          description: Include bibliographic source references (sourceId and page) for each jins
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: List of ajnās retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - count
                  - data
                properties:
                  count:
                    type: integer
                    description: Total number of ajnās
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        jins:
                          $ref: '#/components/schemas/EntityRef'
                        tonic:
                          $ref: '#/components/schemas/IdentifierRef'
                        stats:
                          type: object
                          properties:
                            numberOfPitchClasses:
                              type: integer
                            availableInTuningSystems:
                              type: integer
                        availability:
                          type: object
                          properties:
                            tuningSystems:
                              type: array
                        sources:
                          type: array
                          description: Bibliographic source references for this jins
                          items:
                            type: object
                            properties:
                              sourceId:
                                type: string
                                description: URL-safe source identifier
                                example: "al-Shawwa-(1946)"
                              page:
                                type: string
                                description: Page number or range where the jins is documented
                                example: "49"
                        links:
                          $ref: '#/components/schemas/Links'
              example:
                {
                  "count": 29,
                  "data": [
                    {
                      "jins": {
                        "id": "5",
                        "idName": "jins_ajam_ushayran",
                        "displayName": "jins ʿajam ʿushayrān",
                        "version": "2025-10-18T19:34:26.343Z"
                      },
                      "tonic": {
                        "idName": "ajam_ushayran",
                        "displayName": "ʿajam ʿushayrān"
                      },
                      "stats": {
                        "numberOfPitchClasses": 4,
                        "availableInTuningSystems": 35
                      },
                      "availability": {
                        "tuningSystems": [
                          {
                            "tuningSystem": {
                              "id": "ronzevalle_1904",
                              "idName": "ronzevalle_1904",
                              "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                              "version": "2025-11-26T10:19:38.569Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "alkindi_874",
                              "idName": "alkindi_874",
                              "displayName": "al-Kindī (874) 12-Tone",
                              "version": "2025-11-17T13:16:34.754Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah",
                                "rast"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh",
                                "rāst"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "angloeuropean_1700",
                              "idName": "angloeuropean_1700",
                              "displayName": "Anglo-European (1700) 12-EDO",
                              "version": "2025-11-18T11:53:58.428Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926a",
                              "idName": "aldik_1926a",
                              "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926b",
                              "idName": "aldik_1926b",
                              "displayName": "al-Dīk (1926b) Old Arabic Scale",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "…": "truncated — 30 more items"
                          }
                        ]
                      },
                      "links": {
                        "detail": "/api/ajnas/jins_ajam_ushayran"
                      },
                      "sources": []
                    },
                    {
                      "jins": {
                        "id": "11",
                        "idName": "jins_athar_kurd",
                        "displayName": "jins athar kurd",
                        "version": "2025-10-18T19:34:45.799Z"
                      },
                      "tonic": {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 5,
                        "availableInTuningSystems": 33
                      },
                      "availability": {
                        "tuningSystems": [
                          {
                            "tuningSystem": {
                              "id": "ronzevalle_1904",
                              "idName": "ronzevalle_1904",
                              "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                              "version": "2025-11-26T10:19:38.569Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "alkindi_874",
                              "idName": "alkindi_874",
                              "displayName": "al-Kindī (874) 12-Tone",
                              "version": "2025-11-17T13:16:34.754Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah",
                                "rast"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh",
                                "rāst"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "angloeuropean_1700",
                              "idName": "angloeuropean_1700",
                              "displayName": "Anglo-European (1700) 12-EDO",
                              "version": "2025-11-18T11:53:58.428Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926a",
                              "idName": "aldik_1926a",
                              "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926b",
                              "idName": "aldik_1926b",
                              "displayName": "al-Dīk (1926b) Old Arabic Scale",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "…": "truncated — 28 more items"
                          }
                        ]
                      },
                      "links": {
                        "detail": "/api/ajnas/jins_athar_kurd"
                      },
                      "sources": []
                    },
                    {
                      "jins": {
                        "id": "24",
                        "idName": "jins_awj_ara",
                        "displayName": "jins awj ʾārāʾ",
                        "version": "2025-10-18T19:34:26.343Z"
                      },
                      "tonic": {
                        "idName": "iraq",
                        "displayName": "ʿirāq"
                      },
                      "stats": {
                        "numberOfPitchClasses": 4,
                        "availableInTuningSystems": 29
                      },
                      "availability": {
                        "tuningSystems": [
                          {
                            "tuningSystem": {
                              "id": "ronzevalle_1904",
                              "idName": "ronzevalle_1904",
                              "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                              "version": "2025-11-26T10:19:38.569Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926a",
                              "idName": "aldik_1926a",
                              "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926b",
                              "idName": "aldik_1926b",
                              "displayName": "al-Dīk (1926b) Old Arabic Scale",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932a",
                              "idName": "cairocongresstuningcommittee_1932a",
                              "displayName": "Cairo Congress Tuning Committee (1932a) Ancient Intervals",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932b",
                              "idName": "cairocongresstuningcommittee_1932b",
                              "displayName": "Cairo Congress Tuning Committee (1932b) Arab Intervals according to al-Dīk",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "…": "truncated — 24 more items"
                          }
                        ]
                      },
                      "links": {
                        "detail": "/api/ajnas/jins_awj_ara"
                      },
                      "sources": [
                        {
                          "sourceId": "alshawwa_1946",
                          "page": "38"
                        }
                      ]
                    },
                    {
                      "jins": {
                        "id": "2",
                        "idName": "jins_bayyat",
                        "displayName": "jins bayyāt",
                        "version": "2025-10-18T19:35:30.479Z"
                      },
                      "tonic": {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      "stats": {
                        "numberOfPitchClasses": 4,
                        "availableInTuningSystems": 31
                      },
                      "availability": {
                        "tuningSystems": [
                          {
                            "tuningSystem": {
                              "id": "ronzevalle_1904",
                              "idName": "ronzevalle_1904",
                              "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                              "version": "2025-11-26T10:19:38.569Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926a",
                              "idName": "aldik_1926a",
                              "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926b",
                              "idName": "aldik_1926b",
                              "displayName": "al-Dīk (1926b) Old Arabic Scale",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932a",
                              "idName": "cairocongresstuningcommittee_1932a",
                              "displayName": "Cairo Congress Tuning Committee (1932a) Ancient Intervals",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932b",
                              "idName": "cairocongresstuningcommittee_1932b",
                              "displayName": "Cairo Congress Tuning Committee (1932b) Arab Intervals according to al-Dīk",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "…": "truncated — 26 more items"
                          }
                        ]
                      },
                      "links": {
                        "detail": "/api/ajnas/jins_bayyat"
                      },
                      "sources": []
                    },
                    {
                      "jins": {
                        "id": "28",
                        "idName": "jins_bayyat_ushayran",
                        "displayName": "jins bayyāt ʿushayrān",
                        "version": "2025-10-18T19:34:26.343Z"
                      },
                      "tonic": {
                        "idName": "ushayran",
                        "displayName": "ʿushayrān"
                      },
                      "stats": {
                        "numberOfPitchClasses": 4,
                        "availableInTuningSystems": 30
                      },
                      "availability": {
                        "tuningSystems": [
                          {
                            "tuningSystem": {
                              "id": "ronzevalle_1904",
                              "idName": "ronzevalle_1904",
                              "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                              "version": "2025-11-26T10:19:38.569Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "ushayran",
                                "yegah"
                              ],
                              "displayNames": [
                                "ʿushayrān",
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926a",
                              "idName": "aldik_1926a",
                              "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "aldik_1926b",
                              "idName": "aldik_1926b",
                              "displayName": "al-Dīk (1926b) Old Arabic Scale",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932a",
                              "idName": "cairocongresstuningcommittee_1932a",
                              "displayName": "Cairo Congress Tuning Committee (1932a) Ancient Intervals",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "tuningSystem": {
                              "id": "cairocongresstuningcommittee_1932b",
                              "idName": "cairocongresstuningcommittee_1932b",
                              "displayName": "Cairo Congress Tuning Committee (1932b) Arab Intervals according to al-Dīk",
                              "version": "2025-10-18T19:42:23.643Z"
                            },
                            "startingNotes": {
                              "idNames": [
                                "yegah"
                              ],
                              "displayNames": [
                                "yegāh"
                              ]
                            }
                          },
                          {
                            "…": "truncated — 25 more items"
                          }
                        ]
                      },
                      "links": {
                        "detail": "/api/ajnas/jins_bayyat_ushayran"
                      },
                      "sources": []
                    },
                    {
                      "…": "truncated — 24 more items"
                    }
                  ]
                }
        '500':
          $ref: '#/components/responses/ServerError'

  /ajnas/{idName}:
    get:
      tags:
        - Ajnās
      summary: Get detailed jins data
      description: |
        Retrieve comprehensive data for a specific jins on its conventional or a transposed tonic, in a specific tuning system on a specific starting note name.
        
        Returns:
        - Comprehensive pitch class data in the requested tuning system and starting note name
        - Optional includeIntervals data, transposition to different tonics, and tuning-system-specific modulations (including lower octave)
        
        Parameter Discovery:
        - Use options=true to discover valid values for all parameters including dynamically calculated transposition options
        - Especially useful for finding valid tuning system starting notes and transposition tonics for a specific jins
        
        Important:
        - When options=true, the endpoint returns parameter discovery metadata instead of jins data
        - This mode is mutually exclusive with data-returning parameters (transpose to, include modulations, include lower octave modulations, pitch class data type, includeIntervals)
        - If data-returning parameters are provided with options=true, the API returns a 400 Bad Request error with details about conflicting parameters
        
        Requirements:
        - Tuning system and starting note are required for all requests (both data retrieval and discovery mode)
        - These are fundamental to all pitch class calculations
      operationId: getJins
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe jins identifier
          schema:
            type: string
          examples:
            rast:
              value: jins_rast
              summary: Jins Rāst (representative example - with zalzalian intervals)
            kurd:
              value: jins_kurd
              summary: Jins Kurd (representative example - without zalzalian intervals)
            bayyat:
              value: jins_bayyat
              summary: Jins Bayyāt (representative example - with zalzalian intervals)
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          schema:
            type: string
          examples:
            ibnsina:
              value: "ibnsina_1037"
              summary: Ibn Sīnā (1037) - 7-Fret Oud 17-Tone (representative example)
            farabi:
              value: "alfarabi_950g"
              summary: al-Fārābī (950g) - First Oud Tuning 27-Tone (representative example)
            meshshaqa:
              value: "meshshaqa_1899"
              summary: Meshshāqa (1899) - Arabic Octave According to the Modernists (representative example)
        - name: startingNote
          in: query
          required: true
          description: |
            Tuning system starting note name (URL-safe, diacritics-insensitive).
            
            - Mandatory for all pitch class calculations
            - Different starting notes represent different theoretical frameworks within the same tuning system
            - Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          examples:
            yegah:
              value: "yegah"
              summary: yegāh (for IbnSina, Meshshaqa)
            ushayran:
              value: "ushayran"
              summary: ʿushayrān (for al-Farabi, al-Kindi)
            rast:
              value: "rast"
              summary: rāst (for CairoCongress, al-Sabbagh)
        - name: options
          in: query
          description: |
            When true, returns available parameter options instead of jins data.
            
            - Useful for discovering valid values for tuning systems, starting notes, and other parameters
            - Tuning system and starting note are required for all requests (both data retrieval and discovery mode)
            - These are fundamental to all pitch class calculations and calculate valid starting note options and transposition tonics
            - Mutually exclusive with data-returning parameters (transpose to, include modulations, include lower octave modulations, pitch class data type, intervals)
            - Transposition options are dynamically calculated based on the specific jins, tuning system, and starting note combination
            - Only tonics where the jins can be validly transposed (preserving interval pattern) are included, not all possible pitch classes
            - If data-returning parameters are provided, returns 400 Bad Request error with details about conflicting parameters
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: pitchClassDataType
          in: query
          required: true
          description: |
            Specifies which pitch class data type to return for each pitch class.

            - Defaults to `all` when the parameter is omitted from the query string. Effectively required from a contract perspective — the endpoint always returns a populated `pitchClassDataType` in `parameters` and `pitchData` formatted accordingly.
            - When set to `all`, returns the full set of pitch class fields (englishName, solfege, abjadName, fraction, cents, decimalRatio, stringLength, frequency, fretDivision, midiNoteDecimal, midiNotePlusCentsDeviation, centsDeviation, ipnReferenceNoteName) plus the always-included identifiers (pitchClassIndex, octave, scaleDegree, noteName, noteNameDisplay, and noteNameDisplayAr if includeArabic is enabled).
            - When set to a specific format, returns only that field plus the always-included identifiers.
            - Empty string and unrecognised values return HTTP 400 with a hint listing valid options.
            - In discovery mode (when options=true), this parameter remains optional. Passing any value other than `cents` in discovery mode is treated as a conflict (400).
          schema:
            type: string
            enum: [all, abjadName, englishName, solfege, fraction, cents, centsDeviation, decimalRatio, stringLength, fretDivision, midiNoteNumber, midiNoteDeviation, frequency, referenceNoteName]
            default: all
          example: "cents"
        - name: transposeTo
          in: query
          description: |
            Transpose the jins to a new tonic by preserving the interval patterns (URL-safe, diacritics-insensitive).
            
            - Only tonics where the jins can be validly transposed in the specified tuning system and starting note are valid
            - To see all valid transposition options, request available parameter options instead of jins data
          schema:
            type: string
          example: nawa
        - name: includeIntervals
          in: query
          description: Include interval data between jins degrees
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: |
            Jins data retrieved successfully.
            
            - When options is true, the response structure differs and returns available parameter options instead of jins data
            - In that case, transposition options contain only valid transposition tonics for this specific jins, tuning system, and starting note combination
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JinsDetailResponse'
              examples:
                basic:
                  summary: Basic jins data
                  value:
                    jins:
                      id: "5"
                      idName: "jins_kurd"
                      displayName: "Jins Kurd"
                      version: "2025-10-18T19:41:17.132Z"
                      tonicId: "rast"
                      tonicName: "rāst"
                      transposition: false
                      numberOfTranspositions: 17
                      numberOfPitchClasses: 4
                      commentsEnglish: null
                      commentsArabic: null
                      sources: []
                      pitchClassDataType: "all"
                      includeIntervals: false
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        displayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                        version: "2025-10-18T19:41:17.132Z"
                        originalValueType: "fraction"
                        numberOfPitchClassesSingleOctave: 17
                        numberOfPitchClassesAllOctaves: 68
                        startingNoteName: "yegah"
                        referenceFrequency: 440.0
                    pitchData:
                      - pitchClassIndex: 0
                        octave: 1
                        scaleDegree: "I"
                        noteName: "rast"
                        noteNameDisplay: "rāst"
                        englishName: "C3"
                        fraction: "1/1"
                        cents: 0.0
                        decimalRatio: 1.0
                        stringLength: 1000.0
                        frequency: 440.0
                        fretDivision: "0.000"
                        midiNoteDecimal: 48.0
                        midiNotePlusCentsDeviation: "48 +0.0"
                        centsDeviation: 0.0
                        ipnReferenceNoteName: "C"
                      - pitchClassIndex: 1
                        octave: 1
                        scaleDegree: "II"
                        noteName: "dugah"
                        noteNameDisplay: "dūgāh"
                        englishName: "D3"
                        fraction: "204/182"
                        cents: 204.0
                        decimalRatio: 1.120
                        frequency: 492.8
                transposed:
                  summary: Transposed jins (taṣwīr)
                  value:
                    jins:
                      id: "5"
                      idName: "jins_kurd"
                      displayName: "Jins Kurd al-Nawā"
                      version: "2025-10-18T19:41:17.132Z"
                      tonicId: "nawa"
                      tonicName: "nawā"
                      transposition: true
                      originalTonicId: "rast"
                      originalTonicName: "rāst"
                      transposedTonicId: "nawa"
                      transposedTonicName: "nawā"
                      numberOfTranspositions: 17
                      numberOfPitchClasses: 4
                      commentsEnglish: null
                      commentsArabic: null
                      sources: []
                      pitchClassDataType: "all"
                      includeIntervals: false
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        displayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                        version: "2025-10-18T19:41:17.132Z"
                        originalValueType: "fraction"
                        numberOfPitchClassesSingleOctave: 17
                        numberOfPitchClassesAllOctaves: 68
                        startingNoteName: "yegah"
                        referenceFrequency: 440.0
                    pitchData:
                      - pitchClassIndex: 3
                        octave: 1
                        scaleDegree: "I"
                        noteName: "nawa"
                        noteNameDisplay: "nawā"
                        englishName: "F3"
                        fraction: "498/415"
                        cents: 498.0
                withModulations:
                  summary: With modulation networks
                  value:
                    jins:
                      id: "5"
                      idName: "jins_kurd"
                      displayName: "Jins Kurd"
                      tonicId: "rast"
                      tonicName: "rāst"
                      transposition: false
                      pitchClassDataType: "cents"
                      includeIntervals: false
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        displayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                        startingNoteName: "yegah"
                    pitchData:
                      - pitchClassIndex: 0
                        octave: 1
                        scaleDegree: "I"
                        noteName: "rast"
                        noteNameDisplay: "rāst"
                        cents: 0.0
                      - pitchClassIndex: 1
                        octave: 1
                        scaleDegree: "II"
                        noteName: "dugah"
                        noteNameDisplay: "dūgāh"
                        cents: 204.0
                withOptions:
                  summary: Options response (when options=true)
                  value:
                    jins: "Jins Kurd"
                    tuningSystem: "ibnsina_1037"
                    availableParameters:
                      tuningSystem:
                        required: true
                        description: "ID of tuning system (see /availability for options)"
                      startingNote:
                        options: ["ushayran", "yegah"]
                        default: "ushayran"
                        description: "Theoretical framework for note naming"
                      pitchClassDataType:
                        options: ["all", "frequency", "cents", "fraction", "decimalRatio", "stringLength", "abjadName", "fretDivision", "midiNoteNumber", "midiNoteDeviation", "centsDeviation", "referenceNoteName", "englishName"]
                        default: "cents"
                        description: "Output format for pitch data. Use 'all' for complete pitch class information."
                      includeIntervals:
                        type: "boolean"
                        default: true
                        description: "Include interval data between pitch classes"
                      transposeTo:
                        options: ["rast", "nawa", "dugah"]
                        default: null
                        description: "Transpose to specific tonic (taṣwīr) - only valid transpositions shown"
                    notes:
                      formatOptions: "The 'pitchClassDataType' parameter controls which pitch class properties are returned. Use 'all' for comprehensive data or specific formats like 'cents', 'fraction', etc. for targeted data."
                    examples:
                      - "/api/ajnas/jins_rast?tuningSystem=ibnsina_1037&startingNote=yegah&pitchClassDataType=cents&includeIntervals=true"
                      - "/api/ajnas/jins_kurd?tuningSystem=alkindi_874&startingNote=ushayran&pitchClassDataType=cents&includeIntervals=true"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                conflictingParametersWithOptions:
                  summary: Conflicting parameters with options=true
                  value:
                    error: "Conflicting parameters with options=true"
                    message: "The following data-returning parameters cannot be used with options=true (parameter discovery mode): includeIntervals"
                    hint: "Remove all data-returning parameters to use discovery mode, or remove options=true to retrieve jins data."
                    conflictingParameters: ["includeIntervals"]
                    contextualParametersAllowed: ["tuningSystem", "startingNote"]
                missingTuningSystem:
                  summary: Missing required tuningSystem parameter
                  value:
                    error: "tuningSystem parameter is required"
                    hint: "Use /api/ajnas/{idName}/availability to see available tuning systems"
                    availabilityUrl: "/api/ajnas/{idName}/availability"
                missingStartingNote:
                  summary: Missing required startingNote parameter
                  value:
                    error: "startingNote parameter is required"
                    message: "A tuning system starting note must be specified. This is mandatory for all pitch class calculations."
                    validOptions: ["ʿushayrān", "yegāh"]
                    hint: "Add &startingNote=ʿushayrān to your request"
                invalidStartingNote:
                  summary: Invalid startingNote value
                  value:
                    error: "Invalid startingNote 'invalid_note'"
                    validOptions: ["ʿushayrān", "yegāh"]
                    hint: "Valid starting notes for this tuning system: ʿushayrān, yegāh"
        '404':
          description: Jins not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Jins 'jins_invalid' not found"
                hint: "Use /api/ajnas to see all available ajnās"
        '422':
          description: Jins cannot be realized in the specified tuning system
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Jins 'jins_rast' cannot be realized in tuning system 'alkindi_874' with starting note 'ushayran'"
                hint: "Use /api/ajnas/{idName}/availability to see compatible tuning systems"
        '500':
          $ref: '#/components/responses/ServerError'

  /ajnas/{idName}/availability:
    get:
      tags:
        - Ajnās
      summary: Check jins availability across tuning systems
      description: |
        Return tuning-system availability for a jins.
        
        - Shows which tuning systems on which starting note names can realize the specified jins
        - If `transpositionNoteName` is provided, filters results to show only tuning systems where the jins can be transposed to that specific note
        - Validity is computed against each tuning system's note name sets and the jins's pitch class intervals
        - Availability checking spans 3 octaves to correctly handle ajnās that may span multiple octaves
      operationId: getJinsAvailability
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe jins identifier
          schema:
            type: string
          example: "jins_rast"
        - name: transpositionNoteName
          in: query
          required: false
          description: Filter results to show only tuning systems where the jins can be transposed to this specific note (URL-safe, diacritics-insensitive)
          schema:
            type: string
          example: "nawa"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Availability information retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - jins
                  - availability
                properties:
                  jins:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Numeric identifier for the jins
                      idName:
                        type: string
                        description: URL-safe identifier for the jins (e.g., "jins_kurd")
                      displayName:
                        type: string
                        description: Display name with diacritics (e.g., "Jins Kurd")
                      transpositionNoteName:
                        type: string
                        description: The transposition note name filter applied (present only when transpositionNoteName parameter is provided)
                        example: "nawa"
                      version:
                        type: string
                        description: ISO 8601 timestamp of last modification
                  availability:
                    type: object
                    properties:
                      count:
                        type: integer
                        description: Number of tuning systems supporting this jins (or supporting the specified transposition if transpositionNoteName is provided)
                      tuningSystems:
                        type: array
                        description: Array of tuning systems where this jins is available (filtered by transposition if transpositionNoteName is provided)
                        items:
                          $ref: '#/components/schemas/TuningSystemAvailability'
              examples:
                withoutTransposition:
                  summary: Without transposition filter
                  value:
                    jins:
                      id: "5"
                      idName: "jins_kurd"
                      displayName: "Jins Kurd"
                      version: "2025-10-18T19:41:17.132Z"
                    availability:
                      count: 29
                      tuningSystems:
                        - tuningSystemId: "ibnsina_1037"
                          tuningSystemDisplayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
                        - tuningSystemId: "alfarabi_950g"
                          tuningSystemDisplayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave)"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
                withTransposition:
                  summary: With transposition filter (transpositionNoteName=nawa)
                  value:
                    jins:
                      id: "5"
                      idName: "jins_kurd"
                      displayName: "Jins Kurd"
                      transpositionNoteName: "nawa"
                      version: "2025-10-18T19:41:17.132Z"
                    availability:
                      count: 15
                      tuningSystems:
                        - tuningSystemId: "ibnsina_1037"
                          tuningSystemDisplayName: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
                          tuningSystemStartingNoteNames: ["ʿushayrān"]
                          tuningSystemStartingNoteNamesIds: ["ushayran"]
                        - tuningSystemId: "alfarabi_950g"
                          tuningSystemDisplayName: "al-Fārābī (950g) First Oud Tuning (Full First Octave)"
                          tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
                          tuningSystemStartingNoteNamesIds: ["ushayran", "yegah"]
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                emptyTranspositionNoteName:
                  summary: Empty transpositionNoteName parameter
                  value:
                    error: "Invalid parameter: transpositionNoteName"
                    message: "The 'transpositionNoteName' parameter cannot be empty."
                    hint: "Provide a valid note name like ?transpositionNoteName=nawa or remove the parameter"
        '404':
          description: Jins not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Jins 'jins_invalid' not found"
                hint: "Use /api/ajnas to see all available ajnās"
        '500':
          $ref: '#/components/responses/ServerError'

  /ajnas/{idName}/transpositions:
    get:
      tags:
        - Ajnās
      summary: List transpositions for a jins
      description: |
        Lists distinct transpositions (taswīr) for a jins within a specific tuning system and starting note.
        The analytical form (tahlīl) is excluded. Octave/register variants of the same modal degree
        (e.g. qarār dūgāh vs dūgāh vs muḥayyar) are not treated as distinct taswīr.

        If there are no distinct taswīr for this tuning-system + starting-note combination, this endpoint returns `422`
        with an `{ error, hint }` payload (it does not return a 200 with an empty list).
      operationId: listJinsTranspositions
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe jins identifier
          schema:
            type: string
          example: "jins_bayyat"
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          schema:
            type: string
          example: "meshshaqa_1899"
        - name: startingNote
          in: query
          required: true
          description: Tuning system starting note name (URL-safe, diacritics-insensitive). Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "yegah"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Transpositions retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  jins:
                    $ref: '#/components/schemas/EntityReference'
                  tuningSystem:
                    $ref: '#/components/schemas/EntityReference'
                  startingNote:
                    $ref: '#/components/schemas/IdentifierReference'
                  transpositions:
                    type: object
                    properties:
                      total:
                        type: integer
                        description: Count of distinct taswīr options (no tahlīl; no register variants)
                      options:
                        type: object
                        description: URL-safe tonic identifiers and display names
                        properties:
                          idNames:
                            type: array
                            items: { type: string }
                          displayNames:
                            type: array
                            items: { type: string }
                      detailed:
                        type: array
                        description: Detailed transposition options
                        items:
                          type: object
                          properties:
                            tonic:
                              $ref: '#/components/schemas/IdentifierReference'
                            pitchClass:
                              type: object
                              properties:
                                pitchClassIndex: { type: integer }
                                octave: { type: integer }
                            links:
                              type: object
                              properties:
                                detail: { type: string }
                  links:
                    type: object
                    properties:
                      self:
                        type: string
                        description: Link to this endpoint
                      availability:
                        type: string
                        description: Link to availability endpoint
                      jins:
                        type: string
                        description: Link to jins detail endpoint
              example:
                {
                  "jins": {
                    "id": "2",
                    "idName": "jins_bayyat",
                    "displayName": "jins bayyāt",
                    "version": "2025-10-18T19:35:30.479Z"
                  },
                  "tuningSystem": {
                    "id": "ibnsina_1037",
                    "idName": "ibnsina_1037",
                    "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                    "version": "2025-11-09T00:44:40.275Z",
                    "numberOfPitchClassesSingleOctave": 17
                  },
                  "startingNote": {
                    "idName": "yegah",
                    "displayName": "yegāh"
                  },
                  "transpositions": {
                    "total": 12,
                    "options": {
                      "idNames": [
                        "qarar_yegah",
                        "qarar_ushayran",
                        "qarar_rast",
                        "yegah",
                        "ushayran",
                        {
                          "…": "truncated — 7 more items"
                        }
                      ],
                      "displayNames": [
                        "qarār yegāh",
                        "qarār ʿushayrān",
                        "qarār rāst",
                        "yegāh",
                        "ʿushayrān",
                        {
                          "…": "truncated — 7 more items"
                        }
                      ]
                    },
                    "detailed": [
                      {
                        "tonic": {
                          "idName": "qarar_yegah",
                          "displayName": "qarār yegāh"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 0,
                          "octave": 0
                        },
                        "links": {
                          "detail": "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=qarar_yegah"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "qarar_ushayran",
                          "displayName": "qarār ʿushayrān"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 3,
                          "octave": 0
                        },
                        "links": {
                          "detail": "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=qarar_ushayran"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "qarar_rast",
                          "displayName": "qarār rāst"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 7,
                          "octave": 0
                        },
                        "links": {
                          "detail": "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=qarar_rast"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "yegah",
                          "displayName": "yegāh"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 0,
                          "octave": 1
                        },
                        "links": {
                          "detail": "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=yegah"
                        }
                      },
                      {
                        "tonic": {
                          "idName": "ushayran",
                          "displayName": "ʿushayrān"
                        },
                        "pitchClass": {
                          "pitchClassIndex": 3,
                          "octave": 1
                        },
                        "links": {
                          "detail": "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yegah&transposeTo=ushayran"
                        }
                      },
                      {
                        "…": "truncated — 7 more items"
                      }
                    ]
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/ajnas/jins_bayyat/transpositions?tuningSystem=ibnsina_1037&startingNote=yegah",
                    "availability": "/api/ajnas/jins_bayyat/availability",
                    "detail": "/api/ajnas/jins_bayyat"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingTuningSystem:
                  summary: Missing tuningSystem parameter
                  value:
                    error: "tuningSystem parameter is required"
                    hint: "Add ?tuningSystem=ibnsina_1037 to your request. Use /api/ajnas/{id}/availability to see available tuning systems"
                missingStartingNote:
                  summary: Missing startingNote parameter
                  value:
                    error: "startingNote parameter is required"
                    hint: "Add &startingNote=yegah to your request. Use /api/ajnas/{id}/availability to see available starting notes"
                invalidStartingNote:
                  summary: Invalid starting note for tuning system
                  value:
                    error: "Starting note 'invalid' is not valid for tuning system 'ibnsina_1037'"
                    validOptions: ["ʿushayrān", "yegāh"]
        '422':
          description: Jins cannot be realized with this tuning system and starting note, or no distinct taswīr options exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                jinsNotRealizable:
                  summary: Jins cannot be realized in this context
                  value:
                    error: "Jins 'jins kurd' cannot be realized in tuning system 'ibnsina_1037' with starting note 'yegāh'"
                    hint: "Use /api/ajnas/jins_kurd/availability to see compatible tuning systems"
        '404':
          description: Jins or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                jinsNotFound:
                  summary: Jins not found
                  value:
                    error: "Jins 'jins_invalid' not found"
                    hint: "Use /api/ajnas to see all available ajnās"
                tuningSystemNotFound:
                  summary: Tuning system not found
                  value:
                    error: "Tuning system 'invalid' not found"
                    hint: "Use /api/tuning-systems to see all available tuning systems"
        '500':
          $ref: '#/components/responses/ServerError'

  /ajnas/{idName}/compare:
    get:
      tags:
        - Ajnās
      summary: Compare jins data across multiple tuning systems and starting notes
      description: |
        Compare comprehensive jins data across multiple tuning systems **and** multiple starting
        notes in a single request. Given `N` tuning systems and `M` starting notes, the response
        contains the Cartesian product of `N × M` comparison cells in user-supplied order
        (outer loop = `tuningSystems`, inner loop = `startingNotes`).

        Each successful cell includes the (unidirectional) pitch class sequence, optional
        interval data, and transposition details. When a requested starting note is not available
        in a given tuning system the cell carries an informational `note` field (not `error`)
        together with `validStartingNotes`. When a hard failure occurs (tuning system not found,
        jins not realizable in this configuration, or an unreachable `transposeTo` target) the
        cell carries an `error` field. The overall response is always HTTP 200 — per-cell
        outcomes do not affect the HTTP status.

        The following invariant always holds:

        ```
        successfulComparisons + unavailableStartingNotes + failedComparisons === totalComparisons
        ```

        This endpoint is ideal for comparative musicological analysis of melodic structures
        across historical tuning systems and/or multiple modal transpositions.
      operationId: compareJins
      parameters:
        - name: idName
          in: path
          required: true
          description: URL-safe jins identifier
          schema:
            type: string
          example: "jins_bayyat"
        - name: tuningSystems
          in: query
          required: true
          description: |
            Comma-separated list of tuning system IDs. The response computes the Cartesian
            product of these tuning systems with the requested `startingNotes` (outer loop =
            `tuningSystems`, inner loop = `startingNotes`).
          schema:
            type: string
          example: "alsabbagh_1954,ibnsina_1037"
        - name: startingNotes
          in: query
          required: true
          description: |
            Comma-separated list of starting note names (URL-safe, diacritics-insensitive). The
            response contains `N × M` cells for `N` tuning systems × `M` starting notes, in
            user-supplied order. If a starting note is not available in a given tuning system
            that cell carries an informational `note` field (with a `validStartingNotes`
            namespace) rather than pitch data.
          schema:
            type: string
          example: "rast,yegah,ushayran"
        - name: pitchClassDataType
          in: query
          required: false
          description: Output format for pitch class data. Defaults to `all` when omitted.
          schema:
            type: string
            enum:
              - all
              - englishName
              - fraction
              - cents
              - decimalRatio
              - stringLength
              - frequency
              - abjadName
              - fretDivision
              - midiNoteNumber
              - midiNoteDeviation
              - centsDeviation
              - referenceNoteName
            default: "all"
          example: "cents"
        - name: includeIntervals
          in: query
          required: false
          description: |
            Whether to include interval data between adjacent pitch classes on each cell.
            Defaults to `"false"` when omitted.
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
        - name: transposeTo
          in: query
          required: false
          description: |
            Transpose every successful cell to the given tonic note (applies to every cell in the
            Cartesian product). If the requested target is not reachable in a given cell the cell
            carries an `error` plus an `availableTranspositions` list.
          schema:
            type: string
          example: "nawa"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with `Ar` suffix (e.g., `displayNameAr`, `noteNameDisplayAr`)
            - Note names and jins names get Arabic versions in `*Ar` fields
            - Tuning system display names get Arabic versions in `displayNameAr` if available
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: |
            Comparison data retrieved successfully. Note that the response is always HTTP 200
            regardless of per-cell outcomes; individual failures (missing starting note, tuning
            system not found, jins not realizable, unreachable transposeTo) are reported on the
            respective cell via `note` or `error` fields. The counters
            `successfulComparisons + unavailableStartingNotes + failedComparisons` always equal
            `totalComparisons`.
          content:
            application/json:
              schema:
                type: object
                required:
                  - jins
                  - comparisons
                  - parameters
                  - links
                properties:
                  jins:
                    type: object
                    description: Canonical jins identifier namespace (the jins being compared)
                    required:
                      - id
                      - idName
                      - displayName
                    properties:
                      id:
                        type: string
                        description: Numeric identifier for the jins
                      idName:
                        type: string
                        description: URL-safe identifier for the jins
                      displayName:
                        type: string
                        description: Display name with diacritics
                      displayNameAr:
                        type: string
                        description: Arabic-script display name (present only when `includeArabic=true`)
                      version:
                        type: string
                        description: ISO 8601 timestamp of last modification
                  comparisons:
                    type: object
                    description: |
                      List response object containing every comparison cell in the Cartesian
                      product. Meta counters are hoisted to the top level of this object (siblings
                      of `count` and `data`), not nested under a separate `meta` key.
                    required:
                      - count
                      - totalComparisons
                      - successfulComparisons
                      - unavailableStartingNotes
                      - failedComparisons
                      - data
                    properties:
                      count:
                        type: integer
                        description: Number of items in `data` (equals `totalComparisons`)
                      totalComparisons:
                        type: integer
                        description: Total number of comparison cells in the Cartesian product (`N × M`)
                      successfulComparisons:
                        type: integer
                        description: Number of cells that returned full pitch/interval data
                      unavailableStartingNotes:
                        type: integer
                        description: |
                          Number of cells whose `startingNote` was not available in the cell's
                          `tuningSystem`. These cells carry a `note` field (informational — NOT
                          an error) and are distinct from hard failures.
                      failedComparisons:
                        type: integer
                        description: |
                          Number of cells that hit a hard failure (tuning system not found,
                          jins not realizable, or unreachable `transposeTo`). These cells carry
                          an `error` field.
                      data:
                        type: array
                        description: |
                          Array of comparison cells in user-supplied order (outer loop =
                          `tuningSystems`, inner loop = `startingNotes`). Each cell has one of
                          three mutually exclusive shapes — a successful result, an informational
                          `note` (unavailable starting note), or an `error` (hard failure). All
                          non-common fields below are optional and present only for the
                          corresponding outcome.
                        items:
                          type: object
                          required:
                            - tuningSystem
                            - startingNote
                          properties:
                            tuningSystem:
                              type: object
                              description: |
                                Tuning system entity namespace for this cell. For the
                                "tuning system not found" failure this is a minimal fallback
                                namespace built from the raw ID (no `version`, no `displayNameAr`).
                                In every other outcome this is the full entity namespace.
                              properties:
                                id:
                                  type: string
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                version:
                                  type: string
                                displayNameAr:
                                  type: string
                                  description: Present only when `includeArabic=true`
                            startingNote:
                              type: object
                              description: Identifier namespace for the starting note of this cell
                              properties:
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                displayNameAr:
                                  type: string
                                  description: Present only when `includeArabic=true`
                            jins:
                              type: object
                              description: |
                                Per-cell jins entity namespace. **Present on successful cells
                                only.** When the cell represents a transposition, `idName` and
                                `displayName` reflect the transposed name (e.g. `jins_bayyat_al-nawa`);
                                the canonical `id` is preserved.
                              properties:
                                id:
                                  type: string
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                version:
                                  type: string
                                displayNameAr:
                                  type: string
                            tonic:
                              type: object
                              description: |
                                Identifier namespace for the resolved tonic note. **Present on
                                successful cells only.**
                              properties:
                                idName:
                                  type: string
                                displayName:
                                  type: string
                                displayNameAr:
                                  type: string
                            transposition:
                              type: object
                              description: |
                                Transposition details. **Present only when the tahlil for this
                                cell is a taswīr (tonic differs from the canonical tonic).** Omitted
                                entirely when the cell resolves to the canonical-octave tahlil.
                              properties:
                                original:
                                  type: object
                                  description: Identifier namespace for the canonical tonic
                                  properties:
                                    idName:
                                      type: string
                                    displayName:
                                      type: string
                                    displayNameAr:
                                      type: string
                                resolved:
                                  type: object
                                  description: Identifier namespace for the transposed tonic (same as top-level `tonic`)
                                  properties:
                                    idName:
                                      type: string
                                    displayName:
                                      type: string
                                    displayNameAr:
                                      type: string
                            stats:
                              type: object
                              description: Per-cell statistics. **Present on successful cells only.**
                              properties:
                                tuningSystem:
                                  type: object
                                  properties:
                                    numberOfPitchClassesSingleOctave:
                                      type: integer
                                      description: Number of pitch classes per octave in this tuning system
                                    referenceFrequency:
                                      type: number
                                      description: Reference frequency in Hz for the cell's starting note
                                jins:
                                  type: object
                                  properties:
                                    numberOfPitchClasses:
                                      type: integer
                                      description: Number of pitch classes in this cell's tahlil
                            availableTranspositions:
                              type: array
                              description: |
                                Taswīr-only available transpositions for this cell. **Present on
                                successful cells and on `transposeTo`-unreachable error cells**
                                (in the latter case, this lists the alternatives the caller could
                                try). May be an empty array.
                              items:
                                type: object
                                properties:
                                  idName:
                                    type: string
                                  displayName:
                                    type: string
                                  displayNameAr:
                                    type: string
                                  tonic:
                                    type: object
                                    properties:
                                      idName:
                                        type: string
                                      displayName:
                                        type: string
                                      displayNameAr:
                                        type: string
                            pitchData:
                              type: array
                              description: |
                                Unidirectional pitch class sequence (ajnās have a single
                                direction). **Present on successful cells only.** The field set
                                of each item depends on `pitchClassDataType`.
                              items:
                                type: object
                            intervals:
                              type: array
                              description: |
                                Interval data between adjacent pitch classes (single array —
                                unidirectional). **Present on successful cells only and only
                                when `includeIntervals=true`.**
                              items:
                                type: object
                            parameters:
                              type: object
                              description: Per-cell echo of the effective parameters. **Present on successful cells only.**
                              properties:
                                pitchClassDataType:
                                  type: string
                                includeIntervals:
                                  type: boolean
                                transposeTo:
                                  type: string
                                  nullable: true
                            links:
                              type: object
                              description: Per-cell links namespace. **Present on successful cells only.**
                              properties:
                                detail:
                                  type: string
                                availability:
                                  type: string
                            note:
                              type: string
                              description: |
                                Informational message for cells whose `startingNote` is not
                                available in the cell's `tuningSystem`. **Present only for
                                unavailable-starting-note cells** (counted by
                                `unavailableStartingNotes`). Mutually exclusive with `error`.
                            validStartingNotes:
                              type: object
                              description: |
                                Valid starting notes for the cell's tuning system. Present
                                alongside `note`. A string-array namespace.
                              properties:
                                idNames:
                                  type: array
                                  items:
                                    type: string
                                displayNames:
                                  type: array
                                  items:
                                    type: string
                                displayNamesAr:
                                  type: array
                                  items:
                                    type: string
                                  description: Present only when `includeArabic=true`
                            error:
                              type: string
                              description: |
                                Error message for cells that hit a hard failure (tuning system
                                not found, jins not realizable, or unreachable `transposeTo`).
                                **Present only for failed cells** (counted by `failedComparisons`).
                                Mutually exclusive with `note`.
                            transposeTo:
                              type: string
                              description: |
                                Present only on the `transposeTo`-unreachable error variant:
                                echoes the unreachable target note name.
                  parameters:
                    type: object
                    description: Echo of the effective request parameters at the top level
                    properties:
                      tuningSystems:
                        type: object
                        description: String-array namespace of requested tuning systems
                        properties:
                          idNames:
                            type: array
                            items:
                              type: string
                          displayNames:
                            type: array
                            items:
                              type: string
                          displayNamesAr:
                            type: array
                            items:
                              type: string
                            description: Present only when `includeArabic=true`
                      startingNotes:
                        type: object
                        description: String-array namespace of requested starting notes
                        properties:
                          idNames:
                            type: array
                            items:
                              type: string
                          displayNames:
                            type: array
                            items:
                              type: string
                          displayNamesAr:
                            type: array
                            items:
                              type: string
                            description: Present only when `includeArabic=true`
                      pitchClassDataType:
                        type: string
                      includeIntervals:
                        type: boolean
                      transposeTo:
                        type: string
                        nullable: true
                  links:
                    type: object
                    description: Top-level links namespace
                    properties:
                      self:
                        type: string
                        description: Canonical URL for this comparison request
                      detail:
                        type: string
                        description: Link to the jins detail endpoint
                      availability:
                        type: string
                        description: Link to the jins availability endpoint
              examples:
                twoTuningSystems:
                  summary: Compare a single tuning system × single starting note cell
                  value:
                    jins:
                      id: "19"
                      idName: "jins_rast"
                      displayName: "jins rāst"
                      version: "2025-10-18T19:34:26.343Z"
                    comparisons:
                      count: 1
                      totalComparisons: 1
                      successfulComparisons: 1
                      unavailableStartingNotes: 0
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          jins:
                            id: "19"
                            idName: "jins_rast"
                            displayName: "jins rāst"
                            version: "2025-10-18T19:34:26.343Z"
                          tonic:
                            idName: "rast"
                            displayName: "rāst"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            jins:
                              numberOfPitchClasses: 4
                          availableTranspositions: []
                          pitchData:
                            - pitchClassIndex: 0
                              octave: 1
                              scaleDegree: "I"
                              noteName: "rast"
                              noteNameDisplay: "rāst"
                              cents: 0
                            - pitchClassIndex: 4
                              octave: 1
                              scaleDegree: "II"
                              noteName: "dugah"
                              noteNameDisplay: "dūgāh"
                              cents: 203.91000173077484
                            - pitchClassIndex: 7
                              octave: 1
                              scaleDegree: "III"
                              noteName: "segah"
                              noteNameDisplay: "segāh"
                              cents: 356.8630927494849
                            - pitchClassIndex: 10
                              octave: 1
                              scaleDegree: "IV"
                              noteName: "chahargah"
                              noteNameDisplay: "chahārgāh"
                              cents: 498.0449991346125
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/ajnas/jins_rast?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/ajnas/jins_rast/availability"
                          intervals:
                            - cents: 203.91000173077484
                            - cents: 152.95309101871007
                            - cents: 141.1819063851276
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954"]
                        displayNames: ["alsabbagh_1954"]
                      startingNotes:
                        idNames: ["rast"]
                        displayNames: ["rast"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/ajnas/jins_rast/compare?tuningSystems=alsabbagh_1954&startingNotes=rast&pitchClassDataType=cents"
                      detail: "/api/ajnas/jins_rast"
                      availability: "/api/ajnas/jins_rast/availability"
                multipleStartingNotes:
                  summary: Cartesian product — 2 tuning systems × 3 starting notes
                  description: |
                    Requests `jins_bayyat` against 2 tuning systems × 3 starting notes, yielding
                    6 cells in user-supplied order (outer = `tuningSystems`, inner =
                    `startingNotes`). Three cells are successful; three cells carry a `note`
                    because the requested starting note isn't available in that tuning system.
                    `pitchData` is trimmed for brevity.
                  value:
                    jins:
                      id: "2"
                      idName: "jins_bayyat"
                      displayName: "jins bayyāt"
                      version: "2025-10-18T19:35:30.479Z"
                    comparisons:
                      count: 6
                      totalComparisons: 6
                      successfulComparisons: 3
                      unavailableStartingNotes: 3
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          jins:
                            id: "2"
                            idName: "jins_bayyat"
                            displayName: "jins bayyāt"
                            version: "2025-10-18T19:35:30.479Z"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            jins:
                              numberOfPitchClasses: 4
                          availableTranspositions: []
                          pitchData:
                            - pitchClassIndex: 4
                              octave: 1
                              scaleDegree: "I"
                              noteName: "dugah"
                              noteNameDisplay: "dūgāh"
                              cents: 203.91000173077484
                            - pitchClassIndex: 7
                              octave: 1
                              scaleDegree: "II"
                              noteName: "segah"
                              noteNameDisplay: "segāh"
                              cents: 356.8630927494849
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/ajnas/jins_bayyat?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/ajnas/jins_bayyat/availability"
                          intervals:
                            - cents: 152.95309101871007
                            - cents: 141.1819063851276
                            - cents: 203.91000173077492
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "yegah"
                            displayName: "yegah"
                          note: "Starting note 'yegah' is not available in tuning system 'al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone'"
                          validStartingNotes:
                            idNames: ["rast"]
                            displayNames: ["rāst"]
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "ushayran"
                            displayName: "ushayran"
                          note: "Starting note 'ushayran' is not available in tuning system 'al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone'"
                          validStartingNotes:
                            idNames: ["rast"]
                            displayNames: ["rāst"]
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rast"
                          note: "Starting note 'rast' is not available in tuning system 'Ibn Sīnā  (1037) 7-Fret Oud 17-Tone'"
                          validStartingNotes:
                            idNames: ["ushayran", "yegah"]
                            displayNames: ["ʿushayrān", "yegāh"]
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "yegah"
                            displayName: "yegāh"
                          jins:
                            id: "2"
                            idName: "jins_bayyat"
                            displayName: "jins bayyāt"
                            version: "2025-10-18T19:35:30.479Z"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 17
                              referenceFrequency: 97.999
                            jins:
                              numberOfPitchClasses: 4
                          availableTranspositions:
                            - idName: "jins_bayyat_al-yegah"
                              displayName: "jins bayyāt al-yegāh"
                              tonic:
                                idName: "yegah"
                                displayName: "yegāh"
                            - idName: "jins_bayyat_al-nawa"
                              displayName: "jins bayyāt al-nawā"
                              tonic:
                                idName: "nawa"
                                displayName: "nawā"
                          pitchData:
                            - pitchClassIndex: 10
                              octave: 1
                              scaleDegree: "I"
                              noteName: "dugah"
                              noteNameDisplay: "dūgāh"
                              cents: 701.9550008653874
                            - pitchClassIndex: 12
                              octave: 1
                              scaleDegree: "II"
                              noteName: "segah"
                              noteNameDisplay: "segāh"
                              cents: 840.5276617693106
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=yeg%C4%81h&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/ajnas/jins_bayyat/availability"
                          intervals:
                            - cents: 138.57266090392318
                            - cents: 155.5623364999144
                            - cents: 203.91000173077498
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "ushayran"
                            displayName: "ʿushayrān"
                          jins:
                            id: "2"
                            idName: "jins_bayyat"
                            displayName: "jins bayyāt"
                            version: "2025-10-18T19:35:30.479Z"
                          tonic:
                            idName: "dugah"
                            displayName: "dūgāh"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 17
                              referenceFrequency: 97.999
                            jins:
                              numberOfPitchClasses: 4
                          availableTranspositions:
                            - idName: "jins_bayyat_al-ushayran"
                              displayName: "jins bayyāt al-ʿushayrān"
                              tonic:
                                idName: "ushayran"
                                displayName: "ʿushayrān"
                            - idName: "jins_bayyat_al-nawa"
                              displayName: "jins bayyāt al-nawā"
                              tonic:
                                idName: "nawa"
                                displayName: "nawā"
                          pitchData:
                            - pitchClassIndex: 5
                              octave: 1
                              scaleDegree: "I"
                              noteName: "dugah"
                              noteNameDisplay: "dūgāh"
                              cents: 498.0449991346125
                            - pitchClassIndex: 7
                              octave: 1
                              scaleDegree: "II"
                              noteName: "segah"
                              noteNameDisplay: "segāh"
                              cents: 636.6176600385357
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/ajnas/jins_bayyat?tuningSystem=ibnsina_1037&startingNote=%CA%BFushayr%C4%81n&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/ajnas/jins_bayyat/availability"
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954", "ibnsina_1037"]
                        displayNames: ["alsabbagh_1954", "ibnsina_1037"]
                      startingNotes:
                        idNames: ["rast", "yegah", "ushayran"]
                        displayNames: ["rast", "yegah", "ushayran"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/ajnas/jins_bayyat/compare?tuningSystems=alsabbagh_1954,ibnsina_1037&startingNotes=rast,yegah,ushayran&pitchClassDataType=cents"
                      detail: "/api/ajnas/jins_bayyat"
                      availability: "/api/ajnas/jins_bayyat/availability"
                unavailableStartingNote:
                  summary: Single cell whose starting note isn't available (`note` field)
                  description: |
                    Demonstrates the `note` (informational, not error) per-cell shape when the
                    requested starting note is not available in the given tuning system. The
                    response is HTTP 200 and `unavailableStartingNotes` is incremented.
                  value:
                    jins:
                      id: "19"
                      idName: "jins_rast"
                      displayName: "jins rāst"
                      version: "2025-10-18T19:34:26.343Z"
                    comparisons:
                      count: 1
                      totalComparisons: 1
                      successfulComparisons: 0
                      unavailableStartingNotes: 1
                      failedComparisons: 0
                      data:
                        - tuningSystem:
                            id: "ibnsina_1037"
                            idName: "ibnsina_1037"
                            displayName: "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone"
                            version: "2025-11-09T00:44:40.275Z"
                          startingNote:
                            idName: "bogus_note"
                            displayName: "bogus_note"
                          note: "Starting note 'bogus_note' is not available in tuning system 'Ibn Sīnā  (1037) 7-Fret Oud 17-Tone'"
                          validStartingNotes:
                            idNames: ["ushayran", "yegah"]
                            displayNames: ["ʿushayrān", "yegāh"]
                    parameters:
                      tuningSystems:
                        idNames: ["ibnsina_1037"]
                        displayNames: ["ibnsina_1037"]
                      startingNotes:
                        idNames: ["bogus_note"]
                        displayNames: ["bogus_note"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/ajnas/jins_rast/compare?tuningSystems=ibnsina_1037&startingNotes=bogus_note&pitchClassDataType=cents"
                      detail: "/api/ajnas/jins_rast"
                      availability: "/api/ajnas/jins_rast/availability"
                withErrors:
                  summary: Mixed success and hard-failure cells
                  description: |
                    Demonstrates a cell with `error` (hard failure — tuning system not found)
                    alongside a successful cell. Hard failures use a minimal fallback
                    `tuningSystem` namespace built from the raw request ID.
                  value:
                    jins:
                      id: "19"
                      idName: "jins_rast"
                      displayName: "jins rāst"
                      version: "2025-10-18T19:34:26.343Z"
                    comparisons:
                      count: 2
                      totalComparisons: 2
                      successfulComparisons: 1
                      unavailableStartingNotes: 0
                      failedComparisons: 1
                      data:
                        - tuningSystem:
                            id: "alsabbagh_1954"
                            idName: "alsabbagh_1954"
                            displayName: "al-Ṣabbāgh (1954) Contemporary Arabic Tuning 24-Tone"
                            version: "2025-10-18T19:42:23.643Z"
                          startingNote:
                            idName: "rast"
                            displayName: "rāst"
                          jins:
                            id: "19"
                            idName: "jins_rast"
                            displayName: "jins rāst"
                            version: "2025-10-18T19:34:26.343Z"
                          tonic:
                            idName: "rast"
                            displayName: "rāst"
                          stats:
                            tuningSystem:
                              numberOfPitchClassesSingleOctave: 24
                              referenceFrequency: 130.665
                            jins:
                              numberOfPitchClasses: 4
                          availableTranspositions: []
                          pitchData:
                            - pitchClassIndex: 0
                              octave: 1
                              scaleDegree: "I"
                              noteName: "rast"
                              noteNameDisplay: "rāst"
                              cents: 0
                          parameters:
                            pitchClassDataType: "cents"
                            includeIntervals: true
                            transposeTo: null
                          links:
                            detail: "/api/ajnas/jins_rast?tuningSystem=alsabbagh_1954&startingNote=r%C4%81st&pitchClassDataType=cents&includeIntervals=true"
                            availability: "/api/ajnas/jins_rast/availability"
                        - tuningSystem:
                            id: "InvalidSystem"
                            idName: "invalidsystem"
                            displayName: "InvalidSystem"
                          startingNote:
                            idName: "rast"
                            displayName: "rast"
                          error: "Tuning system 'InvalidSystem' not found"
                    parameters:
                      tuningSystems:
                        idNames: ["alsabbagh_1954", "invalidsystem"]
                        displayNames: ["alsabbagh_1954", "InvalidSystem"]
                      startingNotes:
                        idNames: ["rast"]
                        displayNames: ["rast"]
                      pitchClassDataType: "cents"
                      includeIntervals: true
                      transposeTo: null
                    links:
                      self: "https://diarmaqar.netlify.app/api/ajnas/jins_rast/compare?tuningSystems=alsabbagh_1954,InvalidSystem&startingNotes=rast&pitchClassDataType=cents"
                      detail: "/api/ajnas/jins_rast"
                      availability: "/api/ajnas/jins_rast/availability"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingTuningSystems:
                  summary: Missing tuningSystems parameter
                  value:
                    error: "tuningSystems parameter is required"
                    message: "Provide comma-separated tuning system IDs"
                    hint: 'Format: ?tuningSystems=ibnsina_1037,alfarabi_950g'
                    example: "/api/ajnas/jins_kurd/compare?tuningSystems=ibnsina_1037,alfarabi_950g&startingNotes=yegah&pitchClassDataType=cents"
                missingStartingNotes:
                  summary: Missing startingNotes parameter
                  value:
                    error: "startingNotes parameter is required"
                    message: "At least one starting note must be specified"
                    hint: "Add &startingNotes=yegah,rast to your request (comma-separated list)"
                emptyStartingNotes:
                  summary: startingNotes parameter is empty or whitespace-only
                  value:
                    error: "Invalid parameter: startingNotes"
                    message: "The 'startingNotes' parameter must contain at least one starting note name."
                    hint: "Use ?startingNotes=yegah or ?startingNotes=yegah,rast,ushayran"
                invalidIntervals:
                  summary: Invalid includeIntervals parameter
                  value:
                    error: "Invalid parameter: includeIntervals"
                    message: "The 'includeIntervals' parameter must be 'true' or 'false'."
                    validOptions: ["true", "false"]
                    hint: "Use ?includeIntervals=true or ?includeIntervals=false"
        '404':
          description: Jins not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "Jins 'jins_invalid' not found"
                hint: "Use /api/ajnas to see all available ajnās"
        '500':
          $ref: '#/components/responses/ServerError'

  /pitch-classes/note-names:
    get:
      tags:
        - Pitch Classes
      summary: List all note names
      description: |
        Returns all note names used in the app, bilingually, sortable by note name order or alphabetically.
        
        Note names are sourced from all octave arrays in NoteName.ts (octaveZeroNoteNames, octaveOneNoteNames, 
        octaveTwoNoteNames, octaveThreeNoteNames, octaveFourNoteNames). Each note name includes:
        - Canonical order (index within its octave array)
        - Octave number (0-4)
        - English IPN equivalent
        
        Note names are unique per octave, so the same note name string in different octaves represents 
        different pitch classes.
        
        **Note**: Availability data is not included in this endpoint (it's expensive to calculate). 
        Use the availability endpoint (`/api/pitch-classes/note-names/{noteName}/availability`) to get 
        availability information for a specific note name.
      operationId: listNoteNames
      parameters:
        - name: sortBy
          in: query
          required: false
          description: Sort order for results
          schema:
            type: string
            enum: [order, alphabetical]
            default: order
          examples:
            order:
              value: order
              summary: Sort by canonical note name order (from NoteName.ts arrays)
            alphabetical:
              value: alphabetical
              summary: Sort alphabetically by display name
        - name: filterByTuningSystem
          in: query
          required: false
          description: Filter by tuning system ID to show only note names that exist in that system (URL-safe, case-insensitive, diacritics-insensitive)
          schema:
            type: string
          example: "meshshaqa_1899"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: List of note names retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  meta:
                    type: object
                    properties:
                      count:
                        type: integer
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        noteName:
                          $ref: '#/components/schemas/IdentifierRef'
                        englishName:
                          type: string
                          description: International Pitch Notation equivalent
                          example: "G3"
                        order:
                          type: integer
                          description: Canonical order (index within octave array)
                          example: 16
                        octave:
                          type: integer
                          description: Octave number (0-4)
                          example: 1
                        links:
                          $ref: '#/components/schemas/Links'
                          description: Links to detail and availability endpoints (availability data is only in the availability endpoint)
              example:
                {
                  "count": 183,
                  "data": [
                    {
                      "noteName": {
                        "idName": "qarar_yegah",
                        "displayName": "qarār yegāh"
                      },
                      "englishName": "G1",
                      "order": 0,
                      "octave": 0,
                      "links": {
                        "detail": "/api/pitch-classes/note-names/qarar_yegah",
                        "availability": "/api/pitch-classes/note-names/qarar_yegah/availability"
                      }
                    },
                    {
                      "noteName": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "englishName": "G2",
                      "order": 0,
                      "octave": 1,
                      "links": {
                        "detail": "/api/pitch-classes/note-names/yegah",
                        "availability": "/api/pitch-classes/note-names/yegah/availability"
                      }
                    },
                    {
                      "noteName": {
                        "idName": "nawa",
                        "displayName": "nawā"
                      },
                      "englishName": "G3",
                      "order": 0,
                      "octave": 2,
                      "links": {
                        "detail": "/api/pitch-classes/note-names/nawa",
                        "availability": "/api/pitch-classes/note-names/nawa/availability"
                      }
                    },
                    {
                      "noteName": {
                        "idName": "saham/ramal_tuti",
                        "displayName": "saham/ramal tūtī"
                      },
                      "englishName": "G4",
                      "order": 0,
                      "octave": 3,
                      "links": {
                        "detail": "/api/pitch-classes/note-names/saham/ramal_tuti",
                        "availability": "/api/pitch-classes/note-names/saham/ramal_tuti/availability"
                      }
                    },
                    {
                      "noteName": {
                        "idName": "qarar_qarar_nim_hisar",
                        "displayName": "qarār qarār nīm ḥiṣār"
                      },
                      "englishName": "Ab--1",
                      "order": 1,
                      "octave": 0,
                      "links": {
                        "detail": "/api/pitch-classes/note-names/qarar_qarar_nim_hisar",
                        "availability": "/api/pitch-classes/note-names/qarar_qarar_nim_hisar/availability"
                      }
                    },
                    {
                      "…": "truncated — 178 more items"
                    }
                  ]
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Tuning system not found (when filterByTuningSystem is used)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /pitch-classes/note-names/{noteName}:
    get:
      tags:
        - Pitch Classes
      summary: Get pitch class details by note name
      description: |
        Get pitch class details for a note name, across all tuning systems or a specific one.
        
        When querying by note name, the note name itself identifies the octave (note names are unique per octave).
        The octave parameter is optional and can be used for validation.
        
        Supports:
        - Single tuning system with specific starting note
        - Single tuning system with startingNote="all" (returns array for all starting notes)
        - All tuning systems (returns array for all tuning systems and starting notes)
      operationId: getPitchClassByNoteName
      parameters:
        - name: noteName
          in: path
          required: true
          description: URL-safe note name (e.g., "rast", "dugah", "nawa")
          schema:
            type: string
          example: "rast"
        - name: tuningSystem
          in: query
          required: false
          description: Tuning system ID. If provided, returns data for that tuning system only. If omitted, returns data for all tuning systems.
          schema:
            type: string
          example: "cairocongresstuningcommittee_1929"
        - name: startingNote
          in: query
          required: false
          description: |
            Starting note for the tuning system. Required if tuningSystem is provided.
            
            - Use a specific starting note name (URL-safe) for single result
            - Use "all" to include all available starting notes for that tuning system (returns array of results, one per starting note)
            
            Note: Since note names are unique per octave, the note name itself identifies the octave. No octave parameter is needed. Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "rast"
        - name: pitchClassDataType
          in: query
          required: false
          description: Pitch class data format (defaults to `all` when omitted, matching the handler)
          schema:
            type: string
            enum: [all, abjadName, englishName, solfege, fraction, cents, centsDeviation, decimalRatio, stringLength, fretDivision, midiNoteNumber, midiNoteDeviation, frequency, referenceNoteName]
            default: "all"
          example: "cents"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Pitch class data retrieved successfully
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: Single tuning system response
                    properties:
                      context:
                        type: object
                        properties:
                          tuningSystem:
                            $ref: '#/components/schemas/EntityRef'
                          startingNote:
                            $ref: '#/components/schemas/IdentifierRef'
                          noteName:
                            $ref: '#/components/schemas/IdentifierRef'
                          referenceFrequency:
                            type: number
                          pitchClassDataType:
                            type: string
                      pitchClass:
                        $ref: '#/components/schemas/PitchClass'
                      links:
                        $ref: '#/components/schemas/Links'
                  - type: object
                    description: Multiple tuning systems or starting notes response
                    properties:
                      context:
                        type: object
                        properties:
                          noteName:
                            $ref: '#/components/schemas/IdentifierRef'
                      pitchClasses:
                        type: array
                        description: Array of pitch class data, one per tuning system/starting note combination
                        items:
                          type: object
                          properties:
                            context:
                              type: object
                              properties:
                                tuningSystem:
                                  $ref: '#/components/schemas/EntityRef'
                                startingNote:
                                  $ref: '#/components/schemas/IdentifierRef'
                                referenceFrequency:
                                  type: number
                                pitchClassDataType:
                                  type: string
                            pitchClassData:
                              $ref: '#/components/schemas/PitchClass'
                      links:
                        $ref: '#/components/schemas/Links'
              example:
                {
                  "context": {
                    "tuningSystem": {
                      "id": "ibnsina_1037",
                      "idName": "ibnsina_1037",
                      "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                      "version": "2025-11-09T00:44:40.275Z"
                    },
                    "startingNote": {
                      "idName": "yegah",
                      "displayName": "yegāh"
                    },
                    "noteName": {
                      "idName": "yegah",
                      "displayName": "yegāh"
                    },
                    "referenceFrequency": 97.999,
                    "pitchClassDataType": "cents"
                  },
                  "pitchClass": {
                    "pitchClassIndex": 0,
                    "octave": 1,
                    "noteName": "yegah",
                    "noteNameDisplay": "yegāh",
                    "cents": 0
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/pitch-classes/note-names/yegah?tuningSystem=ibnsina_1037&startingNote=yegah&pitchClassDataType=cents",
                    "availability": "/api/pitch-classes/note-names/yegah/availability",
                    "compare": "/api/pitch-classes/note-names/yegah/compare"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Note name or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /pitch-classes/note-names/{noteName}/availability:
    get:
      tags:
        - Pitch Classes
      summary: Check note name availability across tuning systems
      description: |
        Check which tuning systems support a specific note name and what pitch class index it has in each tuning system.
        
        Returns for each tuning system:
        - All starting notes where the note name exists
        - The pitch class index for each starting note
        - The octave where the note name was found
      operationId: getNoteNameAvailability
      parameters:
        - name: noteName
          in: path
          required: true
          description: URL-safe note name (e.g., "rast", "dugah", "nawa")
          schema:
            type: string
          example: "rast"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Availability data retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  noteName:
                    $ref: '#/components/schemas/IdentifierRef'
                  availability:
                    type: object
                    properties:
                      meta:
                        type: object
                        properties:
                          count:
                            type: integer
                      data:
                        type: array
                        items:
                          type: object
                          properties:
                            tuningSystem:
                              $ref: '#/components/schemas/EntityRef'
                            startingNotes:
                              type: array
                              items:
                                type: object
                                properties:
                                  startingNote:
                                    $ref: '#/components/schemas/IdentifierRef'
                                  pitchClassIndex:
                                    type: integer
                                    description: Pitch class index within this tuning system
                                  octave:
                                    type: integer
                                    description: Octave where the note name was found
                  links:
                    $ref: '#/components/schemas/Links'
              example:
                {
                  "noteName": {
                    "idName": "yegah",
                    "displayName": "yegāh"
                  },
                  "availability": {
                    "count": 35,
                    "data": [
                      {
                        "tuningSystem": {
                          "id": "ronzevalle_1904",
                          "idName": "ronzevalle_1904",
                          "displayName": "Ronzevalle (1904) Modernist Arabic Tuning",
                          "version": "2025-11-26T10:19:38.569Z"
                        },
                        "startingNotes": [
                          {
                            "startingNote": {
                              "idName": "ushayran",
                              "displayName": "ʿushayrān"
                            },
                            "pitchClassIndex": 20,
                            "octave": 0
                          },
                          {
                            "startingNote": {
                              "idName": "yegah",
                              "displayName": "yegāh"
                            },
                            "pitchClassIndex": 0,
                            "octave": 1
                          }
                        ]
                      },
                      {
                        "tuningSystem": {
                          "id": "alkindi_874",
                          "idName": "alkindi_874",
                          "displayName": "al-Kindī (874) 12-Tone",
                          "version": "2025-11-17T13:16:34.754Z"
                        },
                        "startingNotes": [
                          {
                            "startingNote": {
                              "idName": "ushayran",
                              "displayName": "ʿushayrān"
                            },
                            "pitchClassIndex": 10,
                            "octave": 0
                          },
                          {
                            "startingNote": {
                              "idName": "yegah",
                              "displayName": "yegāh"
                            },
                            "pitchClassIndex": 0,
                            "octave": 1
                          },
                          {
                            "startingNote": {
                              "idName": "rast",
                              "displayName": "rāst"
                            },
                            "pitchClassIndex": 7,
                            "octave": 0
                          }
                        ]
                      },
                      {
                        "tuningSystem": {
                          "id": "angloeuropean_1700",
                          "idName": "angloeuropean_1700",
                          "displayName": "Anglo-European (1700) 12-EDO",
                          "version": "2025-11-18T11:53:58.428Z"
                        },
                        "startingNotes": [
                          {
                            "startingNote": {
                              "idName": "ushayran",
                              "displayName": "ʿushayrān"
                            },
                            "pitchClassIndex": 10,
                            "octave": 0
                          },
                          {
                            "startingNote": {
                              "idName": "yegah",
                              "displayName": "yegāh"
                            },
                            "pitchClassIndex": 0,
                            "octave": 1
                          }
                        ]
                      },
                      {
                        "tuningSystem": {
                          "id": "aldik_1926a",
                          "idName": "aldik_1926a",
                          "displayName": "al-Dīk (1926a) al-Sullam al-Fārsī or al-'Arabī (!!!)",
                          "version": "2025-10-18T19:42:23.643Z"
                        },
                        "startingNotes": [
                          {
                            "startingNote": {
                              "idName": "yegah",
                              "displayName": "yegāh"
                            },
                            "pitchClassIndex": 0,
                            "octave": 1
                          }
                        ]
                      },
                      {
                        "tuningSystem": {
                          "id": "aldik_1926b",
                          "idName": "aldik_1926b",
                          "displayName": "al-Dīk (1926b) Old Arabic Scale",
                          "version": "2025-10-18T19:42:23.643Z"
                        },
                        "startingNotes": [
                          {
                            "startingNote": {
                              "idName": "yegah",
                              "displayName": "yegāh"
                            },
                            "pitchClassIndex": 0,
                            "octave": 1
                          }
                        ]
                      },
                      {
                        "…": "truncated — 30 more items"
                      }
                    ]
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/pitch-classes/note-names/yegah/availability",
                    "detail": "/api/pitch-classes/note-names/yegah",
                    "compare": "/api/pitch-classes/note-names/yegah/compare"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Note name not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /pitch-classes/note-names/{noteName}/compare:
    get:
      tags:
        - Pitch Classes
      summary: Compare pitch class by note name across tuning systems
      description: |
        Compare a pitch class (by note name) across different tuning systems.
        
        Returns comprehensive data for each tuning system including:
        - Pitch class data in the requested format
        - Context information (tuning system, starting note, reference frequency)
      operationId: comparePitchClassByNoteName
      parameters:
        - name: noteName
          in: path
          required: true
          description: URL-safe note name (e.g., "rast", "dugah", "nawa")
          schema:
            type: string
          example: "rast"
        - name: tuningSystems
          in: query
          required: true
          description: Comma-separated tuning system IDs
          schema:
            type: string
          example: "meshshaqa_1899,cairocongresstuningcommittee_1929"
        - name: startingNote
          in: query
          required: true
          description: |
            Starting note (applies to all tuning systems), or "all" to include all available starting notes for each tuning system. Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "yegah"
        - name: pitchClassDataType
          in: query
          required: false
          description: Pitch class data format (defaults to `all` when omitted, matching the handler)
          schema:
            type: string
            enum: [all, abjadName, englishName, solfege, fraction, cents, centsDeviation, decimalRatio, stringLength, fretDivision, midiNoteNumber, midiNoteDeviation, frequency, referenceNoteName]
            default: "all"
          example: "cents"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Comparison data retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      noteName:
                        $ref: '#/components/schemas/IdentifierRef'
                  comparisons:
                    type: array
                    description: Array of comparison results, one per tuning system/starting note combination
                    items:
                      type: object
                      properties:
                        tuningSystem:
                          $ref: '#/components/schemas/EntityRef'
                        startingNote:
                          $ref: '#/components/schemas/IdentifierRef'
                        pitchClass:
                          $ref: '#/components/schemas/PitchClass'
                        context:
                          type: object
                          properties:
                            referenceFrequency:
                              type: number
                            pitchClassDataType:
                              type: string
                  meta:
                    type: object
                    properties:
                      totalComparisons:
                        type: integer
                      successfulComparisons:
                        type: integer
                      failedComparisons:
                        type: integer
                  links:
                    $ref: '#/components/schemas/Links'
              example:
                {
                  "context": {
                    "noteName": {
                      "idName": "yegah",
                      "displayName": "yegāh"
                    }
                  },
                  "comparisons": [
                    {
                      "tuningSystem": {
                        "id": "ibnsina_1037",
                        "idName": "ibnsina_1037",
                        "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                        "version": "2025-11-09T00:44:40.275Z"
                      },
                      "startingNote": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "pitchClass": {
                        "pitchClassIndex": 0,
                        "octave": 1,
                        "noteName": "yegah",
                        "noteNameDisplay": "yegāh",
                        "cents": 0
                      },
                      "context": {
                        "referenceFrequency": 97.999,
                        "pitchClassDataType": "cents"
                      }
                    },
                    {
                      "tuningSystem": {
                        "id": "alfarabi_950g",
                        "idName": "alfarabi_950g",
                        "displayName": "al-Fārābī (950g) First Oud Tuning (Full First Octave) 27-Tone",
                        "version": "2025-10-18T19:42:23.643Z"
                      },
                      "startingNote": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "pitchClass": {
                        "pitchClassIndex": 0,
                        "octave": 1,
                        "noteName": "yegah",
                        "noteNameDisplay": "yegāh",
                        "cents": 0
                      },
                      "context": {
                        "referenceFrequency": 97.999,
                        "pitchClassDataType": "cents"
                      }
                    }
                  ],
                  "meta": {
                    "totalComparisons": 2,
                    "successfulComparisons": 2,
                    "failedComparisons": 0
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/pitch-classes/note-names/yegah/compare?tuningSystems=ibnsina_1037%2Calfarabi_950g&pitchClassDataType=cents&startingNote=yegah",
                    "detail": "/api/pitch-classes/note-names/yegah",
                    "availability": "/api/pitch-classes/note-names/yegah/availability"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Note name not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /intervals:
    get:
      tags:
        - Intervals
      summary: Calculate intervals by note names
      description: |
        Get interval calculations for 2 or more pitch classes by listing their note names.
        
        Calculates intervals between consecutive note names: [0→1, 1→2, 2→3, ...]
        
        Supports:
        - Single tuning system with specific starting note
        - Single tuning system with startingNote="all" (returns array for all starting notes)
        - All tuning systems (returns array for all tuning systems and starting notes)
        
        Note: When querying by note names, the pitchClassIndex field is excluded from interval responses 
        as it is not meaningful in this context.
      operationId: calculateIntervalsByNoteNames
      parameters:
        - name: noteNames
          in: query
          required: true
          description: Comma-separated note names (e.g., "rast,dugah,segah"). Note names can come from any octave.
          schema:
            type: string
          example: "rast,dugah,segah"
        - name: tuningSystem
          in: query
          required: false
          description: Tuning system ID. If provided, calculates in that tuning system only. If omitted, calculates in all tuning systems.
          schema:
            type: string
          example: "alsabbagh_1954"
        - name: startingNote
          in: query
          required: false
          description: |
            Starting note. Required if tuningSystem is provided.
            
            - Use a specific starting note name (URL-safe) for single result
            - Use "all" to include all available starting notes for that tuning system (returns array of results, one per starting note)
            
            Note: Since note names are unique per octave, each note name already identifies its octave. No octave parameter is needed. Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "rast"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Interval calculations retrieved successfully
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: Single tuning system response
                    properties:
                      context:
                        type: object
                        properties:
                          noteNames:
                            type: array
                            items:
                              $ref: '#/components/schemas/IdentifierRef'
                          tuningSystem:
                            $ref: '#/components/schemas/EntityRef'
                          startingNote:
                            $ref: '#/components/schemas/IdentifierRef'
                          referenceFrequency:
                            type: number
                      intervals:
                        type: array
                        description: Array of intervals between consecutive note names [0→1, 1→2, 2→3, ...]
                        items:
                          type: object
                          properties:
                            from:
                              $ref: '#/components/schemas/IdentifierRef'
                            to:
                              $ref: '#/components/schemas/IdentifierRef'
                            interval:
                              type: object
                              description: Interval data (excludes pitchClassIndex when querying by note names)
                              properties:
                                fraction:
                                  type: string
                                cents:
                                  type: number
                                decimalRatio:
                                  type: number
                                stringLength:
                                  type: number
                                fretDivision:
                                  type: number
                                originalValue:
                                  type: string
                                originalValueType:
                                  type: string
                      links:
                        $ref: '#/components/schemas/Links'
                  - type: object
                    description: Multiple tuning systems or starting notes response
                    properties:
                      context:
                        type: object
                        properties:
                          noteNames:
                            type: array
                            items:
                              $ref: '#/components/schemas/IdentifierRef'
                      intervals:
                        type: array
                        description: Array of interval calculations, one per tuning system/starting note combination
                        items:
                          type: object
                          properties:
                            intervals:
                              type: array
                              description: Array of intervals between consecutive note names [0→1, 1→2, 2→3, ...]
                              items:
                                type: object
                                properties:
                                  from:
                                    $ref: '#/components/schemas/IdentifierRef'
                                  to:
                                    $ref: '#/components/schemas/IdentifierRef'
                                  interval:
                                    type: object
                            context:
                              type: object
                              properties:
                                tuningSystem:
                                  $ref: '#/components/schemas/EntityRef'
                                startingNote:
                                  $ref: '#/components/schemas/IdentifierRef'
                                referenceFrequency:
                                  type: number
                      links:
                        $ref: '#/components/schemas/Links'
              example:
                {
                  "context": {
                    "noteNames": [
                      {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      {
                        "idName": "segah",
                        "displayName": "segāh"
                      }
                    ]
                  },
                  "intervals": [
                    {
                      "tuningSystemContext": {
                        "tuningSystem": {
                          "id": "ibnsina_1037",
                          "idName": "ibnsina_1037",
                          "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                          "version": "2025-11-09T00:44:40.275Z"
                        },
                        "startingNote": {
                          "idName": "yegah",
                          "displayName": "yegāh"
                        },
                        "referenceFrequency": 97.999
                      },
                      "fromNote": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "toNote": {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      "intervalData": {
                        "fraction": "3/2",
                        "cents": 701.9550008653874,
                        "decimalRatio": 1.5,
                        "stringLength": -333.33333333333337,
                        "fretDivision": 333.333,
                        "originalValue": "3/2",
                        "originalValueType": "fraction"
                      }
                    },
                    {
                      "tuningSystemContext": {
                        "tuningSystem": {
                          "id": "ibnsina_1037",
                          "idName": "ibnsina_1037",
                          "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                          "version": "2025-11-09T00:44:40.275Z"
                        },
                        "startingNote": {
                          "idName": "yegah",
                          "displayName": "yegāh"
                        },
                        "referenceFrequency": 97.999
                      },
                      "fromNote": {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      "toNote": {
                        "idName": "segah",
                        "displayName": "segāh"
                      },
                      "intervalData": {
                        "fraction": "13/12",
                        "cents": 138.57266090392318,
                        "decimalRatio": 1.0833333333333333,
                        "stringLength": -51.28205128205127,
                        "fretDivision": 51.28199999999998,
                        "originalValue": "13/12",
                        "originalValueType": "fraction"
                      }
                    }
                  ],
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/intervals?tuningSystem=ibnsina_1037&startingNote=yegah&pitchClassDataType=cents&noteNames=yegah%2Cdugah%2Csegah",
                    "compare": "/api/intervals/compare?noteNames=yegah,dugah,segah"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Note name or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /intervals/compare:
    get:
      tags:
        - Intervals
      summary: Compare intervals across tuning systems
      description: |
        Compare interval calculations for 2+ pitch classes across different tuning systems by listing their note names.
        
        Calculates intervals between consecutive note names: [0→1, 1→2, 2→3, ...]
        
        Note: When querying by note names, the pitchClassIndex field is excluded from interval responses 
        as it is not meaningful in this context.
      operationId: compareIntervalsByNoteNames
      parameters:
        - name: noteNames
          in: query
          required: true
          description: Comma-separated note names. Note names can come from any octave.
          schema:
            type: string
          example: "rast,dugah,segah"
        - name: tuningSystems
          in: query
          required: true
          description: Comma-separated tuning system IDs
          schema:
            type: string
          example: "alfarabi_950g,cairocongresstuningcommittee_1929"
        - name: startingNote
          in: query
          required: true
          description: |
            Starting note (applies to all tuning systems), or "all" to include all available starting notes for each tuning system.
            
            When "all" is used, the endpoint returns results for all valid starting notes in each tuning system, allowing comprehensive comparison across all theoretical frameworks.
            
            Note: Since note names are unique per octave, each note name already identifies its octave. No octave parameter is needed. Use `yegah` for IbnSina/Meshshaqa, `ushayran` for al-Farabi/al-Kindi, `rast` for CairoCongress/al-Sabbagh
          schema:
            type: string
          example: "ushayran"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Comparison data retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      noteNames:
                        type: array
                        items:
                          $ref: '#/components/schemas/IdentifierRef'
                  comparisons:
                    type: array
                    description: Array of comparison results, one per tuning system/starting note combination
                    items:
                      type: object
                      properties:
                        tuningSystem:
                          $ref: '#/components/schemas/EntityRef'
                        startingNote:
                          $ref: '#/components/schemas/IdentifierRef'
                        intervals:
                          type: array
                          description: Array of intervals between consecutive note names [0→1, 1→2, 2→3, ...]
                          items:
                            type: object
                            properties:
                              from:
                                $ref: '#/components/schemas/IdentifierRef'
                              to:
                                $ref: '#/components/schemas/IdentifierRef'
                              interval:
                                type: object
                                description: Interval data (excludes pitchClassIndex when querying by note names)
                                properties:
                                  fraction:
                                    type: string
                                  cents:
                                    type: number
                                  decimalRatio:
                                    type: number
                                  stringLength:
                                    type: number
                                  fretDivision:
                                    type: number
                                  originalValue:
                                    type: string
                                  originalValueType:
                                    type: string
                        context:
                          type: object
                          properties:
                            referenceFrequency:
                              type: number
                  meta:
                    type: object
                    properties:
                      totalComparisons:
                        type: integer
                      successfulComparisons:
                        type: integer
                      failedComparisons:
                        type: integer
                  links:
                    $ref: '#/components/schemas/Links'
              example:
                {
                  "context": {
                    "noteNames": [
                      {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      {
                        "idName": "dugah",
                        "displayName": "dūgāh"
                      },
                      {
                        "idName": "segah",
                        "displayName": "segāh"
                      }
                    ]
                  },
                  "comparisons": [
                    {
                      "tuningSystem": {
                        "id": "ibnsina_1037",
                        "idName": "ibnsina_1037",
                        "displayName": "Ibn Sīnā  (1037) 7-Fret Oud 17-Tone",
                        "version": "2025-11-09T00:44:40.275Z"
                      },
                      "startingNote": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "intervals": [
                        {
                          "from": {
                            "idName": "yegah",
                            "displayName": "yegāh"
                          },
                          "to": {
                            "idName": "dugah",
                            "displayName": "dūgāh"
                          },
                          "interval": {
                            "fraction": "3/2",
                            "cents": 701.9550008653874,
                            "decimalRatio": 1.5,
                            "stringLength": -333.33333333333337,
                            "fretDivision": 333.333,
                            "originalValue": "3/2",
                            "originalValueType": "fraction"
                          }
                        },
                        {
                          "from": {
                            "idName": "dugah",
                            "displayName": "dūgāh"
                          },
                          "to": {
                            "idName": "segah",
                            "displayName": "segāh"
                          },
                          "interval": {
                            "fraction": "13/12",
                            "cents": 138.57266090392318,
                            "decimalRatio": 1.0833333333333333,
                            "stringLength": -51.28205128205127,
                            "fretDivision": 51.28199999999998,
                            "originalValue": "13/12",
                            "originalValueType": "fraction"
                          }
                        }
                      ],
                      "context": {
                        "referenceFrequency": 97.999
                      }
                    },
                    {
                      "tuningSystem": {
                        "id": "alfarabi_950g",
                        "idName": "alfarabi_950g",
                        "displayName": "al-Fārābī (950g) First Oud Tuning (Full First Octave) 27-Tone",
                        "version": "2025-10-18T19:42:23.643Z"
                      },
                      "startingNote": {
                        "idName": "yegah",
                        "displayName": "yegāh"
                      },
                      "intervals": [
                        {
                          "from": {
                            "idName": "yegah",
                            "displayName": "yegāh"
                          },
                          "to": {
                            "idName": "dugah",
                            "displayName": "dūgāh"
                          },
                          "interval": {
                            "fraction": "3/2",
                            "cents": 701.9550008653874,
                            "decimalRatio": 1.5,
                            "stringLength": -333.33333333333337,
                            "fretDivision": 333.333,
                            "originalValue": "3/2",
                            "originalValueType": "fraction"
                          }
                        },
                        {
                          "from": {
                            "idName": "dugah",
                            "displayName": "dūgāh"
                          },
                          "to": {
                            "idName": "segah",
                            "displayName": "segāh"
                          },
                          "interval": {
                            "fraction": "12/11",
                            "cents": 150.63705850063081,
                            "decimalRatio": 1.0909090909090908,
                            "stringLength": -55.55555555555554,
                            "fretDivision": 55.55599999999998,
                            "originalValue": "12/11",
                            "originalValueType": "fraction"
                          }
                        }
                      ],
                      "context": {
                        "referenceFrequency": 97.999
                      }
                    }
                  ],
                  "meta": {
                    "totalComparisons": 2,
                    "successfulComparisons": 2,
                    "failedComparisons": 0
                  },
                  "links": {
                    "self": "https://diarmaqar.netlify.app/api/intervals/compare?tuningSystems=ibnsina_1037%2Calfarabi_950g&startingNote=yegah&pitchClassDataType=cents&noteNames=yegah%2Cdugah%2Csegah",
                    "intervals": "/api/intervals?noteNames=yegah,dugah,segah"
                  }
                }
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Note name not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /intervals/calculate:
    get:
      tags:
        - Intervals
      summary: Calculate interval between two pitch classes
      description: |
        Calculate the interval between two pitch classes in a specific tuning system.
        
        Supports flexible pitch class identification:
        - By note name (e.g., "rast", "dugah")
        - By value in any format: fraction (e.g., "3/2"), cents (e.g., "701.955"), decimalRatio (e.g., "1.5"), stringLength, or fretDivision
        
        Returns the interval in the requested unit format, plus all other unit formats for reference.
      operationId: calculateIntervalBetweenPitchClasses
      parameters:
        - name: from
          in: query
          required: true
          description: First pitch class identifier (note name or value)
          schema:
            type: string
          example: "rast"
        - name: to
          in: query
          required: true
          description: Second pitch class identifier (note name or value)
          schema:
            type: string
          example: "dugah"
        - name: tuningSystem
          in: query
          required: true
          description: Tuning system ID
          schema:
            type: string
          example: "ibnsina_1037"
        - name: startingNote
          in: query
          required: true
          description: Starting note for the tuning system
          schema:
            type: string
          example: "yegah"
        - name: unit
          in: query
          required: true
          description: Output unit format for the interval
          schema:
            type: string
            enum: [fraction, cents, centsFromZero, decimalRatio, stringLength, fretDivision]
          example: "cents"
        - name: fromType
          in: query
          required: false
          description: |
            Type of 'from' value if it's a value (not a note name).
            Required if 'from' is a value and cannot be auto-detected.
          schema:
            type: string
            enum: [fraction, cents, decimalRatio, stringLength, fretDivision]
          example: "fraction"
        - name: toType
          in: query
          required: false
          description: |
            Type of 'to' value if it's a value (not a note name).
            Required if 'to' is a value and cannot be auto-detected.
          schema:
            type: string
            enum: [fraction, cents, decimalRatio, stringLength, fretDivision]
          example: "fraction"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Interval calculated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      tuningSystem:
                        $ref: '#/components/schemas/EntityRef'
                      startingNote:
                        $ref: '#/components/schemas/IdentifierRef'
                      referenceFrequency:
                        type: number
                        description: Reference frequency in Hz
                  from:
                    type: object
                    properties:
                      pitchClass:
                        type: object
                        properties:
                          pitchClassIndex:
                            type: integer
                          octave:
                            type: integer
                          noteName:
                            type: string
                            description: URL-safe note name
                          noteNameDisplay:
                            type: string
                            description: Display note name with diacritics
                          noteNameDisplayAr:
                            type: string
                            description: Arabic note name (if includeArabic=true)
                          englishName:
                            type: string
                          solfege:
                            type: string
                          abjadName:
                            type: string
                          fraction:
                            type: string
                          cents:
                            type: number
                          decimalRatio:
                            type: number
                          stringLength:
                            type: number
                          fretDivision:
                            type: number
                      identifier:
                        $ref: '#/components/schemas/IdentifierRef'
                        description: The input identifier used to find this pitch class
                  to:
                    type: object
                    properties:
                      pitchClass:
                        type: object
                        properties:
                          pitchClassIndex:
                            type: integer
                          octave:
                            type: integer
                          noteName:
                            type: string
                            description: URL-safe note name
                          noteNameDisplay:
                            type: string
                            description: Display note name with diacritics
                          noteNameDisplayAr:
                            type: string
                            description: Arabic note name (if includeArabic=true)
                          englishName:
                            type: string
                          solfege:
                            type: string
                          abjadName:
                            type: string
                          fraction:
                            type: string
                          cents:
                            type: number
                          decimalRatio:
                            type: number
                          stringLength:
                            type: number
                          fretDivision:
                            type: number
                      identifier:
                        $ref: '#/components/schemas/IdentifierRef'
                        description: The input identifier used to find this pitch class
                  interval:
                    type: object
                    properties:
                      value:
                        oneOf:
                          - type: string
                          - type: number
                        description: The interval in the requested unit format
                      unit:
                        type: string
                        description: The requested unit format
                        enum: [fraction, cents, centsFromZero, decimalRatio, stringLength, fretDivision]
                      allUnits:
                        type: object
                        description: All unit formats for reference
                        properties:
                          fraction:
                            type: string
                          cents:
                            type: number
                          decimalRatio:
                            type: number
                          stringLength:
                            type: number
                          fretDivision:
                            type: number
                  links:
                    $ref: '#/components/schemas/Links'
              examples:
                noteNames:
                  summary: Calculate interval using note names
                  value:
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        idName: "ibnsina_1037"
                        displayName: "Ibn Sīnā (1037)"
                      startingNote:
                        idName: "yegah"
                        displayName: "yegāh"
                      referenceFrequency: 97.999
                    from:
                      pitchClass:
                        pitchClassIndex: 0
                        octave: 0
                        noteName: "yegah"
                        noteNameDisplay: "yegāh"
                        englishName: "D"
                        solfege: "re"
                        abjadName: "د"
                        fraction: "1/1"
                        cents: 0
                        decimalRatio: 1
                        stringLength: 1000
                        fretDivision: 0
                      identifier:
                        idName: "yegah"
                        displayName: "yegāh"
                    to:
                      pitchClass:
                        pitchClassIndex: 1
                        octave: 0
                        noteName: "dugah"
                        noteNameDisplay: "dūgāh"
                        englishName: "E"
                        solfege: "mi"
                        abjadName: "ه"
                        fraction: "9/8"
                        cents: 203.91
                        decimalRatio: 1.125
                        stringLength: 888.889
                        fretDivision: 102
                      identifier:
                        idName: "dugah"
                        displayName: "dūgāh"
                    interval:
                      value: 203.91
                      unit: "cents"
                      allUnits:
                        fraction: "9/8"
                        cents: 203.91
                        decimalRatio: 1.125
                        stringLength: 111.111
                        fretDivision: 102
                    links:
                      self: "/api/intervals/calculate?from=yegah&to=dugah&tuningSystem=ibnsina_1037&startingNote=yegah&unit=cents"
                      tuningSystem: "/api/tuning-systems/ibnsina_1037"
                      intervals: "/api/intervals?noteNames=yegah,dugah&tuningSystem=ibnsina_1037&startingNote=yegah"
                values:
                  summary: Calculate interval using values
                  value:
                    context:
                      tuningSystem:
                        id: "ibnsina_1037"
                        idName: "ibnsina_1037"
                        displayName: "Ibn Sīnā (1037)"
                      startingNote:
                        idName: "yegah"
                        displayName: "yegāh"
                      referenceFrequency: 97.999
                    from:
                      pitchClass:
                        pitchClassIndex: 0
                        octave: 0
                        noteName: "yegah"
                        noteNameDisplay: "yegāh"
                        englishName: "D"
                        solfege: "re"
                        abjadName: "د"
                        fraction: "1/1"
                        cents: 0
                        decimalRatio: 1
                        stringLength: 1000
                        fretDivision: 0
                      identifier:
                        idName: "1/1"
                        displayName: "1/1"
                    to:
                      pitchClass:
                        pitchClassIndex: 1
                        octave: 0
                        noteName: "dugah"
                        noteNameDisplay: "dūgāh"
                        englishName: "E"
                        solfege: "mi"
                        abjadName: "ه"
                        fraction: "9/8"
                        cents: 203.91
                        decimalRatio: 1.125
                        stringLength: 888.889
                        fretDivision: 102
                      identifier:
                        idName: "9/8"
                        displayName: "9/8"
                    interval:
                      value: "9/8"
                      unit: "fraction"
                      allUnits:
                        fraction: "9/8"
                        cents: 203.91
                        decimalRatio: 1.125
                        stringLength: 111.111
                        fretDivision: 102
                    links:
                      self: "/api/intervals/calculate?from=1/1&to=9/8&tuningSystem=ibnsina_1037&startingNote=yegah&unit=fraction&fromType=fraction&toType=fraction"
                      tuningSystem: "/api/tuning-systems/ibnsina_1037"
                      intervals: "/api/intervals?noteNames=yegah,dugah&tuningSystem=ibnsina_1037&startingNote=yegah"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingParameter:
                  summary: Missing required parameter
                  value:
                    error: "from parameter is required"
                    message: "The first pitch class identifier must be provided."
                    hint: "Add &from=rast or &from=3/2 to your request"
                invalidUnit:
                  summary: Invalid unit parameter
                  value:
                    error: "Invalid unit 'invalid'"
                    message: "The unit must be one of the valid options."
                    validOptions: ["fraction", "cents", "centsFromZero", "decimalRatio", "stringLength", "fretDivision"]
                    hint: "Use one of: fraction, cents, centsFromZero, decimalRatio, stringLength, fretDivision"
                invalidStartingNote:
                  summary: Invalid starting note
                  value:
                    error: "Invalid startingNote 'invalid'"
                    validOptions: ["yegah", "ushayran", "rast"]
                    hint: "Valid starting notes for this tuning system: yegah, ushayran, rast"
        '404':
          description: Pitch class or tuning system not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                pitchClassNotFound:
                  summary: Pitch class not found
                  value:
                    error: "Pitch class 'invalid' not found"
                    message: "Could not find a pitch class matching 'invalid' in tuning system 'ibnsina_1037' with starting note 'yegah'."
                    hint: "Use /api/pitch-classes/note-names to see all valid note names, or ensure the value matches exactly a pitch class value in the tuning system"
                tuningSystemNotFound:
                  summary: Tuning system not found
                  value:
                    error: "Tuning system 'invalid' not found"
                    hint: "Use /api/tuning-systems to see all available tuning systems"
        '500':
          $ref: '#/components/responses/ServerError'

  /sources:
    get:
      tags:
        - Sources
      summary: List all bibliographic sources
      description: |
        Return all bibliographic sources (books, articles, theses) with comprehensive metadata.
        
        - Includes titles, contributors, publication information, and version timestamps
        - Sources document the historical and scholarly foundation for the archive's musical data
        - All sources include common fields (edition, publication date, URL, etc.)
        - Type-specific fields are included based on sourceType:
          - Books: publisher, place, ISBN, original publication date
          - Articles: journal, volume, issue, page range, DOI
          - Theses: degree type, university, department, database information
      operationId: listSources
      parameters:
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.
            
            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: List of sources retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - count
                  - data
                properties:
                  count:
                    type: integer
                    description: Total number of sources
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SourceResponse'
              examples:
                comprehensive:
                  summary: Comprehensive source data with all fields
                  value:
                    count: 2
                    data:
                      - id: "al-Khulʿī-(1904/2011)"
                        displayName: "al-Khulʿī-(1904/2011)"
                        sourceType: "Book"
                        title: "Kitāb al-Mūsiqā al-Sharqī"
                        titleAr: "كتاب الموسيقى الشرقي"
                        contributors:
                          - type: "Author"
                            firstName: "Mohammad Kāmil"
                            lastName: "al-Khulʿī"
                            firstNameAr: "محمد كامل"
                            lastNameAr: "الخلعي"
                        edition: ""
                        editionAr: ""
                        publicationDate: "2011"
                        publicationDateAr: "٢٠١١"
                        url: "https://www.hindawi.org/books/46319638/"
                        dateAccessed: "21-05-2025"
                        version: "2025-10-18T19:50:31.664Z"
                        originalPublicationDate: "1904"
                        originalPublicationDateAr: "١٩٠٤"
                        publisher: "Hindawi Foundation for Education and Culture"
                        publisherAr: "مؤسسة هنداوي للتعليم والثقافة"
                        place: "Cairo"
                        placeAr: "القاهرة"
                        ISBN: "9789776416543"
                      - id: "Farmer-(1937)"
                        displayName: "Farmer-(1937)"
                        sourceType: "Article"
                        title: "The Lute Scale of Avicenna"
                        titleAr: "سلم العود لإبن سينا"
                        contributors:
                          - type: "Author"
                            firstName: "Henry George"
                            lastName: "Farmer"
                            firstNameAr: "هنري جورج"
                            lastNameAr: "فارمر"
                        edition: ""
                        editionAr: ""
                        publicationDate: "1937"
                        publicationDateAr: "١٩٣٧"
                        url: ""
                        dateAccessed: ""
                        version: "2025-10-18T19:50:31.664Z"
                        journal: "The Journal of the Royal Asiatic Society of Great Britain and Ireland"
                        journalAr: "مجلة الجمعية الملكية الآسيوية في بريطانيا العظمى وأيرلندا"
                        volume: "2"
                        volumeAr: "٢"
                        issue: ""
                        issueAr: ""
                        pageRange: "245–257"
                        pageRangeAr: "٢٤٥-٢٥٧"
                        DOI: ""
                      - id: "Allami-(2022)"
                        displayName: "Allami-(2022)"
                        sourceType: "Thesis"
                        title: "Échos-monde: Towards a hybrid repertoire of contemporary and experimental acoustic, electroacoustic and electronic Arabic music"
                        titleAr: "‎عالم الأصداء: نحو رصيدٍ هجينٍ للموسيقى العربية المعاصرة والتجريبية، صوتياً وكهروصوتياً وإلكترونياً"
                        contributors:
                          - type: "Author"
                            firstName: "Khyam"
                            lastName: "Allami"
                            firstNameAr: "خيّام"
                            lastNameAr: "اللامي"
                        edition: ""
                        editionAr: ""
                        publicationDate: "2022"
                        publicationDateAr: ""
                        url: "https://www.open-access.bcu.ac.uk/14036/"
                        dateAccessed: "16-10-2025"
                        version: "2025-10-23T22:32:56.276Z"
                        degreeType: "PhD Dissertation"
                        degreeTypeAr: "أطروحة دكتوراه"
                        university: "Birmingham City University"
                        universityAr: "جامعة برمنغهام سيتي"
                        department: "Royal Birmingham Conservatoire"
                        departmentAr: "معهد برمنغهام الملكي"
                        databaseIdentifier: ""
                        databaseName: ""
                    meta:
                      total: 17
        '500':
          $ref: '#/components/responses/ServerError'

  /sources/{id}:
    get:
      tags:
        - Sources
      summary: Get a single bibliographic source
      description: |
        Return detailed information for a single bibliographic source by ID.

        - Returns comprehensive bibliographic metadata for the specified source
        - Includes all common fields (title, contributors, publication date, etc.)
        - Includes type-specific fields based on sourceType:
          - Books: publisher, place, ISBN, original publication date
          - Articles: journal, volume, issue, page range, DOI
          - Theses: degree type, university, department, database information
        - Supports bilingual responses (English/Arabic) via includeArabic parameter
      operationId: getSource
      parameters:
        - name: id
          in: path
          required: true
          description: |
            The unique identifier for the bibliographic source.

            Examples:
            - `al-Khuli-(2011)` - Book source ID (URL-safe, diacritics removed)
            - `Farmer-(1937)` - Article source ID
            - `Allami-(2022)` - Thesis source ID

            **Note**: Source IDs use only the publication date (not original publication date)
            and are URL-safe with diacritics removed for compatibility.

            Use the `/api/sources` endpoint to retrieve all available source IDs.
          schema:
            type: string
          example: "Farmer-(1937)"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., titleAr, firstNameAr, lastNameAr)
            - Contributors get Arabic name versions in firstNameAr and lastNameAr fields
            - Type-specific fields get Arabic versions (e.g., publisherAr, journalAr, universityAr)
            - Display name gets Arabic version in displayNameAr field
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Source retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  source:
                    $ref: '#/components/schemas/SourceResponse'
              examples:
                book:
                  summary: Book source example
                  value:
                    source:
                      id: "al-Khuli-(2011)"
                      displayName: "al-Khuli-(2011)"
                      sourceType: "Book"
                      title: "Kitāb al-Mūsiqā al-Sharqī"
                      contributors:
                        - type: "Author"
                          firstName: "Mohammad Kāmil"
                          lastName: "al-Khulʿī"
                      edition: ""
                      publicationDate: "2011"
                      url: "https://www.hindawi.org/books/46319638/"
                      dateAccessed: "21-05-2025"
                      version: "2025-10-18T19:50:31.664Z"
                      originalPublicationDate: "1904"
                      publisher: "Hindawi Foundation for Education and Culture"
                      place: "Cairo"
                      ISBN: "9789776416543"
                article:
                  summary: Article source example
                  value:
                    source:
                      id: "Farmer-(1937)"
                      displayName: "Farmer-(1937)"
                      sourceType: "Article"
                      title: "The Lute Scale of Avicenna"
                      contributors:
                        - type: "Author"
                          firstName: "Henry George"
                          lastName: "Farmer"
                      edition: ""
                      publicationDate: "1937"
                      url: ""
                      dateAccessed: ""
                      version: "2025-10-18T19:50:31.664Z"
                      journal: "The Journal of the Royal Asiatic Society of Great Britain and Ireland"
                      volume: "2"
                      issue: ""
                      pageRange: "245–257"
                      DOI: ""
                thesis:
                  summary: Thesis source example
                  value:
                    source:
                      id: "Allami-(2022)"
                      displayName: "Allami-(2022)"
                      sourceType: "Thesis"
                      title: "Échos-monde: Towards a hybrid repertoire of contemporary and experimental acoustic, electroacoustic and electronic Arabic music"
                      contributors:
                        - type: "Author"
                          firstName: "Khyam"
                          lastName: "Allami"
                      edition: ""
                      publicationDate: "2022"
                      url: "https://www.open-access.bcu.ac.uk/14036/"
                      dateAccessed: "16-10-2025"
                      version: "2025-10-23T22:32:56.276Z"
                      degreeType: "PhD Dissertation"
                      university: "Birmingham City University"
                      department: "Royal Birmingham Conservatoire"
                      databaseIdentifier: ""
                      databaseName: ""
        '400':
          description: Bad request - Invalid or missing source ID
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  hint:
                    type: string
                  example:
                    type: string
              example:
                error: "No source ID provided"
                hint: "Please specify a source ID"
                example: "/api/sources/Farmer-(1937)"
        '404':
          description: Source not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  hint:
                    type: string
              example:
                error: "Source 'invalid-id' not found"
                hint: "Use /api/sources to see all available sources"
        '500':
          $ref: '#/components/responses/ServerError'

  /sources/{id}/tuning-systems:
    get:
      tags:
        - Sources
      summary: List tuning systems by source
      description: |
        Returns all tuning systems that reference the specified source.
        
        A tuning system is included if any of its sourcePageReferences match the source ID.
        This endpoint provides an easy way to see which tuning systems have been derived from a specific source.
      operationId: listTuningSystemsBySource
      parameters:
        - name: id
          in: path
          required: true
          description: |
            The unique identifier for the bibliographic source.
            
            Use the `/api/sources` endpoint to retrieve all available source IDs.
          schema:
            type: string
          example: "Farmer-(1937)"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Tuning systems retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      source:
                        $ref: '#/components/schemas/EntityRef'
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        tuningSystem:
                          $ref: '#/components/schemas/EntityRef'
                        sourceReferences:
                          type: array
                          description: Page references for this source within the tuning system
                          items:
                            type: object
                            properties:
                              page:
                                type: string
                                description: Page number or range where the tuning system is documented
                        startingNotes:
                          type: array
                          items:
                            type: string
                        links:
                          $ref: '#/components/schemas/Links'
                  meta:
                    type: object
                    properties:
                      count:
                        type: integer
              example:
                {
                  "context": {
                    "source": {
                      "id": "alshawwa_1946",
                      "idName": "alshawwa_1946",
                      "displayName": "al-Qawaʿid al-Fannīya fī al-Mūsiqa al-Sharqīya wa al-Gharbīya"
                    }
                  },
                  "count": 1,
                  "data": [
                    {
                      "tuningSystem": {
                        "id": "allami_2025",
                        "idName": "allami_2025",
                        "displayName": "Allami (2025) al-Ṣabbāgh with al-Shawwā's 28-Tones",
                        "version": "2025-12-11T23:36:54.970Z",
                        "year": "2025"
                      },
                      "sourceReferences": [
                        {
                          "page": "13"
                        }
                      ],
                      "startingNotes": [
                        "rast"
                      ],
                      "links": {
                        "detail": "/api/tuning-systems/allami_2025",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    }
                  ]
                }
        '404':
          description: Source not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /sources/{id}/maqamat:
    get:
      tags:
        - Sources
      summary: List maqamat by source
      description: |
        Returns all maqamat that reference the specified source.
        
        A maqam is included if any of its sourcePageReferences match the source ID.
        This endpoint provides an easy way to see which maqamat have been documented in a specific source.
      operationId: listMaqamatBySource
      parameters:
        - name: id
          in: path
          required: true
          description: |
            The unique identifier for the bibliographic source.
            
            Use the `/api/sources` endpoint to retrieve all available source IDs.
          schema:
            type: string
          example: "Farmer-(1937)"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Maqamat retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      source:
                        $ref: '#/components/schemas/EntityRef'
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        maqam:
                          $ref: '#/components/schemas/EntityRef'
                        sourceReferences:
                          type: array
                          description: Page references for this source within the maqam
                          items:
                            type: object
                            properties:
                              page:
                                type: string
                                description: Page number or range where the maqam is documented
                        links:
                          $ref: '#/components/schemas/Links'
                  meta:
                    type: object
                    properties:
                      count:
                        type: integer
              example:
                {
                  "context": {
                    "source": {
                      "id": "alshawwa_1946",
                      "idName": "alshawwa_1946",
                      "displayName": "al-Qawaʿid al-Fannīya fī al-Mūsiqa al-Sharqīya wa al-Gharbīya"
                    }
                  },
                  "count": 36,
                  "data": [
                    {
                      "maqam": {
                        "id": "1",
                        "idName": "maqam_rast",
                        "displayName": "maqām rāst",
                        "version": "2025-12-10T17:03:34.565Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "42"
                        }
                      ],
                      "links": {
                        "detail": "/api/maqamat/maqam_rast",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "maqam": {
                        "id": "6",
                        "idName": "maqam_nawa_athar",
                        "displayName": "maqām nawā athar",
                        "version": "2025-12-10T18:28:28.107Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "52"
                        }
                      ],
                      "links": {
                        "detail": "/api/maqamat/maqam_nawa_athar",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "maqam": {
                        "id": "10",
                        "idName": "maqam_nahawand",
                        "displayName": "maqām nahāwand",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "49"
                        }
                      ],
                      "links": {
                        "detail": "/api/maqamat/maqam_nahawand",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "maqam": {
                        "id": "11",
                        "idName": "maqam_nahawand_kabir",
                        "displayName": "maqām nahāwand kabīr",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "50"
                        }
                      ],
                      "links": {
                        "detail": "/api/maqamat/maqam_nahawand_kabir",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "maqam": {
                        "id": "17",
                        "idName": "maqam_ajam_ushayran",
                        "displayName": "maqām ʿajam ʿushayrān",
                        "version": "2025-10-18T19:41:17.132Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "30"
                        }
                      ],
                      "links": {
                        "detail": "/api/maqamat/maqam_ajam_ushayran",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "…": "truncated — 31 more items"
                    }
                  ]
                }
        '404':
          description: Source not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

  /sources/{id}/ajnas:
    get:
      tags:
        - Sources
      summary: List ajnas by source
      description: |
        Returns all ajnas that reference the specified source.
        
        A jins is included if any of its SourcePageReferences match the source ID.
        This endpoint provides an easy way to see which ajnas have been documented in a specific source.
      operationId: listAjnasBySource
      parameters:
        - name: id
          in: path
          required: true
          description: |
            The unique identifier for the bibliographic source.
            
            Use the `/api/sources` endpoint to retrieve all available source IDs.
          schema:
            type: string
          example: "Farmer-(1937)"
        - name: includeArabic
          in: query
          required: false
          description: |
            Return bilingual responses with Arabic script when true.

            When true:
            - All English/transliteration fields remain unchanged
            - Arabic versions are added with "Ar" suffix (e.g., displayNameAr, noteNameDisplayAr)
            - Note names, maqām names, and jins names get Arabic versions in *Ar fields
            - Comments get Arabic versions in commentsAr if available
            - Tuning system display names get Arabic versions in displayNameAr if available
            - Source metadata gets Arabic versions in *Ar fields (titleAr, firstNameAr, etc.)
          schema:
            type: string
            enum: ["true", "false"]
            default: "false"
          example: "true"
      responses:
        '200':
          description: Ajnas retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  context:
                    type: object
                    properties:
                      source:
                        $ref: '#/components/schemas/EntityRef'
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        jins:
                          $ref: '#/components/schemas/EntityRef'
                        sourceReferences:
                          type: array
                          description: Page references for this source within the jins
                          items:
                            type: object
                            properties:
                              page:
                                type: string
                                description: Page number or range where the jins is documented
                        links:
                          $ref: '#/components/schemas/Links'
                  meta:
                    type: object
                    properties:
                      count:
                        type: integer
              example:
                {
                  "context": {
                    "source": {
                      "id": "alshawwa_1946",
                      "idName": "alshawwa_1946",
                      "displayName": "al-Qawaʿid al-Fannīya fī al-Mūsiqa al-Sharqīya wa al-Gharbīya"
                    }
                  },
                  "count": 2,
                  "data": [
                    {
                      "jins": {
                        "id": "21",
                        "idName": "jins_buselik_(al-shawwa)",
                        "displayName": "jins būselīk (al-Shawwā)",
                        "version": "2025-10-18T19:34:26.343Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "70"
                        }
                      ],
                      "links": {
                        "detail": "/api/ajnas/jins_buselik_(al-shawwa)",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    },
                    {
                      "jins": {
                        "id": "24",
                        "idName": "jins_awj_ara",
                        "displayName": "jins awj ʾārāʾ",
                        "version": "2025-10-18T19:34:26.343Z"
                      },
                      "sourceReferences": [
                        {
                          "page": "38"
                        }
                      ],
                      "links": {
                        "detail": "/api/ajnas/jins_awj_ara",
                        "source": "/api/sources/alshawwa_1946"
                      }
                    }
                  ]
                }
        '404':
          description: Source not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'

components:
  schemas:
    EntityReference:
      type: object
      description: Standard entity reference object
      required: [id, idName, displayName]
      properties:
        id:
          type: string
          description: Entity identifier
        idName:
          type: string
          description: URL-safe identifier
        displayName:
          type: string
          description: Display name with diacritics
        version:
          type: string
          description: ISO 8601 timestamp of last modification (when available)

    IdentifierReference:
      type: object
      description: Standard identifier reference object
      required: [idName, displayName]
      properties:
        idName:
          type: string
          description: URL-safe identifier
        displayName:
          type: string
          description: Display name with diacritics

    MaqamatListResponse:
      type: object
      required:
        - count
        - data
      properties:
        count:
          type: integer
          description: Total number of maqāmāt
          example: 60
        data:
          type: array
          items:
            $ref: '#/components/schemas/MaqamMetadata'

    MaqamMetadata:
      type: object
      required:
        - id
        - idName
        - displayName
        - version
        - familyId
        - familyDisplay
        - tonicId
        - tonicDisplay
        - numberOfPitchClasses
        - isOctaveRepeating
        - hasAsymmetricDescending
        - hasSuyur
        - availableInTuningSystems
        - tuningSystemsAvailability
        - maqamDetailsUrl
      properties:
        id:
          type: string
          description: Numeric ID
          example: "29"
        idName:
          type: string
          description: URL-safe identifier
          example: "maqam_yegah"
        displayName:
          type: string
          description: Human-readable name with diacritics
          example: "maqām yegāh"
        version:
          type: string
          description: Version timestamp (ISO 8601)
          example: "2025-10-18T19:41:17.132Z"
        familyId:
          type: string
          description: URL-safe family identifier
          example: "no_jins"
        familyDisplay:
          type: string
          description: Human-readable family name
          example: "no jins"
        tonicId:
          type: string
          description: URL-safe tonic name
          example: "yegah"
        tonicDisplay:
          type: string
          description: Tonic name with diacritics
          example: "yegāh"
        numberOfPitchClasses:
          type: integer
          description: Count of pitch classes in ascending sequence
          example: 7
        isOctaveRepeating:
          type: boolean
          description: True when numberOfPitchClasses ≤ 7
          example: true
        hasAsymmetricDescending:
          type: boolean
          description: True when descending sequence differs from ascending
          example: true
        hasSuyur:
          type: boolean
          description: True when suyūr (melodic paths) are documented
          example: true
        availableInTuningSystems:
          type: integer
          description: Count of tuning systems supporting this maqām
          example: 4
        tuningSystemsAvailability:
          type: array
          description: Tuning systems where maqām is available with their valid tuning system starting notes (simplified structure without transposition counts for list endpoint)
          items:
            type: object
            properties:
              tuningSystemId:
                type: string
                example: "alfarabi_950g"
              tuningSystemStartingNoteNames:
                type: array
                items:
                  type: string
                example: ["ʿushayrān"]
          example:
            - tuningSystemId: "alfarabi_950g"
              tuningSystemStartingNoteNames: ["ʿushayrān"]
            - tuningSystemId: "ibnsina_1037"
              tuningSystemStartingNoteNames: ["ʿushayrān", "yegāh"]
        maqamDetailsUrl:
          type: string
          description: Relative URL to maqām detail endpoint
          example: "/api/maqamat/maqam_yegah"
        sources:
          type: array
          description: Bibliographic source references for this maqām
          items:
            type: object
            properties:
              sourceId:
                type: string
                description: URL-safe source identifier
                example: "al-Shawwa-(1946)"
              page:
                type: string
                description: Page number or range where the maqām is documented
                example: "49"
          example:
            - sourceId: "al-Shawwa-(1946)"
              page: "49"

    TuningSystemAvailability:
      type: object
      required:
        - tuningSystemId
        - tuningSystemDisplayName
        - tuningSystemStartingNoteNames
        - tuningSystemStartingNoteNamesIds
      properties:
        tuningSystemId:
          type: string
          description: Tuning system identifier (e.g., 'ibnsina_1037', 'alfarabi_950g', 'meshshaqa_1899')
          example: "ibnsina_1037"
        tuningSystemDisplayName:
          type: string
          description: Display name formatted as "Creator (Year) Title" (e.g., "Ibn Sīnā (1037) 7-Fret Oud 17-Tone")
          example: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
        tuningSystemStartingNoteNames:
          type: array
          description: Tuning system starting note names where this maqām is available (display values with diacritics). These represent different theoretical frameworks, not simple transpositions.
          items:
            type: string
          example: ["ʿushayrān", "yegāh"]
        tuningSystemStartingNoteNamesIds:
          type: array
          description: URL-safe identifiers for tuning system starting note names (for use in API calls)
          items:
            type: string
          example: ["ushayran", "yegah"]

    MaqamDetailResponse:
      type: object
      required:
        - maqam
        - context
        - pitchData
        - ajnas
      properties:
        maqam:
          $ref: '#/components/schemas/MaqamMetadata'
        context:
          $ref: '#/components/schemas/MaqamContext'
        pitchData:
          $ref: '#/components/schemas/PitchData'
        ajnas:
          $ref: '#/components/schemas/AjnasData'
          description: Always included for maqām analysis
        primaryJins:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/JinsDegreeEntry'
          description: |
            Primary jins position(s) for the selected transposition. Array of
            per-degree entries; each entry carries the note name, maqām degree
            (roman numeral), and the resolved jins entity. Usually a
            single-element array; multi-valued when alternatives are recorded
            in the source data. `null` when the source has no primary.
        secondaryJins:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/JinsDegreeEntry'
          description: |
            Secondary jins position(s) — typically on the 5th or 4th degree.
            `null` when the maqām has no standard secondary jins, or for
            no_jins-family maqāmāt without a manual override.
        tertiaryJins:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/JinsDegreeEntry'
          description: |
            Tertiary jins position(s). Rare; `null` for most maqāmāt.
        ghammaz:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/GhammazEntry'
          description: |
            Ghammāz degree(s) — the note(s) most often used for modulation.
            Usually equals the secondaryJins position. Unlike the jins slots,
            entries only carry note name and maqām degree (the ghammāz is a
            melodic-emphasis note, not a jins). `null` when the source
            explicitly suppresses the ghammāz (e.g. maqām dilkeshīdah).
        availableTranspositions:
          type: array
          description: All available transpositions (taswīr) of this maqām in the current tuning system + starting note. Each entry carries its own transposed jins-degree and ghammāz fields.
          items:
            $ref: '#/components/schemas/AvailableTranspositionItem'
        intervals:
          $ref: '#/components/schemas/IntervalsData'
          description: Optional - included when includeIntervals=true
        modulations:
          $ref: '#/components/schemas/ModulationsData'
          description: Optional - included when includeModulations=true
        suyur:
          type: array
          description: Optional - included when includeSuyur=true
          items:
            $ref: '#/components/schemas/Sayr'

    GhammazEntry:
      type: object
      required:
        - noteName
        - noteNameDisplay
        - maqamDegree
      properties:
        noteName:
          type: string
          description: URL-safe note name (e.g. "nawa").
        noteNameDisplay:
          type: string
          description: Note name with diacritics (e.g. "nawā").
        noteNameDisplayAr:
          type: string
          description: Arabic-script note name (only when `includeArabic=true`).
        maqamDegree:
          type: string
          nullable: true
          description: Roman-numeral position of this note within the ascending scale of the selected transposition (e.g. "V"). `null` if the note cannot be located in the scale.
      description: |
        A single ghammāz position. Carries only note-name and maqām-degree
        fields — the ghammāz is a melodic-emphasis note, not a jins.

    JinsDegreeEntry:
      allOf:
        - $ref: '#/components/schemas/GhammazEntry'
        - type: object
          required:
            - jinsId
            - jinsIdName
            - jinsDisplayName
          properties:
            jinsId:
              type: string
              nullable: true
              description: Numeric id of the jins built on this degree. `null` if no matching jins is found.
            jinsIdName:
              type: string
              nullable: true
              description: URL-safe identifier for the jins (e.g. `jins_rast_al-nawa`). `null` if no matching jins is found.
            jinsDisplayName:
              type: string
              nullable: true
              description: Display name of the jins with diacritics (e.g. `jins rāst al-nawā`). `null` if no matching jins is found.
            jinsDisplayNameAr:
              type: string
              nullable: true
              description: Arabic-script jins display name (only when `includeArabic=true`).
      description: |
        A single jins position on a maqām degree. Combines a GhammazEntry
        (noteName, noteNameDisplay, maqamDegree) with the resolved jins
        identity (jinsId, jinsIdName, jinsDisplayName). The jins fields are
        nullable together — when the transposed note doesn't match any
        embedded jins in the ascending or descending ajnas analysis, all four
        jins-prefixed fields are `null` while the note and degree fields
        remain populated.

    AvailableTranspositionItem:
      type: object
      required:
        - idName
        - displayName
        - tonic
      properties:
        idName:
          type: string
          description: URL-safe identifier combining the maqām name and its transposition tonic.
        displayName:
          type: string
          description: Full transposed maqām name with diacritics (e.g. "maqām rāst al-nawā").
        displayNameAr:
          type: string
          description: Arabic display name (only when `includeArabic=true`).
        tonic:
          type: object
          required: [idName, displayName]
          properties:
            idName:
              type: string
            displayName:
              type: string
            displayNameAr:
              type: string
      description: |
        A single available transposition (taswīr) of a maqām within a given
        tuning system and starting note. Intentionally minimal — use the
        detail link (or GET /maqamat/{idName}?transposeTo=<tonic>) to get
        the fully-resolved jins-degree and ghammāz fields for a specific
        tonic, or use GET /maqamat/{idName}/transpositions for the same
        data across all tonics in one response.

    JinsDetailResponse:
      type: object
      required:
        - jins
        - context
        - pitchData
      properties:
        jins:
          type: object
          required:
            - id
            - idName
            - displayName
            - version
            - tonicId
            - tonicName
            - transposition
            - numberOfTranspositions
            - numberOfPitchClasses
            - pitchClassDataType
            - includeIntervals
          properties:
            id:
              type: string
              description: Numeric identifier for the jins
            idName:
              type: string
              description: URL-safe identifier for the jins
            displayName:
              type: string
              description: Display name with diacritics
            version:
              type: string
              description: ISO 8601 timestamp of last modification
            tonicId:
              type: string
              description: URL-safe tonic note identifier
            tonicName:
              type: string
              description: Tonic note name with diacritics
            transposition:
              type: boolean
              description: Whether this is a transposition from the original tonic
            originalTonicId:
              type: string
              description: Original tonic identifier (present only if transposed)
            originalTonicName:
              type: string
              description: Original tonic name (present only if transposed)
            transposedTonicId:
              type: string
              description: Transposed tonic identifier (present only if transposed)
            transposedTonicName:
              type: string
              description: Transposed tonic name (present only if transposed)
            numberOfTranspositions:
              type: integer
              description: Count of distinct transpositions (taswīr) only; excludes tahlīl and register/octave variants
            numberOfPitchClasses:
              type: integer
              description: Number of pitch classes in this jins
            commentsEnglish:
              type: string
              nullable: true
              description: English commentary
            commentsArabic:
              type: string
              nullable: true
              description: Arabic commentary
            sources:
              type: array
              description: Bibliographic source references
              items:
                type: object
                properties:
                  sourceId:
                    type: string
                  page:
                    type: string
            pitchClassDataType:
              type: string
              description: Format used for pitch class data
            includeIntervals:
              type: boolean
              description: Whether intervals are included
        context:
          $ref: '#/components/schemas/MaqamContext'
        pitchData:
          type: array
          description: Pitch class sequence (ajnās are unidirectional, not ascending/descending)
          items:
            $ref: '#/components/schemas/PitchClass'
        intervals:
          type: array
          description: Optional - included when includeIntervals=true. Interval data between consecutive pitch classes.
          items:
            type: object
            description: Interval data (fields vary by format parameter)

    SourceResponse:
      type: object
      description: |
        Comprehensive bibliographic source information. Includes common fields for all source types
        and type-specific fields based on sourceType (Book, Article, or Thesis). Arabic-language
        fields are suffixed with "Ar" and are returned only when the `includeArabic` parameter is true.
      required:
        - id
        - displayName
        - sourceType
        - title
        - contributors
        - edition
        - publicationDate
        - url
        - dateAccessed
        - version
      properties:
        id:
          type: string
          description: Unique identifier for the source
        displayName:
          type: string
          description: Formatted citation string (e.g., "Author (Year) Title")
        displayNameAr:
          type: string
          description: Arabic formatted citation string (present when includeArabic=true)
        sourceType:
          type: string
          enum: [Book, Article, Thesis]
          description: Type discriminator for the source
        title:
          type: string
          description: Source title in English/transliteration
        titleAr:
          type: string
          description: Source title in Arabic (present when includeArabic=true)
        contributors:
          type: array
          description: Array of contributors (authors, editors, translators, etc.)
          items:
            type: object
            properties:
              type:
                type: string
                description: Contributor role (e.g., "Author", "Editor", "Reviewer")
              firstName:
                type: string
                description: First name in English/transliteration
              lastName:
                type: string
                description: Last name in English/transliteration
              firstNameAr:
                type: string
                description: First name in Arabic (present when includeArabic=true)
              lastNameAr:
                type: string
                description: Last name in Arabic (present when includeArabic=true)
        edition:
          type: string
          description: Edition information in English/transliteration
        editionAr:
          type: string
          description: Edition information in Arabic (present when includeArabic=true)
        publicationDate:
          type: string
          description: Publication date (string, e.g., "2025" or ISO date)
        publicationDateAr:
          type: string
          description: Publication date in Arabic (present when includeArabic=true)
        url:
          type: string
          description: URL for online sources (empty string if not applicable)
        dateAccessed:
          type: string
          description: Date when online source was accessed (empty string if not applicable)
        version:
          type: string
          description: ISO 8601 timestamp of last modification
        # Book-specific fields (present only when sourceType is "Book")
        originalPublicationDate:
          type: string
          description: Original publication date (for reprints and editions)
        originalPublicationDateAr:
          type: string
          description: Original publication date in Arabic (present when includeArabic=true)
        publisher:
          type: string
          description: Publisher name
        publisherAr:
          type: string
          description: Publisher name in Arabic (present when includeArabic=true)
        place:
          type: string
          description: Publication place
        placeAr:
          type: string
          description: Publication place in Arabic (present when includeArabic=true)
        ISBN:
          type: string
          description: International Standard Book Number
        # Article-specific fields (present only when sourceType is "Article")
        journal:
          type: string
          description: Journal name
        journalAr:
          type: string
          description: Journal name in Arabic (present when includeArabic=true)
        volume:
          type: string
          description: Volume number
        volumeAr:
          type: string
          description: Volume number in Arabic (present when includeArabic=true)
        issue:
          type: string
          description: Issue number
        issueAr:
          type: string
          description: Issue number in Arabic (present when includeArabic=true)
        pageRange:
          type: string
          description: Page range (e.g., "45–67")
        pageRangeAr:
          type: string
          description: Page range in Arabic (present when includeArabic=true)
        DOI:
          type: string
          description: Digital Object Identifier
        # Thesis-specific fields (present only when sourceType is "Thesis")
        degreeType:
          type: string
          description: Type of degree (e.g., "Master's thesis", "PhD dissertation")
        degreeTypeAr:
          type: string
          description: Type of degree in Arabic (present when includeArabic=true)
        university:
          type: string
          description: University or institution name
        universityAr:
          type: string
          description: University or institution name in Arabic (present when includeArabic=true)
        department:
          type: string
          description: Department name
        departmentAr:
          type: string
          description: Department name in Arabic (present when includeArabic=true)
        databaseIdentifier:
          type: string
          description: Database identifier (e.g., ProQuest ID)
        databaseName:
          type: string
          description: Name of the database (e.g., "ProQuest", "PQDT")

    SourceDetailResponse:
      type: object
      required:
        - source
      properties:
        source:
          $ref: '#/components/schemas/SourceResponse'

    MaqamContext:
      type: object
      required:
        - tuningSystem
      properties:
        tuningSystem:
          $ref: '#/components/schemas/TuningSystemMetadata'

    TuningSystemMetadata:
      type: object
      required:
        - id
        - displayName
        - numberOfPitchClassesSingleOctave
        - tuningSystemStartingNoteNames
        - tuningSystemStartingNoteNamesIds
      properties:
        id:
          type: string
          description: URL-safe tuning system identifier
          example: "ibnsina_1037"
        displayName:
          type: string
          description: Human-readable name
          example: "Ibn Sīnā (1037) 7-Fret Oud 17-Tone"
        version:
          type: string
          description: Version timestamp (ISO 8601)
          example: "2025-10-18T19:42:23.643Z"
        year:
          type: string
          description: Year the tuning system was documented or created
          example: "1037"
        numberOfPitchClassesSingleOctave:
          type: integer
          description: Number of pitch classes per octave
          example: 17
        tuningSystemStartingNoteNames:
          type: array
          description: Available tuning system starting note names (first note in each note name set) with diacritics. These represent different theoretical traditions (ʿushayrān for oud tuning, yegāh for monochord measurements, rāst for pedagogical frameworks).
          items:
            type: string
          example: ["ʿushayrān", "yegāh"]
        tuningSystemStartingNoteNamesIds:
          type: array
          description: URL-safe identifiers for tuning system starting note names (for use in API calls)
          items:
            type: string
          example: ["ushayran", "yegah"]
        numberOfPitchClassesAllOctaves:
          type: integer
          description: Total pitch-class count across available octaves (used for non-octave-repeating maqāmāt)
          example: 51
        originalValueType:
          type: string
          description: The original numeric representation for pitch classes in this tuning system
          example: "fraction"
        startingNoteName:
          type: string
          description: The selected tuning system starting note for this context
          example: "ushayran"

    PitchData:
      type: object
      required:
        - ascending
        - descending
      properties:
        ascending:
          type: array
          description: Ascending scale pitch classes
          items:
            $ref: '#/components/schemas/PitchClass'
        descending:
          type: array
          description: Descending scale pitch classes
          items:
            $ref: '#/components/schemas/PitchClass'

    PitchClass:
      type: object
      description: Pitch class data (fields vary by format parameter)
      properties:
        pitchClassIndex:
          type: integer
          description: Index in tuning system
          example: 0
        octave:
          type: integer
          description: Octave number (0, 1, 2, or 3) indicating which octave this pitch class belongs to
          example: 1
        scaleDegree:
          type: string
          description: Roman numeral maqām degree
          example: "I"
        noteName:
          type: string
          description: URL-safe note name
          example: "ushayran"
        noteNameDisplay:
          type: string
          description: Note name with diacritics
          example: "ʿushayrān"
        englishName:
          type: string
          description: English transliteration
          example: "ushayran"
        abjadName:
          type: string
          description: Arabic script name
          example: "عشيران"
        frequency:
          type: number
          format: float
          description: Frequency in Hz (when pitchClassDataType=frequency or all)
          example: 97.999
        cents:
          type: number
          format: float
          description: Cents above reference (when pitchClassDataType=cents or all)
          example: 0.0
        fraction:
          type: string
          description: Interval fraction ratio (when pitchClassDataType=fraction or all)
          example: "1/1"
        decimalRatio:
          type: number
          format: float
          description: Decimal frequency ratio (when pitchClassDataType=decimalRatio or all)
          example: 1.0
        stringLength:
          type: number
          format: float
          description: Relative string length (when pitchClassDataType=stringLength or all)
          example: 1.0
        midiNoteDecimal:
          type: number
          format: float
          description: MIDI note with decimal cents (when format=midi or all)
          example: 38.0
        midiNotePlusCentsDeviation:
          type: string
          description: MIDI note ± cents deviation (when format=all)
          example: "D2 +0.0"
        centsDeviation:
          type: number
          format: float
          description: Cents deviation from equal temperament (when format=all)
          example: 0.0
        ipnReferenceNoteName:
          type: string
          description: IPN reference note (when format=all)
          example: "D2"

    EntityRef:
      type: object
      required: [id, idName, displayName]
      properties:
        id:
          type: string
          description: Numeric or canonical identifier
        idName:
          type: string
          description: URL-safe identifier
        displayName:
          type: string
          description: Human-readable name with diacritics and transposition suffixes when applicable

    IdentifierRef:
      type: object
      required: [idName, displayName]
      properties:
        id:
          type: string
          description: Optional numeric or canonical identifier
        idName:
          type: string
          description: URL-safe identifier
          example: "rast"
        displayName:
          type: string
          description: Human-readable name with diacritics
          example: "rāst"
        displayNameAr:
          type: string
          description: Arabic display name (present when includeArabic=true)
          example: "راست"

    Links:
      type: object
      description: Navigation links for related endpoints
      properties:
        self:
          type: string
          description: Current endpoint URL
        detail:
          type: string
          description: Link to detail endpoint
        availability:
          type: string
          description: Link to availability endpoint
        compare:
          type: string
          description: Link to compare endpoint
        intervals:
          type: string
          description: Link to intervals endpoint

    AjnasData:
      type: object
      required:
        - ascending
        - descending
      properties:
        ascending:
          type: object
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/EntityRef'
              - type: 'null'
          description: Note-name keyed map of ajnās present at each ascending maqām degree
          example:
            rast: { id: "3", idName: "jins_rast", displayName: "jins rāst" }
            dugah: null
        descending:
          type: object
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/EntityRef'
              - type: 'null'
          description: Note-name keyed map of ajnās present at each descending maqām degree
          example:
            kurd: { id: "9", idName: "jins_kurd", displayName: "jins kurd" }
            nahawand: { id: "4", idName: "jins_nahawand", displayName: "jins nahāwand" }
      description: |
        Ajnās present on each maqām degree, keyed by standardized note names. Values are full entity objects
        `{ id, idName, displayName }` when a jins is present, or `null` when no jins is assigned.

    IntervalsData:
      type: object
      properties:
        ascending:
          type: array
          description: Interval data between ascending maqām degrees
          items:
            type: object
            properties:
              fromDegree:
                type: string
              toDegree:
                type: string
              cents:
                type: number
              ratio:
                type: number
        descending:
          type: array
          description: Interval data between descending maqām degrees
          items:
            type: object

    ModulationsData:
      type: object
      description: |
        Modulation networks for the selected transposition. Both `maqamat`
        and `ajnas` are objects keyed by modulation-degree slot (not by note
        name). Values are arrays of candidate targets — each target is a
        full entity reference with its own tonic and a transposition flag
        (for maqamat modulations).
      required:
        - maqamat
        - ajnas
      properties:
        maqamat:
          $ref: '#/components/schemas/MaqamModulationsByDegree'
          description: Available maqām modulations keyed by modulation-degree slot.
        ajnas:
          $ref: '#/components/schemas/AjnasModulationsByDegree'
          description: Available jins modulations keyed by modulation-degree slot.

    MaqamModulationsByDegree:
      type: object
      required:
        - firstDegree
        - thirdDegree
        - altThirdDegree
        - fourthDegree
        - fifthDegree
        - sixthDegreeAsc
        - sixthDegreeDesc
        - sixthDegreeIfNoThird
      properties:
        firstDegree:
          type: array
          description: Modulations available on the first (tonic) degree.
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        thirdDegree:
          type: array
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        altThirdDegree:
          type: array
          description: Modulations on the alternate third degree (present in some modal systems).
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        fourthDegree:
          type: array
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        fifthDegree:
          type: array
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        sixthDegreeAsc:
          type: array
          description: Sixth-degree modulations in the ascending direction.
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        sixthDegreeDesc:
          type: array
          description: Sixth-degree modulations in the descending direction.
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        sixthDegreeIfNoThird:
          type: array
          description: Sixth-degree modulations that apply only when there is no third-degree modulation.
          items:
            $ref: '#/components/schemas/MaqamModulationEntity'
        noteName2pBelowThird:
          type: string
          nullable: true
          description: |
            Note name used as a secondary pivot two positions below the third
            degree. Present only for selected scale shapes; otherwise `null`
            or omitted.

    AjnasModulationsByDegree:
      type: object
      required:
        - firstDegree
        - thirdDegree
        - altThirdDegree
        - fourthDegree
        - fifthDegree
        - sixthDegreeAsc
        - sixthDegreeDesc
        - sixthDegreeIfNoThird
      properties:
        firstDegree:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        thirdDegree:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        altThirdDegree:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        fourthDegree:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        fifthDegree:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        sixthDegreeAsc:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        sixthDegreeDesc:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        sixthDegreeIfNoThird:
          type: array
          items:
            $ref: '#/components/schemas/JinsModulationEntity'
        noteName2pBelowThird:
          type: string
          nullable: true

    MaqamModulationEntity:
      type: object
      required:
        - id
        - idName
        - displayName
        - tonicIdName
        - tonicDisplayName
        - isTransposition
      properties:
        id:
          type: string
          description: Numeric identifier of the target maqām.
        idName:
          type: string
          description: URL-safe identifier (tahlīl or al-taswīr form).
        displayName:
          type: string
          description: Display name with diacritics.
        tonicIdName:
          type: string
          description: URL-safe tonic identifier on which this modulation resolves.
        tonicDisplayName:
          type: string
          description: Tonic display name with diacritics.
        isTransposition:
          type: boolean
          description: True when the target is a transposition (taṣwīr) of its base maqām, false when it is the tahlīl.

    JinsModulationEntity:
      type: object
      required:
        - id
        - idName
        - displayName
        - tonicIdName
        - tonicDisplayName
      properties:
        id:
          type: string
          description: Numeric identifier of the target jins.
        idName:
          type: string
        displayName:
          type: string
        tonicIdName:
          type: string
          description: URL-safe tonic identifier on which this jins modulation resolves.
        tonicDisplayName:
          type: string

    Sayr:
      type: object
      description: Melodic path (sayr) through the maqām
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        path:
          type: array
          items:
            type: string

    MaqamNode:
      type: object
      description: |
        Represents a node in the modulation graph - a unique (maqamId, tonic) combination.

        Use `maqamId` (stable numeric) or the pair `maqamIdName` + `tonicId` for
        identification. For transposed maqāmāt, `maqamIdName` includes the
        transposition suffix (e.g., "maqam_nahawand_al-kurdan").
      required:
        - maqamId
        - maqamIdName
        - maqamDisplayName
        - tonicId
        - tonicDisplay
        - isTransposition
      properties:
        maqamId:
          type: string
          description: Numeric ID of the maqam (stable across all transpositions)
          example: "1"
        maqamIdName:
          type: string
          description: |
            Identifier for this (maqam, tonic) variant. For non-transpositions
            this is the bare maqam id (e.g. "maqam_nahawand"); for taṣwīr forms
            it includes the tonic suffix (e.g. "maqam_nahawand_al-kurdan").
          example: "maqam_rast"
        maqamDisplayName:
          type: string
          description: Human-readable name (may include transposition info)
          example: "Maqam Rast"
        maqamDisplayNameAr:
          type: string
          description: Arabic display name (only when includeArabic=true)
          example: "مقام راست"
        tonicId:
          type: string
          description: URL-safe tonic identifier
          example: "rast"
        tonicDisplay:
          type: string
          description: Display name for the tonic note
          example: "rāst"
        tonicDisplayAr:
          type: string
          description: Arabic tonic name (only when includeArabic=true)
          example: "راست"
        isTransposition:
          type: boolean
          description: Whether this is a transposition (taṣwīr) or original form (taḥlīl)
          example: false

    ModulationStep:
      type: object
      description: A single step/edge in a modulation route
      required:
        - from
        - to
        - modulationDegree
        - modulationCategory
      properties:
        from:
          $ref: '#/components/schemas/MaqamNode'
        to:
          $ref: '#/components/schemas/MaqamNode'
        modulationDegree:
          type: string
          description: |
            Scale degree where modulation occurs. Roman numerals (`I`, `III`,
            `IV`, `V`, `VI`) correspond to al-Shawwā's ascending rules.
            `8va` / `8vb` appear on pure register-shift steps between
            register-equivalent siblings of the same maqām. Roman-numeral
            variants suffixed with `-8vb` (e.g. `I-8vb`, `V-8vb`, `VI-8vb`)
            label downward-modulation steps — a single-hop modulation to a
            different maqām whose tonic sits one octave below the
            corresponding ascending rule's target.
          enum: ["I", "III", "IV", "V", "VI", "8va", "8vb", "I-8vb", "III-8vb", "IV-8vb", "V-8vb", "VI-8vb"]
          example: "III"
        modulationCategory:
          type: string
          description: |
            Specific modulation category. The first eight values come from
            al-Shawwā's 1946 modulation rules. The two `octave*` values label
            a non-al-Shawwā step that moves between register-equivalent
            siblings of the same maqām (same modal tonic slot, different
            octave): `octaveAbove` shifts UP by one octave (paired with
            `modulationDegree: "8va"`); `octaveBelow` shifts DOWN by one
            octave (paired with `"8vb"`). These appear as regular graph
            edges, so BFS can chain them (e.g. muḥayyar → dūgāh → qarār
            dūgāh as two 8vb steps) and traverse them at any position in a
            path, not just at the end.

            The `*OctaveBelow` values are direct downward-modulation
            edges: a single-hop modulation to a different maqām at the
            lower-register sibling of the corresponding ascending rule's
            target. For example, `sixthDegreeAscOctaveBelow` / `VI-8vb`
            models saba:dūgāh → ajam ushayrān:ajam ushayrān as one hop
            instead of as a two-hop VI↑ + 8vb pair. Gated by the
            `allowDownwardModulation` query parameter.
          enum:
            - firstDegree
            - thirdDegree
            - altThirdDegree
            - fourthDegree
            - fifthDegree
            - sixthDegreeAsc
            - sixthDegreeDesc
            - sixthDegreeIfNoThird
            - octaveAbove
            - octaveBelow
            - firstDegreeOctaveBelow
            - thirdDegreeOctaveBelow
            - altThirdDegreeOctaveBelow
            - fourthDegreeOctaveBelow
            - fifthDegreeOctaveBelow
            - sixthDegreeAscOctaveBelow
            - sixthDegreeDescOctaveBelow
            - sixthDegreeIfNoThirdOctaveBelow
          example: "thirdDegree"

    ModulationRoute:
      type: object
      description: A complete route from source to target maqam
      required:
        - hops
        - steps
      properties:
        hops:
          type: integer
          description: Number of modulation steps
          example: 2
        steps:
          type: array
          description: Ordered sequence of modulation steps
          items:
            $ref: '#/components/schemas/ModulationStep'

    ModulationJourney:
      type: object
      description: |
        One outbound journey from source to target. When the caller sets
        `returnToStartingMaqam=true`, the response includes a SEPARATE
        top-level `possibleReturnRoutes` collection — returns are not nested
        inside each journey because every outbound lands on the same
        canonical target and pairing them individually would duplicate the
        same return list N times.
      required:
        - routeNumber
        - outboundRoute
      properties:
        routeNumber:
          type: integer
          description: |
            1-based index of this route within `totalPossibleRoutes.data`. Lets
            clients refer to a specific route unambiguously (e.g. "route 3").
          example: 1
        outboundRoute:
          $ref: '#/components/schemas/ModulationRoute'

    NumberedReturnRoute:
      type: object
      description: |
        A return route back to the starting maqām, carrying its own 1-based
        `routeNumber` within `possibleReturnRoutes.data`. Its `steps` may
        include `8va` / `8vb` (`octaveAbove` / `octaveBelow`) steps at any
        position — the graph carries register-shift edges between every pair
        of adjacent register-equivalent siblings, so BFS can cross octaves
        anywhere in the path (including chained 8vb/8va jumps to reach a
        tonic two or more octaves away from the nearest non-shift landing).
      required:
        - routeNumber
        - hops
        - steps
      properties:
        routeNumber:
          type: integer
          example: 1
        hops:
          type: integer
          example: 3
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ModulationStep'

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
          example: "Failed to retrieve maqāmāt"
        message:
          type: string
          description: Additional detailed message explaining the error (not always present)
          example: "An unexpected error occurred while processing the request"
        details:
          type: string
          description: Additional error details (for some error types, not always present)
          example: "Database connection failed"
        hint:
          type: string
          description: Helpful hint for resolving the error (not always present)
          example: "Please try again later or contact support"
        availabilityUrl:
          type: string
          description: URL to check availability (only for missing parameter errors, not always present)
          example: "/api/maqamat/{id}/availability"
        validOptions:
          type: array
          description: List of valid values (only for invalid parameter errors, not always present). These are display values (with diacritics) as returned by the tuning system.
          items:
            type: string
          example: ["ʿushayrān", "yegāh"]

  responses:
    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: "Failed to retrieve maqāmāt"

  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for authenticated requests (if implemented in future)

security: []  # Currently no authentication required
