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.
A forma do erro
Seção intitulada “A forma do erro”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.
Erro de validação: campo: mensagem
Seção intitulada “Erro de validação: campo: mensagem”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.
O 500 é sempre genérico
Seção intitulada “O 500 é sempre genérico”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.
Os status do lado do parceiro
Seção intitulada “Os status do lado do parceiro”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.
| Status | Significado para o parceiro | O que fazer |
|---|---|---|
| 400 | Payload malformado ou regra de negócio recusou (estoque, valor, formato). | Corrija o corpo; não repita igual. Em validação, leia campo: mensagem. |
| 401 | Assinatura 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). |
| 403 | Autenticado, 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. |
| 404 | O 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. |
| 409 | Conflito 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. |
| 5xx | Falha nossa. A message é genérica. | Retry com backoff. Não altere o payload por causa disso. |
Idempotência e o 401 de envelope repetido
Seção intitulada “Idempotência e o 401 de envelope repetido”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.