Idempotency key em API REST para POSTs seguros

Idempotency key em API REST para POSTs seguros

Um timeout depois de um `POST` cria uma dúvida operacional que não se resolve apenas com um novo envio: a operação falhou ou a resposta se perdeu no caminho? Em integrações que consultam, registram ou solicitam dados veiculares em escala, repetir a chamada sem critério pode produzir cobrança duplicada, duas solicitações para o mesmo fluxo ou divergência na conciliação. A idempotency key em API REST existe para tratar exatamente esse cenário.

A chave não impede falhas de rede. Ela torna o reenvio identificável pelo servidor. Quando a mesma chave volta para a mesma operação, a API consegue distinguir uma tentativa repetida de uma nova intenção de negócio.

O que uma idempotency key resolve em uma API REST

Idempotência significa que repetir uma operação produz o mesmo efeito observável da primeira execução. Em HTTP, métodos como `GET`, `PUT` e `DELETE` costumam ter semântica idempotente por definição do protocolo, embora a implementação ainda precise respeitá-la. Já `POST` geralmente cria ou inicia algo novo e, por isso, demanda um identificador adicional quando a repetição precisa ser controlada.

A idempotency key é esse identificador. O cliente gera uma chave única antes de enviar o `POST` e a encaminha em um header definido pelo contrato da API. O servidor associa a chave à requisição processada, ao resultado e a um período de retenção. Se receber novamente a mesma chave no mesmo escopo, devolve o resultado já registrado ou informa que a operação está em processamento, em vez de executá-la outra vez.

O benefício não é apenas técnico. Uma operação de cobrança, um pedido de laudo ou a abertura de um fluxo interno pode atravessar filas, gateways e retentativas automáticas. Sem uma referência única, produto, financeiro e suporte passam a discutir eventos que deveriam ser verificáveis no log.

Como funciona o reenvio de um POST

O fluxo correto começa no cliente, antes da primeira chamada. A chave deve representar uma única intenção de negócio, não uma tentativa de transporte. Se o usuário ou o sistema decidiu executar uma nova operação, gere uma nova chave. Se apenas houve timeout, queda de conexão ou resposta `5xx`, reutilize a chave original.

Gere a chave uma vez por operação

Uma chave com UUID aleatório é uma escolha comum. Ela deve ter entropia suficiente para evitar colisões e ser armazenada junto ao identificador interno da operação. Não use data e hora isoladamente, placa, CPF, número sequencial previsível ou um valor derivado de informação que possa expor contexto de negócio.

Também não gere a chave dentro de um interceptor que roda a cada retentativa. Esse erro elimina a proteção: cada novo envio parecerá uma operação inédita para a API.

```http POST /v1/servico HTTP/1.1 Content-Type: application/json Idempotency-Key: 7a4f9d3b-2ed2-4dd7-9d9a-0d9c0c8f1ea2

{ "campo": "valor" } ```

O nome do header, o formato aceito, o escopo e o prazo de retenção dependem da documentação de cada API. Não presuma que uma convenção usada em outro serviço vale para toda integração. O exemplo mostra a lógica, não substitui o contrato técnico.

Reenvie com a mesma chave após uma falha incerta

Se o cliente recebeu um timeout, a situação é indeterminada: a API pode não ter recebido a chamada, pode estar processando ou pode ter concluído e perdido a conexão de resposta. Nesse caso, envie o mesmo payload com a mesma chave.

Se a API responder que a solicitação anterior ainda está em andamento, não troque a chave para "destravar" o fluxo. A ação correta é aplicar a política de espera prevista no contrato, registrar a ocorrência e consultar o estado quando houver um recurso ou identificador para isso.

Quando o servidor devolve uma resposta previamente registrada, o cliente deve tratá-la como conclusão da intenção original. Não como uma segunda execução. Esse detalhe é decisivo para conciliação: uma mesma chave precisa aparecer como um único evento de negócio nos sistemas internos.

Mantenha o payload consistente

A mesma chave associada a conteúdos diferentes é um conflito, não uma retentativa válida. Por exemplo, não reutilize a chave de uma solicitação com um conjunto de parâmetros para outra solicitação alterada depois por regra de produto.

Uma API pode comparar o corpo recebido, armazenar uma impressão criptográfica do payload ou aplicar validações equivalentes. Se houver divergência, a resposta deve sinalizar erro de uso da chave, com um código e uma mensagem acionáveis. Para o integrador, a regra é mais simples: mudou a intenção ou o conteúdo relevante, gere outra chave.

Idempotency key não substitui autenticação nem validação

Uma chave de idempotência não autoriza o acesso e não prova a integridade da mensagem. Ela apenas correlaciona tentativas de uma operação. Esses controles precisam existir em camadas separadas.

Na API da Lanet, requisições são assinadas com HMAC-SHA256 e incluem carimbo de tempo. A assinatura permite verificar que a mensagem foi produzida por quem detém o segredo e que seu conteúdo não foi alterado durante o tráfego. A chave de idempotência obrigatória em `POST`, por sua vez, permite identificar a repetição de uma solicitação. Um controle não substitui o outro.

Também permanece necessária a validação de autorização, esquema JSON, limites de uso e regras do serviço. Uma chamada autenticada pode ser inválida. Uma chamada idempotente pode estar fora do escopo contratado. Projetar a integração como se a chave resolvesse todos os erros produz comportamentos difíceis de auditar.

Decisões de implementação que evitam duplicidade real

A chave precisa ser persistida antes do envio externo. Se o processo cair entre gerar a chave e receber a resposta, o worker ou aplicativo deve recuperar o mesmo valor ao retomar a tarefa. Guardar a chave apenas em memória funciona em testes simples, mas falha em reinícios, filas distribuídas e múltiplas instâncias.

Defina também um escopo claro. Em geral, a combinação de credencial, endpoint e chave deve identificar uma única operação. Reutilizar o mesmo valor em endpoints distintos pode ser permitido ou não pelo provedor, mas não é uma boa base para o controle interno. Prefira uma chave nova para cada comando de negócio.

A política de retentativa merece o mesmo cuidado. Erros de conexão e determinadas respostas de servidor podem justificar novo envio com a mesma chave. Erros de validação `4xx`, em regra, exigem correção do payload antes de uma nova operação. Repetir automaticamente uma solicitação inválida apenas aumenta ruído e pode acionar limites de requisição.

Por fim, registre o mínimo necessário para investigação: chave de idempotência, identificador interno, horário, endpoint, status HTTP, identificador de correlação quando houver e resultado da operação. Evite registrar segredos de assinatura ou dados além do necessário para a finalidade operacional.

O que observar nos erros e na conciliação

Uma implementação madura separa falha conhecida de estado desconhecido. Se a API devolveu uma rejeição de validação, o cliente sabe que não houve execução válida e pode corrigir a origem. Se houve timeout, o estado é desconhecido até que a mesma chave seja reenviada ou o status seja confirmado por um mecanismo previsto pela API.

Essa distinção deve aparecer na tela operacional e nas rotinas de backoffice. Marcar todo timeout como "falha" leva pessoas a dispararem manualmente uma operação que talvez já tenha sido concluída. Marcar como "concluída" sem confirmação cria o problema oposto. O estado adequado é pendente de confirmação, associado à chave original.

Para equipes que conciliam consumo de serviços, a chave é uma referência útil entre aplicação cliente, logs de integração e registros do fornecedor. Ela não elimina a necessidade de observar regras de cobrança e respostas do contrato, mas reduz ambiguidades quando existe mais de uma tentativa de rede para o mesmo comando.

Uma regra simples para produto e engenharia

Trate a idempotency key como parte do comando de negócio. Crie-a quando a operação nasce, persista-a antes do primeiro envio e reutilize-a somente enquanto a intenção for a mesma. Ao alterar os dados relevantes ou iniciar uma nova ação, gere outra chave.

Esse padrão parece pequeno no código, mas evita que uma instabilidade transitória vire duplicidade financeira, retrabalho operacional ou disputa de auditoria. Antes de colocar um `POST` em produção, vale testar deliberadamente timeout, reinício do worker, duas retentativas concorrentes e reenvio com payload divergente. É nesses cenários que a idempotência deixa de ser um header e passa a ser um controle de operação.

← Todos os artigos do blog

Fale com o comercial no WhatsApp +55 11 4858-7977