Retries e idempotência

Aprenda a implementar retries com segurança, tratar timeouts e evitar operações duplicadas utilizando identificação, validação e controle de concorrência.

Falhas temporárias fazem parte de qualquer integração. Timeouts, instabilidades de rede e respostas inconclusivas podem acontecer mesmo quando a operação foi processada corretamente.

O risco aparece quando a aplicação repete uma operação sem saber se a tentativa anterior já produziu efeito. Em fluxos financeiros, retry sem controle pode gerar duplicidades e inconsistências.

📘

Ao concluir esta página, você entenderá quando repetir uma operação, como diferenciar falhas temporárias de definitivas e como reduzir o risco de duplicidade utilizando identificação, validação e controle de concorrência.

Quando utilizar

Aplique essas práticas quando sua integração precisar:

  • repetir chamadas após timeout ou instabilidade;
  • recuperar operações com resultado inconclusivo;
  • executar retries automáticos;
  • evitar criação duplicada de entidades;
  • processar operações em filas ou workers;
  • garantir que uma mesma intenção de negócio não seja executada mais de uma vez.

Antes de começar

Antes de implementar retries:

  • defina quais erros podem justificar uma nova tentativa;
  • atribua um identificador interno único para cada operação;
  • relacione esse identificador ao ID retornado pelo Asaas;
  • utilize externalReference quando o recurso utilizado disponibilizar esse campo e ele for adequado ao fluxo;
  • defina como sua aplicação verificará se uma tentativa anterior já produziu efeito;
  • implemente controle para impedir execuções concorrentes da mesma operação.

Como funciona

Uma falha de comunicação não significa automaticamente que a operação falhou.

Antes de repetir uma chamada, sua aplicação deve primeiro determinar se a tentativa anterior pode ter sido processada.

%%{init: {"flowchart": {"nodeSpacing": 28,"rankSpacing": 32,"diagramPadding": 8,"padding": 10}}}%%
flowchart TD
    A["Executar operação"] --> B{"Resposta conclusiva?"}

    B --> BSim(("Sim"))
    B --> BNao(("Não"))

    BSim --> C["Registrar resultado"]

    BNao --> D["Registrar tentativa inconclusiva"]
    D --> E{"Operação já existe?"}

    E --> ESim(("Sim"))
    E --> ENao(("Não"))

    ESim --> F["Utilizar operação existente"]
    ENao --> G{"Erro permite nova tentativa?"}

    G --> GSim(("Sim"))
    G --> GNao(("Não"))

    GSim --> H["Aguardar backoff"]
    H --> I["Executar nova tentativa"]
    I --> B

    GNao --> J["Registrar falha definitiva"]

    classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px,font-size:17px
    classDef processo fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px,font-size:17px
    classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:2px,font-size:17px
    classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px,font-size:17px
    classDef recuperacao fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:2px,font-size:17px

    classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
    classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px

    class A inicio
    class B,E,G decisao
    class C,F sucesso
    class D,H,I processo
    class J recuperacao

    class BSim,ESim,GSim respostaSim
    class BNao,ENao,GNao respostaNao

    linkStyle default stroke:#94A3B8,stroke-width:2px

O retry deve ser a última etapa da decisão, não a primeira. Antes de executar uma nova tentativa, sua aplicação precisa avaliar o resultado anterior e confirmar se repetir a operação é realmente necessário.

1. Não trate timeout como falha confirmada

Quando uma chamada expira ou a conexão é interrompida, sua aplicação sabe apenas que não recebeu uma resposta conclusiva.

Isso não significa necessariamente que a operação deixou de ser processada.

Uma cobrança, transferência, assinatura ou outra entidade pode ter sido criada no Asaas mesmo que a resposta não tenha chegado até sua aplicação.

Por isso, o fluxo não deve ser:

timeout → repetir imediatamente

O fluxo deve considerar:

timeout → verificar resultado anterior → decidir se é necessário repetir

⚠️

Não assuma que uma operação falhou apenas porque sua aplicação não recebeu a resposta. Repetir uma criação sem verificar o resultado anterior pode gerar uma nova entidade para a mesma intenção de negócio.

2. Diferencie falhas temporárias de falhas definitivas

Nem todo erro deve gerar retry.

Antes de repetir uma chamada, classifique o tipo de falha ocorrido.

SituaçãoTratamento
Timeout ou falha de conexãoConsidere o resultado inconclusivo e verifique se a operação já foi executada antes de repetir
Erro temporário do servidorPode justificar nova tentativa depois de validação e espera adequada
Limite temporário de requisiçõesAguarde antes de tentar novamente e respeite os limites aplicáveis
Erro de validaçãoCorrija os dados antes de enviar uma nova requisição
Regra de negócio rejeitadaAvalie a causa antes de decidir por uma nova tentativa
Erro de autenticação ou autorizaçãoCorrija a configuração ou credencial antes de repetir

Repetir a mesma requisição sem alterar a causa de uma falha definitiva apenas produz o mesmo erro novamente.

3. Identifique cada operação de forma única

Sua aplicação deve conseguir reconhecer a intenção de negócio que originou uma chamada.

Crie um identificador interno único antes da primeira tentativa e mantenha esse identificador durante todo o ciclo de retries.

Esse vínculo permite responder perguntas como:

  • essa operação já foi enviada?
  • existe uma tentativa em andamento?
  • o Asaas já criou a entidade correspondente?
  • qual ID do Asaas está relacionado a essa operação?
  • quantas tentativas já ocorreram?
  • qual foi o resultado da última tentativa?

O identificador deve existir antes da chamada, e não apenas depois de uma resposta bem-sucedida.

4. Utilize externalReference quando aplicável

Quando o recurso utilizado disponibilizar externalReference, o campo pode ajudar a relacionar o registro criado no Asaas à operação existente no seu sistema.

Por exemplo:

{
  "customer": "cus_000000000000",
  "billingType": "PIX",
  "value": 100,
  "dueDate": "2026-09-30",
  "externalReference": "pedido-98765"
}

O valor utilizado deve ser estável para a mesma operação.

Evite gerar um novo externalReference a cada retry, pois isso elimina justamente a relação necessária para reconhecer diferentes tentativas da mesma intenção.

Gere o identificador da operação antes da primeira chamada e reutilize-o em todas as tentativas relacionadas à mesma intenção de negócio.

5. Relacione o identificador interno ao ID do Asaas

Quando uma chamada retornar com sucesso, armazene a relação entre:

  • identificador interno da operação;
  • externalReference, quando utilizado;
  • ID retornado pelo Asaas;
  • resultado da tentativa;
  • data e horário;
  • estado conhecido naquele momento.

Essa relação deve ser mantida mesmo depois da conclusão da operação.

Ela será útil para:

  • consultas posteriores;
  • processamento de Webhooks;
  • reconciliação;
  • investigação de falhas;
  • prevenção de novas tentativas indevidas.

6. Verifique o resultado antes de repetir

Quando uma tentativa tiver resultado inconclusivo, procure determinar se a operação anterior já produziu efeito.

O fluxo recomendado é:

%%{init: {"flowchart": {"nodeSpacing": 28,"rankSpacing": 32,"diagramPadding": 8,"padding": 10}}}%%
flowchart TD
    A["Tentativa com resultado inconclusivo"] --> B["Recuperar identificador da operação"]
    B --> C["Verificar registros locais"]
    C --> D{"Existe ID do Asaas associado?"}

    D --> DSim(("Sim"))
    D --> DNao(("Não"))

    DSim --> E["Consultar ou utilizar operação existente"]
    DNao --> F["Verificar se a operação foi criada"]
    F --> G{"Operação encontrada?"}

    G --> GSim(("Sim"))
    G --> GNao(("Não"))

    GSim --> H["Associar ID encontrado"]
    H --> E

    GNao --> I["Avaliar nova tentativa"]

    classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px,font-size:17px
    classDef processo fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px,font-size:17px
    classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:2px,font-size:17px
    classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px,font-size:17px
    classDef recuperacao fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:2px,font-size:17px

    classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
    classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px

    class A inicio
    class B,C,F processo
    class D,G decisao
    class E,H sucesso
    class I recuperacao

    class DSim,GSim respostaSim
    class DNao,GNao respostaNao

    linkStyle default stroke:#94A3B8,stroke-width:2px

Se a operação já existir, utilize o registro encontrado em vez de criar um novo.

Somente prossiga com uma nova tentativa quando houver elementos suficientes para concluir que a operação anterior não produziu o efeito esperado.

7. Controle retries concorrentes

Mesmo utilizando um identificador único, sua aplicação ainda pode gerar duplicidades se vários processos executarem a mesma operação simultaneamente.

Isso pode acontecer, por exemplo, quando:

  • dois workers recebem a mesma tarefa;
  • uma mensagem é entregue mais de uma vez por uma fila;
  • o usuário envia a mesma ação repetidamente;
  • dois servidores processam a mesma operação;
  • um retry começa enquanto a tentativa anterior ainda está em andamento.

Nesse cenário, dois processos podem verificar quase ao mesmo tempo que a operação ainda não existe e ambos executar a criação.

Implemente algum mecanismo que garanta que apenas um processo avance com a mesma operação por vez.

Dependendo da arquitetura, isso pode ser feito com:

  • lock distribuído;
  • controle transacional;
  • restrição única no banco de dados;
  • estado interno de processamento;
  • serialização por chave de negócio.

O mecanismo escolhido depende da infraestrutura da sua aplicação. O requisito é impedir que duas execuções concorrentes realizem o mesmo efeito.

8. Aplique espera progressiva entre tentativas

Retries imediatos em sequência aumentam a carga sobre a integração e podem repetir chamadas enquanto a causa da falha ainda está presente.

Utilize uma estratégia de backoff, aumentando o intervalo entre as tentativas.

Por exemplo:

1ª nova tentativa: aguardar 1 segundo
2ª nova tentativa: aguardar 2 segundos
3ª nova tentativa: aguardar 4 segundos
4ª nova tentativa: aguardar 8 segundos

Os valores devem ser ajustados ao comportamento da operação e aos limites aplicáveis à API.

Também estabeleça um número máximo de tentativas. Uma operação não deve permanecer em retry indefinidamente.

9. Registre todas as tentativas

Cada tentativa deve deixar informações suficientes para reconstruir o que aconteceu.

Registre, quando aplicável:

  • identificador interno da operação;
  • ID do Asaas, quando disponível;
  • número da tentativa;
  • data e horário;
  • resultado HTTP;
  • classificação da falha;
  • decisão tomada após o erro;
  • intervalo aplicado antes da próxima tentativa;
  • estado final do processamento.

Esses dados permitem diferenciar uma instabilidade isolada de um problema recorrente na integração.

Atenção — duplicidade e erros comuns

Evite implementações que:

  • tratam timeout como confirmação de que a operação não ocorreu;
  • repetem uma criação imediatamente após perder a resposta;
  • executam retry para qualquer tipo de erro;
  • repetem falhas de validação sem corrigir o payload;
  • não possuem um identificador interno por operação;
  • geram um novo identificador a cada tentativa;
  • não relacionam o registro local ao ID do Asaas;
  • procuram operações apenas por combinações frágeis como valor, data e cliente;
  • permitem que múltiplos processos repitam a mesma operação simultaneamente;
  • executam tentativas em sequência sem intervalo;
  • realizam retries indefinidamente.
⚠️

Retry não significa repetir a chamada automaticamente. Significa recuperar uma operação com segurança depois de determinar o que aconteceu na tentativa anterior.

Confirme sua arquitetura

Antes do go-live, confirme se sua aplicação consegue responder:

  • Quais erros podem gerar uma nova tentativa?
  • Quais erros exigem correção antes de qualquer retry?
  • O que acontece quando uma chamada termina em timeout?
  • Existe um identificador único criado antes da primeira tentativa?
  • Esse identificador permanece igual durante os retries?
  • O ID retornado pelo Asaas é associado à operação interna?
  • É possível verificar se a operação anterior já produziu efeito?
  • O sistema evita duas execuções simultâneas da mesma operação?
  • Existe espera progressiva entre tentativas?
  • Existe um limite máximo de retries?
  • Todas as tentativas ficam registradas para diagnóstico?

Se essas condições não estiverem atendidas, uma tentativa criada para recuperar uma falha pode gerar um problema adicional: executar novamente uma operação que já havia sido concluída.

Boas práticas

  • considere timeout como resultado inconclusivo, e não como falha confirmada;
  • classifique o erro antes de decidir por um retry;
  • crie o identificador interno antes da primeira tentativa;
  • utilize externalReference quando o recurso disponibilizar esse campo e ele fizer sentido para o fluxo;
  • mantenha o mesmo identificador durante todas as tentativas da operação;
  • armazene o ID retornado pelo Asaas assim que ele estiver disponível;
  • verifique o resultado anterior antes de criar uma nova operação;
  • proteja o fluxo contra execuções concorrentes;
  • utilize backoff entre tentativas;
  • estabeleça limite máximo de retries;
  • mantenha histórico suficiente para diagnosticar falhas e duplicidades.

Próximos passos

Depois de estruturar retries e mecanismos para evitar duplicidades, valide se todos os componentes da integração estão preparados para operar no ambiente produtivo:


Did this page help you?