Proteções no saque Pix por chave
Visão Geral
Antes de enviar um saque por chave, o provedor de liquidação consulta a chave no diretório do Pix (DICT). Esse provedor limita as consultas e pode bloquear temporariamente a conta que repete consultas a chaves inexistentes. Quando isso acontece, todos os saques da conta sofrem.
Para evitar isso, a plataforma responde na hora com o que já sabe e evita consultas repetidas que fariam o provedor bloquear a conta. Existem três comportamentos, que você percebe na resposta do POST /api/pix/cash-out:
| Situação | Resposta da API | Transação criada? | Webhook? |
|---|---|---|---|
| Chave que o provedor já informou não existir | 422 imediato | Não | Não |
| Provedor bloqueando consultas | 429 imediato com Retry-After | Não | Não |
| Limite de ritmo do provedor | 201 normal, envio até 90 s mais lento | Sim | Sim, como sempre |
Repetir uma chave já negada não bloqueia a conta nem o cliente: só aquela chave fica com a resposta guardada (15 minutos, ou até 30 se o provedor negá-la de novo em até 2 horas) e a plataforma devolve a mesma negativa sem consultar o provedor. Todas as outras chaves seguem normais.
Este comportamento depende do liquidante da sua conta: está ativo em alguns liquidantes e, nos demais, o fluxo não muda. Em caso de dúvida sobre a sua conta, consulte seu gerente de integração.
Chave inexistente (422)
Quando acontece: a primeira tentativa para uma chave inexistente segue o fluxo normal da sua conta: com aceite assíncrono, a API responde 201 e, depois, você recebe o webhook de falha informando que a chave não foi encontrada; com aceite síncrono, o erro volta na própria resposta. Se o provedor já respondeu recentemente que a chave não existe, a recusa pode vir já na primeira tentativa da sua conta. Quando o provedor responde que a chave não existe, a plataforma guarda essa resposta por 15 minutos. Durante esse tempo, novos envios para essa chave recebem a mesma negativa direto da plataforma (422 PIX_KEY_NOT_FOUND, details.cached: true), sem consultar o provedor.
- Se, depois de vencer, o provedor negar a mesma chave de novo dentro de 2 horas, o novo registro vale 30 minutos. Esse é o máximo; nunca passa disso.
- Tentativas respondidas pela plataforma não contam e não prorrogam o prazo. Só uma nova negativa do próprio provedor conta.
- Só aquela chave é afetada. A conta, o cliente e todas as outras chaves seguem normais. Não é bloqueio nem punição.
- Por que existe: o provedor aplica uma regra do DICT que suspende por cerca de 30 minutos todos os saques por chave da conta quando a mesma chave inexistente é consultada repetidas vezes. Guardar a resposta protege a conta inteira.
- Uma chave recém-cadastrada pode levar até esse prazo para ser aceita.
- Sem transação, sem reserva de saldo e sem webhook.
- O
externalIdda requisição recusada não fica consumido e pode ser reutilizado.
{
"code": "PIX_KEY_NOT_FOUND",
"message": "Chave Pix não encontrada",
"details": {
"retryAfterSeconds": 780,
"source": "provider-guard",
"cached": true
}
}retryAfterSeconds (e o cabeçalho Retry-After) trazem sempre o tempo real que falta.
O que fazer: trate como a falha de chave não encontrada que chegaria por webhook: peça ao usuário para conferir a chave. Se a chave foi cadastrada agora, aguarde retryAfterSeconds e tente de novo.
A negativa de chave inexistente pode chegar de duas formas. Trate as duas como a mesma situação (a chave não existe): corrija antes de reenviar.
| Origem da negativa | Como chega | Identificação |
|---|---|---|
| Provedor | Falha do saque (webhook) | errorCode e errorMessage do liquidante (o errorCode varia conforme o liquidante e não deve ser usado para lógica) |
| Registro da plataforma | 422 na resposta | code PIX_KEY_NOT_FOUND, details.cached: true e details.retryAfterSeconds |
Linha do tempo de exemplo:
10:00 Saque para a chave X
-> 201, depois webhook de falha "chave não encontrada"
10:02 Novo saque para a chave X
-> 422 imediato do registro, retryAfterSeconds ~ 780, cached: true
10:15 O registro vence
10:16 Novo saque para a chave X
-> consulta o provedor de novo
-> se negar de novo, novo registro de 30 minutos (até 10:46)Saque já aceito: em contas com aceite assíncrono (resposta 201 e resultado por webhook), a chave pode ser registrada como inexistente depois de o saque ter sido aceito e antes de ele ser enviado ao provedor, por exemplo quando vários saques para a mesma chave são enviados ao mesmo tempo. Nesse caso o saque é encerrado com erro de chave inexistente e o valor reservado é liberado, exatamente como se o provedor tivesse negado. O aviso chega pelo webhook de falha normal. Em contas com aceite síncrono a mesma checagem acontece na própria chamada e o erro volta na resposta.
Saques sem details.document são validados pelo provedor de forma assíncrona: a recusa chega depois por webhook, em geral sem detalhar o motivo. Por isso a chave entra no registro principalmente a partir de envios com documento; depois de registrada, a resposta guardada vale para qualquer envio para aquela chave, com ou sem documento. Informe o documento do recebedor sempre que possível: o erro volta na hora e com o motivo.
Provedor bloqueando consultas (429)
Quando acontece: o provedor bloqueou temporariamente as consultas de chave. Enquanto ele estiver bloqueando, a maior parte dos saques por chave com documento do recebedor informado é recusada na hora, sem transação, sem reserva e sem webhook.
- Dura só enquanto o provedor estiver bloqueando.
- A cada 30 segundos, no máximo um saque é liberado para testar se o provedor voltou. Esse saque é aceito normalmente (
201) e, se o bloqueio continuar, termina em erro pelo webhook, com o valor reservado devolvido. Quando o teste dá certo, a proteção é encerrada. - Esse bloqueio é do provedor (regra de consultas ao DICT), não uma punição da plataforma. A proteção existe justamente para evitar que ele aconteça.
A resposta traz o cabeçalho Retry-After e details.retryAfterSeconds (60).
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json{
"code": "TOO_MANY_PIX_KEYS_NOT_FOUND",
"message": "Muitas tentativas com chaves Pix inexistentes. Saque por chave bloqueado temporariamente; tente novamente em instantes.",
"details": {
"retryAfterSeconds": 60,
"source": "provider-guard"
}
}O que fazer: aguarde o tempo de Retry-After e tente de novo, com backoff se a recusa persistir. Nada foi criado nem reservado.
10:00 Provedor bloqueia consultas de chave
10:00 Saque por chave com documento -> 429 imediato, Retry-After: 60
10:00:30 Um saque é liberado como teste -> 201, se o provedor ainda bloquear, webhook de falha
10:01 Nova tentativa -> 429 (ainda bloqueado)
10:03 Provedor libera; a plataforma detecta sozinha
10:03 Nova tentativa -> 201, fluxo normalLimite de ritmo do provedor (envio mais lento)
Quando acontece: o volume de consultas se aproxima do limite do provedor.
O que a API responde: o saque é aceito normalmente (201) e pode levar até 90 segundos a mais para ser enviado ao provedor. O resultado chega pelo webhook como sempre. A espera só se aplica a saques com documento do recebedor informado, em contas com aceite assíncrono; saques sem documento não são atrasados.
O que fazer: nada. Não reenvie o saque por causa da demora: consulte o status antes (veja Monitoramento de Status).
10:00:00 Saque aceito -> 201 (PENDING)
10:01:20 Saque enviado ao provedor (atraso de até 90 s)
10:01:23 Webhook com o resultadoO que muda para quem integra
| Antes | Agora | |
|---|---|---|
| Chave inexistente repetida | 201 e webhook de falha a cada tentativa | 422 imediato enquanto o registro da chave estiver ativo, sem transação e sem webhook |
| Provedor bloqueando consultas | 201 e webhook de falha minutos depois | 429 imediato, sem transação e sem webhook, para quase todos. A cada 30 s um saque é liberado como teste e pode falhar por webhook |
| Limite de ritmo | 201 e webhook | 201 e webhook, envio até 90 s mais lento |
Você precisa tratar a recusa síncrona (422 e 429) na própria resposta da requisição. Para as recusas 422 e 429 nenhum webhook será enviado, então não fique esperando um. Um 201 recebido durante o bloqueio é um saque real (o teste do provedor) e segue o fluxo normal, inclusive o webhook.
Boas práticas
- Trate
PIX_KEY_NOT_FOUNDcomcached: trueexatamente como a falha de chave não encontrada que chegaria por webhook: peça ao usuário para conferir a chave. - Ao receber
PIX_KEY_NOT_FOUND, corrija a chave antes de reenviar. Não faça retry automático da mesma chave e respeiteretryAfterSeconds. - Não repita em laço a mesma chave inválida: a resposta será a mesma até o prazo vencer.
- Respeite
Retry-Afteredetails.retryAfterSeconds, com backoff se a recusa persistir. - Informe
details.documentdo recebedor sempre que souber: os erros chegam na hora, e não minutos depois por webhook. - Não reenvie o mesmo saque sem conferir o status. A idempotência é por
externalId; veja PIX Cash-Out.