Saque via PIX (cash-out) com segurança: chave, QR Code e allowlist de IP
Como implementar pagamentos de saída pela API Pagnovo — cash-out por chave PIX ou QR, allowlist de IP, escopos, restrição de recebedor e eventos de retorno.
Equipe Pagnovo · 2026-08-08
Receber dinheiro é relativamente seguro: no pior caso, o pagamento não acontece. Enviar dinheiro é outra história — um erro aqui significa recurso saindo da sua conta para o destino errado, muitas vezes sem volta.
Este guia cobre o cash-out da API Pagnovo com o foco certo: segurança primeiro.
Referência completa: portal.pagnovo.com/docs.
Duas formas de enviar
Por chave PIX (DICT)
POST /withdraws/cash-out
{
"amount": 50000,
"pixKey": "destinatario@email.com",
"restrictReceiverDocument": true
}
Por QR Code
POST /withdraws/cash-out/qrc
Use quando o destinatário apresenta um QR (cobrança de fornecedor, por exemplo) em vez de uma chave.
E, para acompanhar:
GET /withdraws/collect/:id → status e ciclo de vida do saque
As três camadas de proteção
O que separa uma integração de cash-out segura de um acidente esperando para acontecer:
1. Allowlist de IP
A Pagnovo permite restringir por IP as operações mais sensíveis — createWithdraw e
createRefund. Com a lista configurada, uma chamada vinda de qualquer outro IP recebe:
400 "IP unauthorized"
Isso significa que, mesmo se a sua chave vazar, ela não consegue movimentar dinheiro fora dos seus servidores. É a proteção com melhor custo-benefício de toda a integração — configure antes de ir para produção.
Lista vazia = sem restrição. Não deixe assim em produção.
2. Escopos mínimos na chave
Cada chave carrega escopos: leitura exige view<Recurso>, escrita exige manage<Recurso>. Sem o
escopo, a resposta é 403 "Unauthorized Permissions".
Aproveite isso: a chave que o seu checkout usa para criar cobranças não precisa poder sacar. Separe as chaves por função. Se a do front-end vazar, o estrago é limitado.
3. restrictReceiverDocument
{ "restrictReceiverDocument": true }
Com essa flag, o saque só é concluído se o CPF/CNPJ do titular da chave bater com o esperado. Protege contra o cenário clássico de fraude: alguém troca a chave PIX cadastrada e o dinheiro sai para outra pessoa.
Se você paga fornecedores ou faz repasses, use sempre.
O ciclo de vida e os eventos
Diferente do cash-in, o saque tem um estado a mais que precisa de tratamento:
| Evento | Significado | O que fazer |
|---|---|---|
cashout.success |
Saque concluído | Baixar no seu sistema |
cashout.failed |
Falhou | Investigar, notificar, permitir nova tentativa |
cashout.returned |
Devolvido | Recreditar e investigar |
O cashout.returned é o que costuma faltar nas integrações. O dinheiro saiu, foi rejeitado pelo
destino (conta encerrada, chave inválida, devolução) e voltou. Se o seu sistema não trata esse
evento, o saldo volta mas o seu registro continua dizendo "pago" — e a diferença só aparece na
conciliação, dias depois.
O saldo que você pode sacar
Antes de disparar um saque, cheque o saldo — e leia com atenção:
GET /accounts/balance
A resposta separa disponível, em liquidação, depósitos de garantia, bloqueios cautelares e reservas de chargeback. Apenas o disponível pode ser sacado.
Somar tudo e tentar sacar o total é caminho garantido para falha — e, pior, para prometer ao seu usuário um saque que não vai acontecer.
Idempotência: crítico em cash-out
Em cobranças, um webhook duplicado gera trabalho repetido. Em saques, uma requisição duplicada gera dinheiro enviado duas vezes.
Proteja-se antes de chamar a API:
// Trave ANTES de disparar
const lock = await db.withdrawLocks.insertIfAbsent({
key: `saque:${usuarioId}:${solicitacaoId}`,
});
if (!lock) throw new Error('Saque já solicitado');
const saque = await pagnovo.post('/withdraws/cash-out', {
amount, pixKey, restrictReceiverDocument: true,
});
await db.withdraws.save({ ...saque, traceId: response.headers['x-trace-id'] });
Regras práticas:
- Trave por solicitação, não por usuário
- Nunca retente automaticamente um saque cuja resposta você não recebeu — consulte
GET /withdraws/collect/:idprimeiro e descubra o que aconteceu - Grave o
x-trace-idde toda operação de saída; é o que o suporte usa para rastrear
Aprovação humana para valores altos
Nem tudo deve ser automático. Um padrão que evita prejuízos sérios:
valor <= limite automático → processa direto
valor > limite automático → fila de aprovação humana
Some a isso limites por período (diário/mensal) e alerta para padrões atípicos — muitos saques pequenos em sequência, saque logo após troca de chave cadastrada, saque para chave nunca usada antes.
Testando no sandbox
Com sk_test_*, você pode exercitar os caminhos de falha sem risco. Teste explicitamente:
- Saque bem-sucedido →
cashout.success - Falha →
cashout.failed - Devolução →
cashout.returned(o mais esquecido) - Chamada de IP não autorizado →
400 - Chave sem escopo de saque →
403
Lembre que o ambiente de teste permite 30 operações por dia (transações + saques + estornos).
Checklist
- Allowlist de IP configurada e não vazia
- Chave de saque separada da chave de cobrança, com escopos mínimos
-
restrictReceiverDocumentativo em repasses - Trava de idempotência antes da chamada
-
cashout.returnedtratado com recrédito - Saldo lido por categoria (só o disponível)
- Limite de valor com aprovação humana acima do teto
-
x-trace-idgravado em toda operação - Caminhos de falha e devolução testados no sandbox
Conheça nossa API PIX, veja a documentação de saques e leia nossa Política de Prevenção à Fraude. Fale com o time.