# Design de uma API pública: as decisões por trás do nosso lançamento

> Por que escolhemos REST em vez de GraphQL, header em vez de query string, e quotas rolling em vez de calendário.

**Autor:** Camila Ribeiro — Editora de Operações de Campo  
**Publicado:** 2026-05-02  
**Atualizado:** 2026-05-02  
**URL:** https://quilometragem.com.br/blog/designing-a-public-api-for-mileage

**TL;DR:** Por que escolhemos REST em vez de GraphQL, header em vez de query string, e quotas rolling em vez de calendário.

- Nossa API é consumida por desenvolvedores de RH e fintech que normalmente integram cinco ou seis APIs por projeto.
- Api keys passadas em query string aparecem em logs de servidor, em referers de browser e em capturas de tela.
- Escolhemos rolling 30 dias em vez de "mês civil" porque calendário cria picos artificiais no início do mês.
- 1.000 requisições por janela cobre confortavelmente um protótipo, um hackathon ou uma equipe de 10 pessoas testando uma integração.
- O endpoint POST /receipt retorna um SHA-256 dos campos de entrada.

## A audiência define o estilo

Nossa API é consumida por desenvolvedores de RH e fintech que normalmente integram cinco ou seis APIs por projeto.[^irs-2025] Eles não querem aprender uma nova linguagem de query nem montar resolvers — querem cURL, fetch, requests. Por isso fomos com REST clássico: cinco endpoints, JSON simples, OpenAPI 3.1. Para o caso de uso (calcular distância e reembolso), GraphQL não acrescenta nada. Ele só adicionaria um cliente extra e uma curva de aprendizado.

## Autenticação no header, não na URL

Api keys passadas em query string aparecem em logs de servidor, em referers de browser e em capturas de tela. Movê-las para `X-API-Key` (com fallback para `Authorization: Bearer …`) elimina a maior parte desse vazamento sem complicar a vida do desenvolvedor — todo cliente HTTP suporta headers de primeira classe.

## Quotas rolling de 30 dias, não calendário

Escolhemos rolling 30 dias em vez de "mês civil" porque calendário cria picos artificiais no início do mês. Com janela rolling, o limite se distribui naturalmente. A janela é renovada quando a primeira requisição mais antiga sai dos últimos 30 dias — implementação simples no banco com um `usage_window_start` e contador.

## Free tier suficiente para validar valor

1.000 requisições por janela cobre confortavelmente um protótipo, um hackathon ou uma equipe de 10 pessoas testando uma integração. Acima disso esperamos conversa humana — quem está consumindo 5.000 req/mês geralmente já tem requisitos de SLA, IP allowlist ou rotação de chaves que merecem um plano partner customizado.

## Hash de integridade no recibo

O endpoint `POST /receipt` retorna um SHA-256 dos campos de entrada. Auditores podem recalcular o hash localmente e comparar — qualquer alteração no PDF é detectada instantaneamente. É a mesma técnica que bancos usam para extratos imutáveis.

## OpenAPI antes do código

Desenhamos a OpenAPI primeiro, geramos os types do TypeScript a partir dela, e só então escrevemos os handlers. Isso garante que a documentação nunca fica desatualizada — ela é o contrato.

## O que ficou de fora (por enquanto)

Webhooks, batch endpoints e suporte a multi-stop com otimização de rota. Cada um tem demanda real, mas adiciona complexidade. Vamos lançá-los conforme parceiros pedirem — e cada release vai aparecer no [changelog público](/api/changelog) com RSS para que ninguém precise nos perguntar.

## Fontes

- [IRS — Standard Mileage Rates for 2025](https://www.irs.gov/tax-professionals/standard-mileage-rates) — Internal Revenue Service (2026-04-28)
