Secure PIX cash-out: keys, QR Codes and IP allowlisting

How to implement outgoing payments with the Pagnovo API — cash-out by PIX key or QR, IP allowlist, scopes, receiver restriction and return events.

Pagnovo Team · 2026-08-08

Receiving money is relatively safe: worst case, the payment does not happen. Sending money is a different story — a mistake here means funds leaving your account to the wrong destination, often irreversibly.

This guide covers cash-out on the Pagnovo API with the right focus: security first.

Full reference: portal.pagnovo.com/docs.

Two ways to send

By PIX key (DICT)

POST /withdraws/cash-out

{
  "amount": 50000,
  "pixKey": "recipient@email.com",
  "restrictReceiverDocument": true
}

By QR Code

POST /withdraws/cash-out/qrc

Use it when the recipient presents a QR (a supplier invoice, for example) instead of a key.

And to track it:

GET /withdraws/collect/:id   → withdrawal status and lifecycle

The three layers of protection

What separates a safe cash-out integration from an accident waiting to happen:

1. IP allowlist

Pagnovo lets you restrict the most sensitive operations by IP — createWithdraw and createRefund. With the list configured, a call from any other IP receives:

400 "IP unauthorized"

That means even if your key leaks, it cannot move money from outside your servers. It is the best cost-benefit protection in the whole integration — configure it before going to production.

An empty list means no restriction. Do not leave it that way in production.

2. Minimum scopes on the key

Every key carries scopes: reads require view<Resource>, writes require manage<Resource>. Without the scope, the response is 403 "Unauthorized Permissions".

Take advantage of that: the key your checkout uses to create charges does not need to be able to withdraw. Separate keys by function. If the front-facing one leaks, the damage is bounded.

3. restrictReceiverDocument

{ "restrictReceiverDocument": true }

With this flag, the withdrawal only completes if the CPF/CNPJ of the key holder matches what you expect. It protects against the classic fraud scenario: someone swaps the registered PIX key and the money goes to a different person.

If you pay suppliers or make payouts, always use it.

The lifecycle and its events

Unlike cash-in, withdrawals have one extra state that needs handling:

Event Meaning What to do
cashout.success Withdrawal completed Settle it in your system
cashout.failed Failed Investigate, notify, allow a retry
cashout.returned Returned Re-credit and investigate

cashout.returned is the one integrations usually forget. The money left, was rejected by the destination (closed account, invalid key, refund) and came back. If your system does not handle this event, the balance returns but your record still says "paid" — and the difference only surfaces in reconciliation, days later.

The balance you can actually withdraw

Before firing a withdrawal, check the balance — and read it carefully:

GET /accounts/balance

The response separates available, in settlement, collateral deposits, precautionary blocks and chargeback reserves. Only the available portion can be withdrawn.

Summing everything and trying to withdraw the total is a guaranteed failure — and worse, a promise to your user of a withdrawal that will not happen.

Idempotency: critical in cash-out

With charges, a duplicate webhook creates repeated work. With withdrawals, a duplicate request creates money sent twice.

Protect yourself before calling the API:

// Lock BEFORE firing
const lock = await db.withdrawLocks.insertIfAbsent({
  key: `withdraw:${userId}:${requestId}`,
});
if (!lock) throw new Error('Withdrawal already requested');

const withdrawal = await pagnovo.post('/withdraws/cash-out', {
  amount, pixKey, restrictReceiverDocument: true,
});

await db.withdraws.save({ ...withdrawal, traceId: response.headers['x-trace-id'] });

Practical rules:

Human approval for large amounts

Not everything should be automatic. A pattern that prevents serious losses:

amount <= automatic limit  → process directly
amount >  automatic limit  → human approval queue

Add period limits (daily/monthly) and alerts for atypical patterns — many small withdrawals in sequence, a withdrawal right after a registered key change, a withdrawal to a key never used before.

Testing in the sandbox

With sk_test_* you can exercise the failure paths risk-free. Explicitly test:

Remember the test environment allows 30 operations per day (transactions + withdrawals + refunds).

Checklist


Explore our PIX API, see the withdrawals documentation and read our Fraud Prevention Policy. Talk to our team.