Idempotência em APIs: como evitar operações duplicadas
Aprenda como a idempotência permite repetir requisições com segurança e evita cobranças, pedidos ou processamentos duplicados em APIs.

Você envia uma solicitação para criar um pedido. A conexão cai antes da resposta e o cliente tenta novamente.
O servidor recebeu a primeira requisição ou não? Se a repetição criar outro pedido ou realizar uma segunda cobrança, uma falha comum de rede se transforma em um problema de negócio.
Idempotência em APIs é a propriedade que permite repetir a mesma operação lógica sem produzir um efeito adicional depois da primeira execução bem-sucedida.
Ela torna retentativas mais seguras, mas precisa envolver contrato, persistência e concorrência — não basta comparar duas requisições na memória.
Neste guia, você entenderá o significado do termo nos métodos HTTP, quando usar uma chave idempotente, como armazenar e devolver resultados, quais falhas ainda permanecem e como testar a implementação em condições reais.
O que é idempotência em APIs?
Uma operação é idempotente quando executá-la várias vezes com a mesma intenção deixa o servidor no mesmo estado que uma única execução.
Definir o endereço de entrega de um pedido como “endereço B” continua produzindo esse estado mesmo que o comando seja repetido.
Já acrescentar uma unidade ao carrinho em cada chamada produz efeitos cumulativos e não é idempotente.
A propriedade pertence ao comportamento observável do contrato, não à tecnologia escolhida.
Banco relacional, cache distribuído ou fila podem ajudar na implementação, mas nenhum deles torna uma operação idempotente automaticamente.
Também é importante separar conceitos. Um identificador de correlação acompanha uma requisição nos logs. Uma chave idempotente representa uma operação lógica que pode reaparecer.
Deduplicação encontra repetições, enquanto idempotência define qual efeito deve permanecer quando elas acontecem.
Por que requisições se repetem?
Em sistemas distribuídos, o cliente nem sempre consegue distinguir “o servidor não recebeu” de “o servidor concluiu, mas a resposta se perdeu”. Um timeout descreve o que o cliente observou, não o que ocorreu no servidor.
A retentativa é necessária justamente porque o resultado ficou incerto.
- o aplicativo móvel perde a conexão depois de enviar os dados;
- um proxy encerra a espera antes de o processamento terminar;
- o cliente repete automaticamente uma chamada após erro transitório;
- uma pessoa toca duas vezes no botão de confirmação;
- um worker reinicia antes de confirmar que consumiu a mensagem;
- um webhook é reenviado porque o destinatário não respondeu a tempo.
Uma boa estratégia de desenvolvimento de APIs assume que essas situações acontecerão.
Bloquear o botão na interface melhora a experiência, mas não protege contra rede instável, clientes diferentes ou chamadas diretas ao endpoint.
Idempotência nos métodos HTTP
A especificação HTTP define como idempotente o método cujo efeito pretendido no servidor, após várias requisições idênticas, é o mesmo de uma única requisição.
Métodos seguros são idempotentes; PUT e DELETE também possuem essa semântica. POST e PATCH não a oferecem por definição, embora uma API possa projetar operações específicas para se comportarem assim.
| Método | Semântica idempotente? | Exemplo |
|---|---|---|
| GET | Sim | Ler um pedido sem alterar seu estado pretendido |
| PUT | Sim | Substituir o recurso pelo estado informado |
| DELETE | Sim | Garantir que o recurso deixe de existir |
| POST | Não por padrão | Criar uma nova cobrança a cada chamada |
| PATCH | Não por padrão | Pode definir um valor ou aplicar uma alteração cumulativa |
O método sozinho não corrige uma implementação incoerente. Um GET que envia e-mail ou altera saldo viola sua intenção segura.
Um PUT que interpreta o corpo como “some dez” também não entrega a semântica esperada. Os fundamentos de uma API REST ajudam a escolher método e contrato de maneira consistente.
Mesmo efeito não significa resposta idêntica
Idempotência não exige que todas as respostas tenham o mesmo status ou corpo. O primeiro DELETE pode retornar 204 e o segundo, 404; ao final, o recurso continua ausente.
Logs, métricas e auditoria também podem registrar cada tentativa sem transformar o efeito de negócio em duplicado.
Em endpoints protegidos por chave, porém, devolver a resposta armazenada costuma ser mais conveniente. O cliente recebe o mesmo identificador de pedido ou cobrança e não precisa descobrir qual execução venceu. Esse é um contrato adicional da API, não uma exigência geral do HTTP.
Quando usar uma chave idempotente
Uma chave idempotente é especialmente útil em comandos POST cuja repetição teria impacto financeiro ou operacional: criar pedido, cobrar pagamento, reservar assento, emitir documento, iniciar transferência ou disparar um job caro.
O cliente cria um valor opaco e exclusivo para aquela intenção e o envia em todas as tentativas da mesma operação.
A documentação de idempotência da Stripe mostra uma aplicação conhecida: a plataforma associa a chave ao primeiro resultado e permite que chamadas de criação ou atualização sejam repetidas com segurança.
Regras de tamanho, retenção e resposta são específicas do provedor; o padrão arquitetural é que interessa aqui.
POST /pedidos
Idempotency-Key: valor-opaco-unico
Content-Type: application/json
{
"cliente_id": "...",
"itens": [ ... ]
}
Uma nova tentativa do mesmo pedido reutiliza a chave. Um novo pedido, mesmo feito pelo mesmo cliente, recebe outra.
Gerar uma chave diferente a cada retry elimina a proteção, pois o servidor passa a enxergar operações independentes.
Como funciona uma chave idempotente
- O cliente gera a chave antes da primeira tentativa.
- O servidor combina a chave com um escopo, como conta, endpoint e operação.
- Um fingerprint estável do payload é calculado.
- O servidor reserva atomicamente o registro como em processamento.
- A lógica de negócio é executada uma única vez.
- O resultado relevante é persistido e o registro passa a concluído.
- Uma repetição compatível recebe o resultado salvo, sem executar o comando novamente.

A chave não precisa revelar o conteúdo do pedido. Valores aleatórios com entropia suficiente, como UUIDs adequadamente gerados, são simples de produzir e evitam colisões práticas.
O servidor não deve confiar apenas na chave: autenticação, autorização e validação do corpo continuam obrigatórias.
Fluxo completo no servidor
Ao receber uma chave inédita, o servidor tenta criar o registro de controle. Se vencer, executa a operação.
Se encontrar um registro concluído com o mesmo fingerprint, devolve o resultado armazenado.
Se o registro ainda estiver em processamento, aguarda por um período curto ou responde com um estado documentado para que o cliente tente depois.
Falhas de validação anteriores ao início da execução normalmente não precisam ser memorizadas como resultado definitivo: o cliente pode corrigir a entrada. Falhas depois que a operação começou exigem mais cuidado. Armazenar indiscriminadamente qualquer erro pode transformar uma indisponibilidade temporária em resposta permanente; repetir cegamente pode executar o negócio duas vezes.
Por isso, o contrato deve definir quando a execução é considerada iniciada, quais respostas são reproduzidas e como um estado incerto é reconciliado.
A implementação de referência do AWS Lambda Powertools ilustra registros com status, expiração, hash do payload e resultado, além do tratamento de operações em andamento.
Persistência e atomicidade
O registro idempotente precisa sobreviver a reinícios e ser compartilhado pelas instâncias da aplicação.
Guardar chaves em uma variável local funciona apenas em demonstrações: uma requisição seguinte pode cair em outro processo ou chegar depois de uma reinicialização.
| Campo | Finalidade |
|---|---|
| escopo + chave | Identificador único da operação lógica |
| fingerprint | Comprovar que o conteúdo relevante é o mesmo |
| status | Distinguir processamento, conclusão e falha tratável |
| recurso criado | Relacionar pedido, pagamento ou job resultante |
| resposta | Reproduzir status e corpo definidos pelo contrato |
| criado e expira em | Controlar retenção e limpeza |
Quando o registro e a alteração de negócio estão no mesmo banco, uma transação pode torná-los atômicos. Em recursos externos, a fronteira é mais difícil: uma cobrança pode ser aceita e a gravação local falhar.
Use também a chave idempotente oferecida pelo provedor, reconcilie resultados por um identificador de negócio e trate o estado “desconhecido” explicitamente.
Como controlar requisições concorrentes
Duas requisições iguais podem chegar quase ao mesmo tempo. O padrão “consultar e depois inserir” sofre uma condição de corrida: ambas consultam, não encontram a chave e executam.
A reserva precisa ser uma operação atômica, sustentada por restrição única, inserção condicional, transação ou mecanismo equivalente do armazenamento.

A instância vencedora marca a operação como em andamento. As demais não devem executar o negócio.
Elas podem aguardar a conclusão, receber conflito temporário ou uma resposta que indique processamento, conforme o tempo esperado e o desenho da API.
Locks precisam expirar ou ter mecanismo de recuperação para não bloquear a chave para sempre após uma falha.
A mesma chave com payload diferente
Reutilizar uma chave para enviar valores diferentes é erro de contrato. O servidor deve comparar um fingerprint dos campos que definem a intenção e rejeitar a segunda requisição.
Aceitá-la poderia devolver o resultado de um pedido antigo para um conteúdo novo; sobrescrever o registro permitiria executar outra operação sob a mesma identidade.
Normalize somente o que for semanticamente seguro. Espaços irrelevantes ou ordem de propriedades JSON podem ser canonicalizados, mas remover campos significativos do hash cria falsos equivalentes. Cabeçalhos que alteram moeda, conta ou comportamento também podem precisar participar do fingerprint ou do escopo.
Expiração e retenção
Registros idempotentes não precisam existir indefinidamente, mas o prazo não deve ser escolhido apenas para economizar armazenamento. Ele precisa cobrir a janela real de retentativas, atrasos de filas, funcionamento offline do cliente e risco financeiro da operação.
Depois da expiração, uma chave reutilizada pode ser tratada como nova. Documente esse comportamento para que o cliente não presuma proteção eterna. Em operações duradouras, um identificador de negócio com unicidade permanente pode ser mais apropriado que um registro temporário de resposta.
Estratégias sem uma chave explícita
- Estado desejado: PUT define o recurso completo ou um valor final, em vez de aplicar um incremento.
- Identificador criado pelo cliente: o cliente escolhe o ID do recurso e repete a criação no mesmo endereço.
- Chave natural do negócio: uma restrição única impede duas matrículas para a mesma pessoa e turma.
- Upsert ou inserção condicional: o armazenamento cria somente se o registro ainda não existir.
- Versão esperada: controle otimista aplica a alteração apenas se a versão do recurso continuar igual.
Essas técnicas podem ser mais expressivas quando o domínio já possui identidade estável. Uma chave idempotente genérica não deve esconder um modelo que permitiria representar diretamente o estado desejado.
Idempotência em webhooks e eventos
Provedores de webhooks e brokers frequentemente repetem entregas quando não recebem confirmação. O consumidor deve usar o identificador estável do evento, combinado com origem e tipo, para registrar que o efeito já foi aplicado. Hash do corpo é uma alternativa menos robusta quando o produtor não oferece ID.
A confirmação da mensagem deve acontecer depois que o efeito e o registro de processamento estiverem seguros. Confirmar antes pode perder trabalho; confirmar depois sem idempotência pode repeti-lo. Em integrações baseadas em Change Data Capture, a posição no log e a identidade do evento também ajudam a retomar o fluxo sem duplicar alterações.
O que a idempotência não resolve
- Exactly once ponta a ponta: proteger a entrada da API não garante que e-mail, fila, pagamento e outros destinos executem uma única vez.
- Conflitos entre operações diferentes: duas chaves distintas ainda podem disputar o último item do estoque.
- Ordem: idempotência evita efeito repetido, mas não corrige comandos recebidos fora de sequência.
- Transação distribuída: falhas entre banco e serviço externo exigem reconciliação, compensação ou publicação confiável.
- Validação e autorização: uma repetição continua sujeita às regras de identidade e acesso do sistema.
Cada efeito externo deve ter sua própria proteção. Para publicar uma mensagem após alterar o banco, por exemplo, uma estratégia de outbox pode fechar a lacuna entre a transação local e o envio — tema que merece um guia próprio.
Segurança e privacidade
Escopo é parte da segurança. A mesma sequência não deve permitir que uma pessoa recupere a resposta de outra. Associe a chave à identidade autorizada, ao tenant e à operação; valide a autorização também nas repetições.
Chaves opacas e difíceis de adivinhar reduzem enumeração, mas não substituem controle de acesso.
Não coloque dados pessoais ou segredos na chave. Defina limites de tamanho, taxa e quantidade para evitar abuso de armazenamento. Se a resposta persistida contém informações sensíveis, aplique criptografia, retenção mínima e regras equivalentes às do recurso original.
Nos logs, prefira hash ou prefixo seguro em vez do valor completo.
Observabilidade da idempotência
Meça quantas chaves são novas, repetidas, incompatíveis ou encontradas em processamento.
Picos de duplicidade podem revelar rede instável ou política de retry agressiva; muitas chaves diferentes para a mesma ação podem indicar um cliente implementado incorretamente.
- taxa de acertos idempotentes e reservas novas;
- conflitos simultâneos por chave;
- reuso com payload diferente;
- tempo até a conclusão e tamanho do armazenamento;
- registros presos em processamento;
- operações reconciliadas após estado incerto.
Propague o identificador de correlação separadamente e use OpenTelemetry para seguir a tentativa por serviços quando apropriado. Uma estratégia mais ampla de observabilidade e monitoramento conecta esses sinais a logs, métricas, traces e alertas acionáveis.
Como testar uma implementação idempotente
- Envie a mesma chave e o mesmo payload em sequência; confirme um único efeito.
- Dispare várias chamadas simultâneas; apenas uma deve executar a lógica de negócio.
- Simule timeout depois do commit e antes da resposta; a retentativa deve recuperar o resultado.
- Interrompa a instância durante o processamento; valide expiração, recuperação ou reconciliação.
- Reutilize a chave com payload diferente; a API deve rejeitar claramente.
- Teste múltiplas instâncias compartilhando o armazenamento.
- Avance além da retenção e confirme o comportamento documentado.
- Crie uma tempestade de retries e observe limites, latência e contenção.
Não restrinja o teste ao status HTTP. Verifique saldo, número de pedidos, estoque, mensagens publicadas e efeitos em terceiros.
O objetivo é provar a invariável de negócio: para uma mesma operação lógica, o efeito relevante acontece no máximo uma vez dentro do escopo definido.
Checklist de implementação
- Defina exatamente qual operação lógica a chave representa.
- Documente quem gera a chave e quando deve reutilizá-la.
- Inclua identidade, tenant e endpoint no escopo.
- Compare fingerprint do payload e rejeite incompatibilidades.
- Faça a reserva por operação atômica com unicidade.
- Persista status, resultado e referência do recurso.
- Planeje falha após commit e antes da resposta.
- Defina retenção com base na janela de retry.
- Proteja cada efeito externo e implemente reconciliação.
- Monitore duplicidade, concorrência e registros presos.
- Teste repetição sequencial, concorrente e após reinício.
Erros comuns
- Salvar somente em memória: não funciona entre instâncias nem reinícios.
- Consultar antes de inserir sem restrição única: permite corrida.
- Ignorar o payload: mistura operações diferentes sob a mesma chave.
- Usar request ID como sinônimo: cada retry pode receber um request ID novo.
- Gerar a chave no servidor depois da chegada: o servidor não consegue relacionar tentativas que já chegaram separadas.
- Proteger apenas o banco: mensagens, e-mails e provedores externos ainda podem duplicar.
- Manter lock eterno: uma falha pode bloquear a operação sem recuperação.
- Prometer “exatamente uma vez” sem definir a fronteira: a garantia raramente cobre todos os componentes.
Perguntas frequentes
Idempotência e deduplicação são a mesma coisa?
Não. Deduplicação identifica itens repetidos; idempotência garante que a repetição não produza efeito adicional. Uma implementação pode usar deduplicação para alcançar comportamento idempotente.
Uma requisição POST pode ser idempotente?
Sim, se o contrato acrescentar essa garantia. Uma chave idempotente ou um identificador de negócio único pode impedir que várias chamadas POST criem efeitos duplicados.
DELETE continua idempotente se a segunda chamada retornar 404?
Sim. O código de resposta pode mudar; o efeito pretendido continua sendo deixar o recurso ausente. Idempotência não exige respostas idênticas.
Quem deve gerar a chave idempotente?
Normalmente, o cliente que conhece a operação lógica. Ele precisa criar a chave antes da primeira tentativa e reutilizá-la nos retries da mesma intenção.
Por quanto tempo a chave deve ser armazenada?
Durante toda a janela plausível de repetição e risco do negócio. Não existe um prazo universal; aplicativos offline, filas demoradas e pagamentos podem exigir políticas diferentes.
A chave idempotente substitui transações?
Não. A chave identifica repetições; transações protegem consistência e atomicidade dentro de sua fronteira. Em geral, as duas técnicas são complementares.
Idempotência garante processamento exactly once?
Não de forma irrestrita. Ela pode fazer o efeito parecer único dentro de uma fronteira definida, mas sistemas externos e mensagens precisam de proteção própria.
Conclusão
Idempotência transforma a repetição de uma ameaça em uma parte prevista do contrato.
Métodos HTTP já oferecem semânticas úteis, mas comandos de criação e outros efeitos sensíveis normalmente exigem identidade da operação, reserva atômica, persistência compartilhada, comparação do payload e uma política clara para resultados e expiração.
Comece pelos fluxos em que duplicidade custa dinheiro, estoque ou confiança. Defina a fronteira da garantia, teste a falha mais desconfortável — commit concluído e resposta perdida — e acompanhe cada repetição em produção.
O próximo passo é entender como tornar confiável a publicação de eventos quando a mesma transação também altera o banco de dados.