Idempotência
Idempotência é a garantia de que reenviar a mesma requisição não gera um segundo pagamento. Em sistemas distribuídos a resposta pode se perder mesmo depois de a operação ter sido efetivada — um timeout de rede, um deploy no meio do caminho — e sem idempotência o retry viraria débito duplicado.
Duas chaves controlam esse comportamento:
| Campo | Onde vai | Papel |
|---|---|---|
Idempotency-Key | Header HTTP | Identifica a tentativa. É por ela que recuperamos o pagamento já registrado e devolvemos o mesmo resultado. |
identifier | Campo do body | Identifica a operação no seu sistema. Ecoamos para o liquidante, e ele o devolve em todos os webhooks. |
Ambas são opcionais: quando você não envia, geramos um valor aleatório. Isso é importante e costuma passar batido — uma requisição sem essas chaves é sempre tratada como uma operação nova, e não tem proteção nenhuma contra duplicidade. Se o seu retry precisa ser seguro, envie as duas e repita exatamente os mesmos valores.
Recomendamos UUID v4 na Idempotency-Key e, no identifier, a chave que você já usa internamente para a operação (número do lote, id da ordem de pagamento).
O que cada fluxo usa para deduplicar
A chave que vale muda por fluxo. Reenviar com os mesmos valores é sempre seguro; o que difere é qual campo você precisa manter fixo.
| Fluxo | Dedupe por | Reenvio depois de uma falha |
|---|---|---|
| PIX out e devolução PIX | identifier + Idempotency-Key | Executa novamente |
| Transferência interna | identifier + Idempotency-Key | Executa novamente |
| Pagamento de boleto | Idempotency-Key | Executa novamente |
| TED | identifier | Não executa; devolve o estado da tentativa anterior |
Cenários
Os exemplos usam PIX out, mas o raciocínio vale para transferência interna e boleto. A TED tem uma seção própria porque se comporta de forma diferente.
A operação deu certo e você reenvia
Devolvemos 200 com o resultado original — mesmo paymentId, mesmo endToEndId. Nenhum novo débito acontece, e o payload da segunda requisição é ignorado.
{
"paymentId": "pay_9f2c…",
"status": "COMPLETED",
"endToEndId": "E1234567820260803…",
"identifier": "ordem-4471"
}
A operação falhou de forma definitiva e você reenvia
Este é o caso da conta sem saldo, da chave PIX inválida, da recusa por antifraude e do bloqueio por uma policy sua. A resposta é 422 com o motivo:
{
"paymentId": "pay_9f2c…",
"status": "FAILED",
"errorCode": "insufficient_funds",
"errorReason": "PIX não autorizado por falta de saldo na conta"
}
Uma falha definitiva significa que o dinheiro não saiu e não vai sair. Reenviar com a mesma Idempotency-Key executa o pagamento outra vez — é exatamente o que você quer depois de aportar saldo ou ajustar a regra que barrou a operação. O paymentId é reaproveitado, então você continua acompanhando o mesmo registro em vez de precisar rastrear um novo.
Só reenvie quando a causa da recusa tiver sido resolvida. Repetir contra a mesma condição vai devolver a mesma recusa.
A operação ainda está em andamento
Enquanto o pagamento não tem desfecho, reenviar com a mesma Idempotency-Key anexa à execução em curso em vez de abrir uma segunda. A resposta é 202 com status: "PENDING". Nunca existem dois pagamentos concorrentes para a mesma chave.
O desfecho ficou indeterminado (TIMEOUT)
Caso mais delicado da API. O liquidante aceitou a submissão mas não confirmou o resultado dentro da janela de espera: o dinheiro pode ter saído. Devolvemos 202 com status: "TIMEOUT" e um warning.
Aqui o comportamento muda de propósito: reenviar com a mesma Idempotency-Key devolve o estado existente e não submete o pagamento de novo, porque reexecutar sobre um desfecho desconhecido é o cenário clássico de pagamento em duplicidade.
O que fazer:
- Consulte
GET /v1/accounts/{accountId}/payments/{identifier}ou aguarde o webhook — o desfecho tardio (pix.out.completedoupix.out.failed) chega quando o liquidante confirmar. - Antes de emitir um pagamento novo, confira o extrato.
- Só use uma
Idempotency-Keynova se tiver confirmado que o valor não saiu.
Você reenvia com uma Idempotency-Key nova
É uma operação nova, e vai gerar um segundo pagamento. Trocar a chave é a forma de dizer "quero pagar de novo, de propósito". Nunca gere uma chave nova dentro de um laço de retry automático.
Com uma ressalva útil: se você trocar a chave mas repetir o mesmo identifier, o PIX out reconhece a operação e devolve 202 sem criar um segundo pagamento. Vale como rede de proteção adicional, não como substituto — a garantia forte de não duplicar vem de repetir a mesma Idempotency-Key.
Você reenvia a mesma chave com um payload diferente
Não comparamos payloads: quem manda é a chave. Se já existe pagamento para aquela Idempotency-Key, devolvemos o resultado dele e ignoramos os novos valores — não devolvemos erro de conflito. Portanto, jamais reaproveite uma chave para uma operação diferente, ou você vai receber 200 de um pagamento que não é o que você acabou de pedir.
TED
A TED deduplica só pelo identifier, e nunca reexecuta. Qualquer reenvio com um identifier já usado devolve 200 com o estado atual daquela TED, inclusive quando o estado é de falha:
{
"tedId": "ted-ordem-4471",
"status": "FAILED",
"identifier": "ordem-4471",
"note": "TED already exists for this identifier — returning current state (idempotent)"
}
Para tentar de novo depois de uma TED falhada, envie um identifier novo. Isso é deliberado: a TED tem janela de liquidação longa (até 48h) e o identifier é o que amarra o registro aos webhooks do liquidante.
Expiração
As chaves não expiram. O registro do pagamento é permanente, então uma Idempotency-Key usada meses atrás continua devolvendo o resultado daquela operação. Não reaproveite chaves entre operações diferentes.
Boas práticas
- Gere a
Idempotency-Keyantes da primeira tentativa e persista junto com a ordem de pagamento. Uma chave gerada dentro do retry não protege nada. - Mantenha a mesma chave em todo o backoff exponencial de erros de rede e
5xx. - Envie sempre o
identifier, com o id que a operação já tem no seu sistema. Ele é a chave de rastreio nos webhooks e no suporte. - Trate
TIMEOUTcomo estado próprio, nunca como falha. É o único desfecho em que reemitir pode duplicar dinheiro. - Antes de reenviar depois de um
422, resolva a causa. OerrorCodediz qual é; a lista completa está em Erros.