OpenAPI é um formato para descrever uma API HTTP em um arquivo YAML ou JSON comum. Nele ficam listados os caminhos que a API expõe, os métodos que cada um aceita, os campos que seguem na requisição e o que a resposta devolve. O arquivo é legível tanto por pessoas quanto por programas, e é dessa mesma fonte que saem a documentação, as bibliotecas cliente e os simuladores usados em testes.

O nome formal é OpenAPI Specification, abreviado como OAS. Quem a mantém é a OpenAPI Initiative, um projeto de governança aberta sob a Linux Foundation.

A seguir: como o documento é montado, em que OpenAPI difere de Swagger, o que mudou nas versões 3.1 e 3.2 e por que a promessa central do formato — documentação que nunca fica desatualizada — não se cumpre sozinha.

Como é um documento OpenAPI

Um arquivo que funciona é mais curto do que a maioria imagina:

openapi: 3.1.0

info:
  title: Links
  version: "1.0"

paths:
  /links:
    post:
      summary: Criar link curto
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
      responses:
        '201':
          description: Link criado

Só dois campos de nível superior são obrigatórios, openapi e info, e o documento precisa conter pelo menos um entre paths, components e webhooks. É assim que a especificação OAS 3.1.1 define.

Já nessas primeiras linhas mora uma armadilha: openapi e info.version são coisas diferentes. O primeiro é a versão do próprio formato, que as ferramentas leem para saber como interpretar o documento. O segundo é a versão da sua API. A especificação diz isso de forma explícita, mas a intuição sugere o contrário — e é assim que surgem arquivos declarando openapi: 1.0.

Especificações maiores costumam ser divididas em vários arquivos com $ref: o documento raiz aponta para ./paths/links.yaml, que aponta para ./schemas/link.yaml. É prático e gera um problema ao qual volto no final.

OpenAPI e Swagger não são a mesma coisa

É a confusão mais comum do assunto.

O que é Quem mantém
OpenAPI o padrão, o formato em si OpenAPI Initiative, Linux Foundation
Swagger um conjunto de ferramentas que leem esse formato SmartBear

A história explica a bagunça. No começo, Swagger designava ao mesmo tempo a especificação e as ferramentas. Depois a especificação passou para a OpenAPI Initiative e virou OpenAPI Specification, enquanto o nome Swagger ficou com as ferramentas — Swagger UI, Swagger Editor e as demais.

Na prática, "nós usamos Swagger" quase sempre significa "temos um arquivo OpenAPI e o exibimos com o Swagger UI". Nada prende uma coisa à outra: o mesmo arquivo abre igualmente bem no Redoc, no Scalar ou em um gerador de clientes.

Versões: 3.0, 3.1 e 3.2

As diferenças entre os ramos não são cosméticas.

3.0 continua sendo a mais difundida. Seu ponto fraco são os esquemas de dados: parecem JSON Schema sem serem totalmente compatíveis com ele, o que atrapalha a interoperabilidade entre ferramentas repetidamente.

3.1 fechou essa lacuna. O objeto Schema passou a ser um superconjunto do JSON Schema Draft 2020-12, então os esquemas que você já mantém em JSON Schema podem ser reaproveitados em vez de reescritos. A revisão atual, 3.1.1, foi publicada em 24 de outubro de 2024.

3.2 chegou em setembro de 2025. Pelo anúncio da OpenAPI Initiative, o que mais se destaca:

  • o método HTTP query, para leituras idempotentes cujos parâmetros não cabem na URL;
  • additionalOperations, para métodos fora do padrão;
  • tipos de mídia em streaming: Server-Sent Events, JSON Lines, sequências JSON;
  • tags hierárquicas, com summary, parent e kind;
  • fluxo de dispositivo do OAuth 2.0, para aparelhos com entrada desconfortável.

Vale escolher a versão que suas ferramentas realmente suportam, e não o número mais alto. O suporte a 3.1 no ecossistema é sólido; a 3.2 é mais recente e parte das ferramentas ainda está se adaptando. Nossa própria especificação declara 3.1.0.

O que a especificação entrega na prática

Um único arquivo resolve várias coisas ao mesmo tempo:

  • documentação interativa — Swagger UI ou Redoc montam a página direto do arquivo, com formulário para testar requisições;
  • bibliotecas cliente — geradores produzem um SDK na sua linguagem, dispensando invólucros escritos à mão;
  • simuladores — um servidor simulado sobe a partir da descrição antes de o backend existir, e o front não fica parado;
  • testes de contrato — os testes comparam respostas reais com os esquemas e flagram o momento em que uma resposta deixa de bater com a descrição.

É dessa lista que sai o principal argumento a favor do OpenAPI: a documentação deixa de ser um texto à parte que alguém esquece de atualizar.

O argumento está correto. Ele só não se executa sozinho.

Nada liga a especificação ao código

Esta é a parte que os textos de "o que é OpenAPI" quase nunca mencionam, e a que morde em produção.

O arquivo de especificação é só um arquivo. O roteador não sabe que ele existe. O controlador também não. Você pode apagar um endpoint, acrescentar outro ou transformar um campo obrigatório em opcional — e nenhum teste vai falhar. A documentação não se sincroniza sozinha: ela continua correta exatamente enquanto alguém lembrar de editá-la.

O desencontro acontece nos dois sentidos, e cada um prejudica de um jeito:

  • documentado, mas não funciona. A pessoa lê a documentação, envia a requisição e recebe um 404. A confiança no resto da documentação desaba na hora — se aqui mentiram, onde mais?
  • funciona, mas não está documentado. Para quem procura, o recurso simplesmente não existe. Ninguém encontra, ninguém paga por ele.

O segundo caso é o mais perigoso, porque ninguém reclama. Nem há quem reclame: o usuário não sabe que aquele endpoint está lá.

Foi exatamente o que aconteceu conosco. A API da Lix.li tinha três endpoints de conversões funcionando — uma listagem de eventos mais o recebimento de postbacks, individual e em lote — e nenhum deles aparecia na especificação. O código rodava, os testes passavam, e enquanto isso a página da API exibia dois caminhos completamente diferentes que não existiam no código. Isso apareceu em uma conferência geral, não por um relato de erro.

Como detectar o desencontro automaticamente

Não se resolve com disciplina, e sim com um teste. A ideia é direta: pegar a lista de caminhos da especificação, pegar a lista de rotas do roteador e comparar os dois conjuntos nos dois sentidos.

caminhos do openapi.yaml   →   existe essa rota?        →   se não, a documentação mente
rotas do roteador          →   o caminho está descrito? →   se não, o recurso é invisível

A metade "o que está documentado funciona" se verifica melhor com uma requisição real: chamar o endpoint sem credenciais e conferir que a resposta é 401, e não 404. A distinção importa. 401 quer dizer que a rota foi encontrada e pede chave; 404, que não existe esse caminho. Nada é gravado no processo, porque a autenticação roda antes do controlador.

A verificação inversa lê a configuração de rotas. Ela ainda obriga a listar as exceções de forma explícita — endpoints internos que não têm lugar em um contrato público. Essa lista já vale por si: faz você decidir uma vez, deliberadamente, o que considera a sua API pública.

Escrevemos esse teste depois do episódio das conversões. Ele se justificou de imediato: se as conversões forem removidas da especificação de novo, o teste falha e nomeia os dois caminhos ausentes.

Duas armadilhas que se descobrem tarde

$ref e caminhos relativos. Quando a especificação está dividida em arquivos, as referências internas são resolvidas em relação à URL de onde o arquivo foi servido. Um documento raiz disponível em /openapi vai procurar ./paths/links.yaml em /paths/links.yaml e não encontrar nada. No navegador tudo parece certo, porque o Swagger UI carrega o arquivo do local real dele — mas um gerador de clientes apontado para o mesmo documento vai tropeçar. Ou você serve a especificação apenas do diretório dela, ou a empacota em um arquivo único sem $ref externos.

O Swagger UI é vazio para um rastreador. Uma página de Swagger UI são poucas linhas de marcação mais um script que desenha o conteúdo no navegador. No HTML de origem não há um único caminho de endpoint. Para quem lê, a página está boa; para indexação, está em branco. Some a isso um erro frequente no robots.txt: uma regra Disallow: /api/, escrita para esconder os endpoints JSON, esconde junto a documentação para humanos que mora no mesmo prefixo. O certo é bloquear o caminho exato com a versão — /api/1.0/, por exemplo — e não a seção inteira.

Por onde começar

Se a API já existe e a descrição não, a ordem é mais ou menos esta:

  1. Descreva um endpoint à mão. Isso basta para entender a estrutura.
  2. Escolha a versão que suas ferramentas suportam com folga; por padrão, a 3.1.
  3. Divida o arquivo com $ref quando ele deixar de caber em uma tela, e defina na hora de qual URL ele é servido.
  4. Coloque um teste que compare a especificação com o roteador nos dois sentidos. Antes desse passo, qualquer afirmação sobre documentação em dia é promessa, não propriedade.

Para ver como isso fica em uma API real, a documentação da API da Lix.li cobre links, grupos, testes A/B e conversões. O serviço construído em volta dessa API está na página da API de encurtamento de links. Em temas vizinhos ficam o que são webhooks, a análise dos redirecionamentos 301, 302, 307 e 308 e o SDK de PHP para integrar por código.