openapi: 3.1.0
info:
  title: OpenMusicMetadata API
  version: 1.0.0
  description: Canonical metadata for OpenMusic songs, albums, and artists.
  license:
    name: AGPL-3.0

servers:
  - url: https://api.vleer.app/metadata/v1
    description: Production server

tags:
  - name: Info
  - name: Lookup
  - name: Match

paths:
  /:
    get:
      tags: [Info]
      summary: Get info
      operationId: getInfo
      responses:
        "200":
          description: Info
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Info"
        default:
          $ref: "#/components/responses/Error"

  /lookup:
    get:
      tags: [Lookup]
      summary: Look up metadata resources
      operationId: lookupResources
      description: Returns matching resources. Use exactly one of ids, isrc, or upc. ids returns resources in request order and omits missing ids from data.
      parameters:
        - $ref: "#/components/parameters/LookupIds"
        - $ref: "#/components/parameters/LookupIsrc"
        - $ref: "#/components/parameters/LookupUpc"
        - $ref: "#/components/parameters/Include"
      responses:
        "200":
          description: Resource list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceList"
        default:
          $ref: "#/components/responses/Error"

  /lookup/{id}:
    get:
      tags: [Lookup]
      summary: Look up one metadata resource
      operationId: lookupResource
      parameters:
        - $ref: "#/components/parameters/ResourceId"
        - $ref: "#/components/parameters/Include"
      responses:
        "200":
          description: Resource
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceSingle"
        default:
          $ref: "#/components/responses/Error"

  /match/{type}:
    get:
      tags: [Match]
      summary: Match one metadata resource
      operationId: matchResource
      description: Matches provided metadata to one canonical OpenMusicMetadata resource. Songs can use name, artist, and album. Albums can use name and artist. Artists use name.
      parameters:
        - name: type
          in: path
          required: true
          schema:
            type: string
            enum: [song, album, artist]
        - name: name
          in: query
          required: true
          description: Resource name.
          schema:
            type: string
        - name: album
          in: query
          required: false
          description: Album name. Used for song matching.
          schema:
            type: string
        - name: artist
          in: query
          required: false
          description: Artist name. Used for song and album matching.
          schema:
            type: string
        - $ref: "#/components/parameters/Include"
      responses:
        "200":
          description: Matched resource
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceSingle"
        default:
          $ref: "#/components/responses/Error"

components:
  responses:
    Error:
      description: Error response
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

  parameters:
    ResourceId:
      name: id
      in: path
      required: true
      description: OpenMusicMetadata id.
      schema:
        $ref: "#/components/schemas/Id"

    LookupIds:
      name: ids
      in: query
      required: false
      description: Comma-separated OpenMusicMetadata ids. Maximum 100 lookup values. Cannot be combined with isrc or upc.
      schema:
        type: string
      example: omm:song:1,omm:album:2,omm:artist:3

    LookupIsrc:
      name: isrc
      in: query
      required: false
      description: Song ISRC values. Maximum 100 lookup values. Cannot be combined with ids or upc.
      schema:
        type: string
      example: USRC17607839

    LookupUpc:
      name: upc
      in: query
      required: false
      description: Album UPC values. Maximum 100 lookup values. Cannot be combined with ids or isrc.
      schema:
        type: string
      example: "602455878540"

    Include:
      name: include
      in: query
      required: false
      description: Comma-separated relationships to include. Includes are limited to one relationship level. Songs support albums and artists. Albums support artists.
      schema:
        type: string
      examples:
        song:
          value: albums,artists
        album:
          value: artists

  schemas:
    Info:
      type: object
      required: [stats]
      properties:
        stats:
          type: object
          required: [songs, albums, artists]
          properties:
            songs:
              type: integer
              minimum: 0
            albums:
              type: integer
              minimum: 0
            artists:
              type: integer
              minimum: 0

    ResourceType:
      type: string
      enum: [song, album, artist]

    Id:
      type: string
      description: OpenMusicMetadata id.
      examples:
        - omm:song:1
        - omm:album:2
        - omm:artist:3

    Resource:
      type: object
      required: [id, type]
      properties:
        id:
          $ref: "#/components/schemas/Id"
        type:
          $ref: "#/components/schemas/ResourceType"

    ArtworkUrl:
      type: string
      description: Artwork URL. It may contain {w} and {h} placeholders for client-selected width and height.

    Song:
      allOf:
        - $ref: "#/components/schemas/Resource"
        - type: object
          required: [type, attributes]
          properties:
            type:
              const: song
            attributes:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                albumName:
                  type: string
                artistName:
                  type: string
                isrc:
                  type: string
                artworkUrl:
                  $ref: "#/components/schemas/ArtworkUrl"
                trackNumber:
                  type: integer
                  minimum: 1
                discNumber:
                  type: integer
                  minimum: 1
                genres:
                  type: array
                  items:
                    type: string
                releaseDate:
                  type: string
                  description: Date string in YYYY, YYYY-MM, or YYYY-MM-DD form.
                durationMs:
                  type: integer
                  minimum: 0
            relationships:
              type: object
              properties:
                albums:
                  $ref: "#/components/schemas/AlbumList"
                artists:
                  $ref: "#/components/schemas/ArtistList"

    Album:
      allOf:
        - $ref: "#/components/schemas/Resource"
        - type: object
          required: [type, attributes]
          properties:
            type:
              const: album
            attributes:
              type: object
              required: [name, trackCount]
              properties:
                name:
                  type: string
                trackCount:
                  type: integer
                  minimum: 0
                artistName:
                  type: string
                artworkUrl:
                  $ref: "#/components/schemas/ArtworkUrl"
                upc:
                  type: string
                genres:
                  type: array
                  items:
                    type: string
                releaseDate:
                  type: string
                  description: Date string in YYYY, YYYY-MM, or YYYY-MM-DD form.
            relationships:
              type: object
              properties:
                artists:
                  $ref: "#/components/schemas/ArtistList"

    Artist:
      allOf:
        - $ref: "#/components/schemas/Resource"
        - type: object
          required: [type, attributes]
          properties:
            type:
              const: artist
            attributes:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                artworkUrl:
                  $ref: "#/components/schemas/ArtworkUrl"

    ResourceSingle:
      type: object
      required: [data]
      properties:
        data:
          oneOf:
            - $ref: "#/components/schemas/Song"
            - $ref: "#/components/schemas/Album"
            - $ref: "#/components/schemas/Artist"

    SongList:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Song"

    AlbumList:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Album"

    ArtistList:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Artist"

    ResourceList:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            oneOf:
              - $ref: "#/components/schemas/Song"
              - $ref: "#/components/schemas/Album"
              - $ref: "#/components/schemas/Artist"

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [status, message]
          properties:
            status:
              type: integer
              minimum: 100
              maximum: 599
            message:
              type: string
            details:
              type: array
              items:
                $ref: "#/components/schemas/ErrorDetail"
            requestId:
              type: string

    ErrorDetail:
      type: object
      required: [message]
      properties:
        message:
          type: string
        param:
          type: string
        value: {}
