Pular para o conteúdo

Erros e status HTTP

O contrato tem uma forma de erro só, e ela é deliberadamente pequena. Entender essa forma e o significado de cada status HTTP é o que separa um cliente que reage certo de um que trava, exibe a mensagem errada ou repete uma chamada que nunca vai passar. Esta página é o mapa.

Sucesso com corpo vem embrulhado em data. Erro não usa envelope — é um objeto único, na raiz:

{ "message": "Credenciais inválidas." }

Só isso: message: string. Não existe code, não existe errors[], não existe details. Qualquer lógica que você queira construir sobre “que tipo de erro é este” sai do HTTP status, nunca do texto. A message vem em pt-BR e pronta para exibir — não precisa ser traduzida nem reescrita no cliente; jogue-a direto na tela quando for o caso.

Quando o 400 vem de validação de payload, a message chega no formato campo: mensagem, por exemplo:

password: A senha deve conter ao menos um número.

Isso permite associar o erro ao campo certo do formulário: quebre na primeira :, use a parte da esquerda como nome do campo e a da direita como o recado a exibir. É a única estrutura que a message carrega, e ainda assim ela é para exibir, não para ramificar lógica de negócio.

Em 500 a message é sempre genérica, de propósito: o detalhe fica no log do servidor, não na resposta. Não tente extrair causa de um 500 pelo texto — não há nada ali além do aviso genérico. Um 5xx é um problema do nosso lado; a reação certa é retry com backoff, não corrigir o payload.

Estes são os significados que valem para as rotas de integração do servidor. Alguns status são ambíguos por segurança — o 404, por exemplo, não distingue “não existe” de “não é seu”, justamente para não virar oráculo de enumeração.

StatusSignificado para o parceiroO que fazer
400Payload malformado ou regra de negócio recusou (estoque, valor, formato).Corrija o corpo; não repita igual. Em validação, leia campo: mensagem.
401Assinatura ausente, inválida ou expirada — ou envelope repetido num modo em que o jti é nonce. Os dois casos são indistinguíveis.Refaça a assinatura com iat/exp frescos e um jti novo. Confira o relógio (NTP).
403Autenticado, mas fora do escopo — a credencial não alcança aquele recurso.Não repita; a credencial não muda o veredito. Revise qual servidor/entidade está chamando.
404O recurso não existe ou não é seu. Indistinguíveis, de propósito.Trate como “não acessível”. Não deduza existência a partir do 404.
409Conflito de estado — a operação colide com o estado atual (ex.: itemRef repetido, cadastro já vigente).Releia o estado atual antes de reagir; o recurso provavelmente já está como você queria.
5xxFalha nossa. A message é genérica.Retry com backoff. Não altere o payload por causa disso.

Vale reforçar um ponto que confunde: nas rotas service (onde o jti é nonce), reenviar um envelope já processado responde 401 "Requisição já processada." — o mesmo status de uma assinatura inválida. É esperado, e é por isso que cada tentativa nas rotas service precisa de um jti novo. Já nas rotas signature-only (criar transação, registrar anúncio), o jti é chave de idempotência: reusar o mesmo envelope devolve o mesmo resultado, sem erro — o que torna seguro o client do jogo reenviar uma oferta legítima num retry de rede, sem risco de gerar um segundo anúncio ou uma segunda transação.