{
  "openapi": "3.1.0",
  "info": {
    "title": "API Weblyrics",
    "version": "0.1.4",
    "summary": "Pesquisa no dicionário de acordes (instrumentoX) e no Universo WL.",
    "description": "A **API Weblyrics** expõe o dicionário de acordes — posições no braço para instrumentos de cordas dedilhadas (violão, guitarra, cavaquinho, viola caipira, etc.) — e o catálogo público do Universo.\n\n**instrumentoX:** combinação de um instrumento com uma afinação (ex.: violão 6 cordas; `tuning` array índice 0 = corda mais fina, ex. `E,B,G,D,A,E`). Campo na API: `instrument_x_id`.\n\nBusca por nome usa símbolos do catálogo (notação primária e alternativas de `chord_spec.notation`).\n\nUm único endpoint HTTP: `POST https://api.weblyrics.com.br/v1` com body JSON `{ call, data }`.\n\n**Autenticação:** header `x-api-key: wlk_…`. A chave **nunca** vai no body.\n\nOperações (`call`): instruments.list, instruments.get, chords.search, universe.songs.search, universe.artists.search. Cada variante do body está no oneOf de `ChordApiRequest`.\n\nDocumentação humana: https://weblyrics.com.br/developers",
    "contact": {
      "name": "Weblyrics",
      "url": "https://weblyrics.com.br/developers",
      "email": "contato@weblyrics.com.br"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.weblyrics.com.br",
      "description": "Produção"
    }
  ],
  "tags": [
    {
      "name": "API Weblyrics",
      "description": "Um único `POST /v1`; a operação é escolhida pelo campo `call` no body."
    }
  ],
  "paths": {
    "/v1": {
      "post": {
        "operationId": "weblyricsApiInvoke",
        "tags": [
          "API Weblyrics"
        ],
        "summary": "Invoca um call da API Weblyrics",
        "description": "Escolha o **exemplo** (ou o seletor de `call` no portal) correspondente à operação desejada — o body muda por `call`.\n\nEnvie `x-api-key: wlk_…` e body `{ \"call\": \"…\", \"data\": { … } }`.\n\nO request body é um **oneOf** por `call` (campos obrigatórios/opcionais em cada variante).\n\nResposta 200: `{ call, data, meta? }`. Erros: `{ error, message?, contact? }` (código string em `error`).",
        "security": [
          {
            "ApiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChordApiRequest"
              },
              "examples": {
                "instrumentsList": {
                  "summary": "instruments.list",
                  "description": "Listar instrumentoX (paginado).",
                  "value": {
                    "call": "instruments.list",
                    "data": {
                      "limit": 50
                    }
                  }
                },
                "instrumentsGet": {
                  "summary": "instruments.get",
                  "description": "Obter um instrumentoX pelo UUID.",
                  "value": {
                    "call": "instruments.get",
                    "data": {
                      "instrument_x_id": "11111111-1111-4111-8111-111111111111"
                    }
                  }
                },
                "chordsSearch": {
                  "summary": "chords.search — acorde Am",
                  "description": "Pesquisar no dicionário de acordes.",
                  "value": {
                    "call": "chords.search",
                    "data": {
                      "name": "Am",
                      "format": "json",
                      "limit": 20
                    }
                  }
                },
                "universeSongs": {
                  "summary": "universe.songs.search",
                  "description": "Pesquisar versões no Universo.",
                  "value": {
                    "call": "universe.songs.search",
                    "data": {
                      "q": "boemia",
                      "format": "json"
                    }
                  }
                },
                "universeArtists": {
                  "summary": "universe.artists.search",
                  "description": "Pesquisar artistas no Universo.",
                  "value": {
                    "call": "universe.artists.search",
                    "data": {
                      "name_prefix": "To"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sucesso. O formato de `data` depende do `call` e de `data.format`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChordApiSuccess"
                },
                "examples": {
                  "instrumentsList": {
                    "summary": "instruments.list",
                    "value": {
                      "call": "instruments.list",
                      "data": {
                        "items": [
                          {
                            "instrument_x_id": "11111111-1111-4111-8111-111111111111",
                            "instrument_type": "violão"
                          }
                        ]
                      },
                      "meta": {
                        "limit": 50,
                        "offset": 0
                      }
                    }
                  },
                  "instrumentsGet": {
                    "summary": "instruments.get",
                    "value": {
                      "call": "instruments.get",
                      "data": {
                        "instrument_x_id": "11111111-1111-4111-8111-111111111111",
                        "instrument_type": "violão",
                        "tuning": [
                          "E",
                          "B",
                          "G",
                          "D",
                          "A",
                          "E"
                        ]
                      }
                    }
                  },
                  "chordsSearch": {
                    "summary": "chords.search (json)",
                    "value": {
                      "call": "chords.search",
                      "data": {
                        "hits": [
                          {
                            "symbol": "Am",
                            "name": "Am",
                            "query_name": "Am",
                            "spelling": "canonical",
                            "notation_source": "primary",
                            "instrument_x_id": "11111111-1111-4111-8111-111111111111",
                            "string_count": 6,
                            "tuning": [
                              "E",
                              "B",
                              "G",
                              "D",
                              "A",
                              "E"
                            ],
                            "tuning_name": "Padrão"
                          }
                        ]
                      },
                      "meta": {
                        "limit": 20,
                        "offset": 0
                      }
                    }
                  },
                  "universeSongs": {
                    "summary": "universe.songs.search",
                    "value": {
                      "call": "universe.songs.search",
                      "data": {
                        "items": [
                          {
                            "title": "Boemia",
                            "slug": "boemia"
                          }
                        ]
                      },
                      "meta": {
                        "limit": 20,
                        "offset": 0
                      }
                    }
                  },
                  "universeArtists": {
                    "summary": "universe.artists.search",
                    "value": {
                      "call": "universe.artists.search",
                      "data": {
                        "items": [
                          {
                            "name": "Toquinho"
                          }
                        ]
                      },
                      "meta": {
                        "limit": 20,
                        "offset": 0
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`call`/`data` inválidos, JSON inválido, ou chave no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChordApiError"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente ou inválida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChordApiError"
                }
              }
            }
          },
          "404": {
            "description": "Recurso não encontrado (ex.: instruments.get).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChordApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit (IP, chave ou cota diária).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChordApiError"
                }
              }
            }
          }
        },
        "x-wl-calls": [
          "instruments.list",
          "instruments.get",
          "chords.search",
          "universe.songs.search",
          "universe.artists.search"
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Chave API Weblyrics (`wlk_…`). Única forma documentada para clientes: header `x-api-key`. Nunca enviar a chave no body."
      }
    },
    "schemas": {
      "ApiCall": {
        "type": "string",
        "enum": [
          "instruments.list",
          "instruments.get",
          "chords.search",
          "universe.songs.search",
          "universe.artists.search"
        ],
        "description": "Identificador versionado da operação (`call`)."
      },
      "ResponseFormat": {
        "type": "string",
        "enum": [
          "json",
          "txt",
          "markdown",
          "html"
        ],
        "default": "json",
        "description": "Formato da resposta. `json` (padrão) devolve objeto tipado; `txt` / `markdown` / `html` serializam o resultado."
      },
      "ChordInclude": {
        "type": "string",
        "enum": [
          "meta",
          "diagram_svg",
          "tablature"
        ],
        "description": "Item de `include` em chords.search."
      },
      "InstrumentsListData": {
        "type": "object",
        "title": "instruments.list — data",
        "additionalProperties": false,
        "description": "Todos os campos são opcionais. Sem filtros, lista instrumentoX do sistema com paginação.",
        "properties": {
          "instrument_type": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional. Filtra pelo tipo de instrumento (ex.: violão, guitarra). Cada item retornado é um instrumentoX."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Tamanho da página (opcional)."
          },
          "offset": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Deslocamento 0-based (opcional)."
          }
        }
      },
      "InstrumentsGetData": {
        "type": "object",
        "title": "instruments.get — data",
        "additionalProperties": false,
        "required": [
          "instrument_x_id"
        ],
        "description": "`instrument_x_id` é obrigatório.",
        "properties": {
          "instrument_x_id": {
            "type": "string",
            "format": "uuid",
            "description": "Obrigatório. UUID do instrumentoX (instrumento + afinação)."
          }
        }
      },
      "ChordsSearchData": {
        "type": "object",
        "title": "chords.search — data",
        "additionalProperties": false,
        "description": "Obrigatório: pelo menos um de `name`, `name_prefix`, `name_contains`, `frets` ou `instrument_x_id`. Consulta o dicionário de acordes (cordas dedilhadas). Os campos `name*` são texto digitado; o filtro aplica-se a `v_chord_catalog.symbol` (inclui `spelling` canonical/enharmonic e `notation_source` primary/alternative). Cada hit inclui `symbol`, `name`, `query_name`, `spelling`, `notation_source`, `tuning`, `tuning_name` e `string_count`; `include: meta` acrescenta `spec_group`/`spec_name`/`spec_formula`.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Símbolo exato a buscar em `symbol` (ex.: Am, C7, A#/C##). Aliases enarmônicos são tentados via @wl/chord-theory."
          },
          "name_prefix": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Prefixo de `v_chord_catalog.symbol`."
          },
          "name_contains": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Substring em `v_chord_catalog.symbol`."
          },
          "ignore_bass": {
            "type": "boolean",
            "default": false,
            "description": "Opcional. Ignora baixo slash: filtra `symbol` igual à raiz ou `raiz/%` (inclui enarmônicos)."
          },
          "frets": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": -1,
              "maximum": 24
            },
            "description": "Opcional (critério). Trastes por corda; índice 0 = corda mais fina (aguda). -1 = muda. Mesmo eixo que `tuning` do instrumentoX e `barre.from_string`/`to_string`."
          },
          "fingers": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": -1,
              "maximum": 4
            },
            "description": "Opcional. Dedos associados aos trastes (mesma ordem: 0 = mais fina)."
          },
          "instrument_x_id": {
            "type": "string",
            "format": "uuid",
            "description": "Opcional (critério ou filtro). Restringe ao instrumentoX indicado."
          },
          "instrument_type": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional. Filtra por tipo de instrumento."
          },
          "min_base_fret": {
            "type": "integer",
            "minimum": 0,
            "maximum": 24,
            "description": "Opcional. Traste base mínimo da posição."
          },
          "priority_only": {
            "type": "boolean",
            "default": false,
            "description": "Opcional. Só posições prioritárias do dicionário."
          },
          "include": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "meta",
                "diagram_svg",
                "tablature"
              ]
            },
            "description": "Partes extras em `chords.search`: metadados, diagrama SVG e/ou tablatura ASCII."
          },
          "hand": {
            "type": "string",
            "enum": [
              "left",
              "right"
            ],
            "description": "Opcional. Orientação destro/canhoto do diagrama."
          },
          "finger_numbers": {
            "type": "boolean",
            "description": "Opcional. Numeração de dedos no SVG."
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "txt",
              "markdown",
              "html"
            ],
            "default": "json",
            "description": "Formato da resposta. `json` (padrão) devolve objeto tipado; `txt` / `markdown` / `html` serializam o resultado."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Tamanho da página (opcional)."
          },
          "offset": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Deslocamento 0-based (opcional)."
          }
        }
      },
      "UniverseSongsSearchData": {
        "type": "object",
        "title": "universe.songs.search — data",
        "additionalProperties": false,
        "description": "Obrigatório: pelo menos um de `q`, `title`, `artist`, `document_id`, `slug`, `canonical_slug` ou `canonical_entity_id`. Pesquisa versões publicadas no Universo.",
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Busca textual geral."
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Título da composição/versão."
          },
          "artist": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Nome de artista."
          },
          "document_id": {
            "type": "string",
            "format": "uuid",
            "description": "Opcional (critério). UUID da versão (documento)."
          },
          "slug": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Slug canônico da composição."
          },
          "canonical_slug": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Alias de `slug`."
          },
          "canonical_entity_id": {
            "type": "string",
            "format": "uuid",
            "description": "Opcional (critério). UUID da entidade canônica."
          },
          "instrument_x_id": {
            "type": "string",
            "format": "uuid",
            "description": "Opcional. Filtra pelo instrumentoX da versão."
          },
          "key": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional. Tonalidade."
          },
          "tonality": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional. Alias de `key`."
          },
          "min_likes": {
            "type": "integer",
            "minimum": 0,
            "description": "Opcional. Mínimo de curtidas da versão."
          },
          "author_system_only": {
            "type": "boolean",
            "description": "Opcional. Só versões do curador sistema."
          },
          "published_after": {
            "type": "string",
            "format": "date-time"
          },
          "published_before": {
            "type": "string",
            "format": "date-time"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "txt",
              "markdown",
              "html"
            ],
            "default": "json",
            "description": "Formato da resposta. `json` (padrão) devolve objeto tipado; `txt` / `markdown` / `html` serializam o resultado."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Tamanho da página (opcional)."
          },
          "offset": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Deslocamento 0-based (opcional)."
          }
        }
      },
      "UniverseArtistsSearchData": {
        "type": "object",
        "title": "universe.artists.search — data",
        "additionalProperties": false,
        "description": "Obrigatório: pelo menos um de `q`, `name` ou `name_prefix`. Pesquisa artistas ligados a publicações no Universo.",
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Busca textual."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Nome do artista."
          },
          "name_prefix": {
            "type": "string",
            "minLength": 1,
            "description": "Opcional (critério). Prefixo do nome."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Tamanho da página (opcional)."
          },
          "offset": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Deslocamento 0-based (opcional)."
          }
        }
      },
      "ChordApiRequest": {
        "oneOf": [
          {
            "type": "object",
            "title": "instruments.list — listar instrumentoX",
            "additionalProperties": false,
            "required": [
              "call",
              "data"
            ],
            "description": "Body para a operação `instruments.list`.",
            "properties": {
              "call": {
                "type": "string",
                "const": "instruments.list",
                "description": "Deve ser exatamente `instruments.list`."
              },
              "data": {
                "$ref": "#/components/schemas/InstrumentsListData"
              }
            }
          },
          {
            "type": "object",
            "title": "instruments.get — obter instrumentoX",
            "additionalProperties": false,
            "required": [
              "call",
              "data"
            ],
            "description": "Body para a operação `instruments.get`.",
            "properties": {
              "call": {
                "type": "string",
                "const": "instruments.get",
                "description": "Deve ser exatamente `instruments.get`."
              },
              "data": {
                "$ref": "#/components/schemas/InstrumentsGetData"
              }
            }
          },
          {
            "type": "object",
            "title": "chords.search — pesquisar acordes",
            "additionalProperties": false,
            "required": [
              "call",
              "data"
            ],
            "description": "Body para a operação `chords.search`.",
            "properties": {
              "call": {
                "type": "string",
                "const": "chords.search",
                "description": "Deve ser exatamente `chords.search`."
              },
              "data": {
                "$ref": "#/components/schemas/ChordsSearchData"
              }
            }
          },
          {
            "type": "object",
            "title": "universe.songs.search — pesquisar Universo",
            "additionalProperties": false,
            "required": [
              "call",
              "data"
            ],
            "description": "Body para a operação `universe.songs.search`.",
            "properties": {
              "call": {
                "type": "string",
                "const": "universe.songs.search",
                "description": "Deve ser exatamente `universe.songs.search`."
              },
              "data": {
                "$ref": "#/components/schemas/UniverseSongsSearchData"
              }
            }
          },
          {
            "type": "object",
            "title": "universe.artists.search — pesquisar artistas",
            "additionalProperties": false,
            "required": [
              "call",
              "data"
            ],
            "description": "Body para a operação `universe.artists.search`.",
            "properties": {
              "call": {
                "type": "string",
                "const": "universe.artists.search",
                "description": "Deve ser exatamente `universe.artists.search`."
              },
              "data": {
                "$ref": "#/components/schemas/UniverseArtistsSearchData"
              }
            }
          }
        ]
      },
      "ChordApiSuccess": {
        "type": "object",
        "title": "Resposta de sucesso",
        "required": [
          "call",
          "data"
        ],
        "additionalProperties": false,
        "description": "Envelope comum. `data` depende do `call` e do `format` pedido (json estruturado ou payload serializado).",
        "properties": {
          "call": {
            "type": "string",
            "enum": [
              "instruments.list",
              "instruments.get",
              "chords.search",
              "universe.songs.search",
              "universe.artists.search"
            ],
            "description": "Operação executada."
          },
          "data": {
            "description": "Resultado tipado por `call`. Em JSON típico: lista/`items`/`hits` + paginação no `meta`. Em txt/markdown/html o handler pode devolver campos como `content` / `content_type`.",
            "type": "object",
            "additionalProperties": true
          },
          "meta": {
            "type": "object",
            "additionalProperties": true,
            "description": "Metadados opcionais (paginação, cotas restantes, etc.)."
          }
        }
      },
      "ChordApiError": {
        "type": "object",
        "title": "Resposta de erro",
        "required": [
          "error"
        ],
        "additionalProperties": true,
        "properties": {
          "error": {
            "type": "string",
            "description": "Código do erro.",
            "examples": [
              "missing_or_invalid_api_key",
              "invalid_api_key",
              "unknown_call",
              "invalid_json",
              "rate_limited_ip",
              "rate_limited_key",
              "rate_limited_day",
              "not_found"
            ]
          },
          "message": {
            "type": "string",
            "description": "Mensagem legível (quando presente)."
          },
          "contact": {
            "type": "string",
            "description": "Contato de suporte (quando presente).",
            "examples": [
              "contato@weblyrics.com.br"
            ]
          },
          "known_calls": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Presente em `unknown_call`."
          },
          "details": {
            "type": "object",
            "additionalProperties": true,
            "description": "Detalhes opcionais de validação."
          }
        }
      }
    }
  }
}
