docs
Integration Guides

Safeguards on Pix key cash-out

Overview

Before sending a cash-out by key, the settlement provider looks the key up in the Pix directory (DICT). That provider limits lookups and may temporarily block an account that repeats lookups for non-existent keys. When that happens, every cash-out on the account suffers.

To prevent this, the platform answers right away with what it already knows and avoids repeated lookups that would make the provider block the account. There are three behaviors, visible in the response of POST /api/pix/cash-out:

SituationAPI responseTransaction created?Webhook?
Key the provider already reported as non-existentImmediate 422NoNo
Provider blocking lookupsImmediate 429 with Retry-AfterNoNo
Provider rate limitNormal 201, dispatch up to 90 s slowerYesYes, as usual

Repeating a key that was already denied does not block the account or the client: only that key has its answer stored (15 minutes, or up to 30 if the provider denies it again within 2 hours) and the platform returns the same denial without querying the provider. All other keys work normally.

This behavior depends on your account's settlement provider: it is active with some providers, and with the others the flow does not change. If in doubt about your account, contact your integration manager.

Non-existent key (422)

When it happens: the first attempt for a non-existent key follows your account's normal flow: with asynchronous acceptance, the API answers 201 and you later receive the failure webhook saying the key was not found; with synchronous acceptance, the error comes back in the response itself. If the provider has recently answered that the key does not exist, the denial can come already on your account's first attempt. When the provider answers that the key does not exist, the platform keeps that answer for 15 minutes. During that time, new cash-outs to that key get the same denial straight from the platform (422 PIX_KEY_NOT_FOUND, details.cached: true), without querying the provider.

  • If, after it expires, the provider denies the same key again within 2 hours, the new record lasts 30 minutes. That is the maximum; it never goes beyond it.
  • Attempts answered by the platform do not count and do not extend the period. Only a new denial from the provider itself counts.
  • Only that key is affected. The account, the client and all other keys keep working normally. This is not a block or a penalty.
  • Why it exists: the provider applies a DICT rule that suspends all key cash-outs of the account for about 30 minutes when the same non-existent key is queried repeatedly. Keeping the answer protects the whole account.
  • A newly registered key may take up to that period to be accepted.
  • No transaction, no balance reservation and no webhook.
  • The rejected request does not consume the externalId: you can reuse it.
{
  "code": "PIX_KEY_NOT_FOUND",
  "message": "Chave Pix não encontrada",
  "details": {
    "retryAfterSeconds": 780,
    "source": "provider-guard",
    "cached": true
  }
}

retryAfterSeconds (and the Retry-After header) always carry the real time remaining.

What to do: treat it like the key-not-found failure that would arrive by webhook: ask the user to check the key. If the key was just registered, wait retryAfterSeconds and try again.

The non-existent key denial can arrive in two ways. Treat both as the same situation (the key does not exist): fix it before resending.

Denial sourceHow it arrivesIdentification
ProviderCash-out failure (webhook)The provider's errorCode and errorMessage (the errorCode varies by settlement provider and should not be used for logic)
Platform record422 in the responsecode PIX_KEY_NOT_FOUND, details.cached: true and details.retryAfterSeconds

Example timeline:

10:00  Cash-out to key X
       -> 201, then failure webhook "key not found"
10:02  New cash-out to key X
       -> immediate 422 from the record, retryAfterSeconds ~ 780, cached: true
10:15  The record expires
10:16  New cash-out to key X
       -> queries the provider again
       -> if denied again, new 30-minute record (until 10:46)

Cash-out already accepted: on accounts with asynchronous acceptance (201 response and result by webhook), the key may be recorded as non-existent after the cash-out was accepted and before it is sent to the provider, for example when several cash-outs to the same key are sent at the same time. In that case the cash-out is closed with a non-existent key error and the reserved amount is released, exactly as if the provider had denied it. The notice arrives through the normal failure webhook. On accounts with synchronous acceptance the same check happens in the call itself and the error comes back in the response.

Cash-outs without details.document are validated by the provider asynchronously: the rejection arrives later by webhook, usually without detailing the reason. So the key enters the record mainly from cash-outs with a document; once recorded, the stored answer applies to any cash-out to that key, with or without a document. Send the recipient document whenever possible: the error comes back immediately and with the reason.

Provider blocking lookups (429)

When it happens: the provider temporarily blocked key lookups. While it keeps blocking, most key cash-outs with the recipient document provided are rejected immediately, with no transaction, no reservation and no webhook.

  • It lasts only while the provider is blocking.
  • Every 30 seconds, at most one cash-out is let through to test whether the provider is back. That cash-out is accepted normally (201) and, if the block continues, ends in an error through the webhook, with the reserved amount returned. When the test succeeds, the safeguard ends.
  • This block is the provider's (its DICT lookup rule), not a penalty from the platform. The safeguard exists precisely to prevent it from happening.

The response carries the Retry-After header and 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"
  }
}

What to do: wait the Retry-After time and try again, with backoff if the rejection persists. Nothing was created or reserved.

10:00  Provider blocks key lookups
10:00  Key cash-out with document -> immediate 429, Retry-After: 60
10:00:30  One cash-out is let through as a test -> 201, if the provider still blocks, failure webhook
10:01  New attempt -> 429 (still blocked)
10:03  Provider releases; the platform detects it on its own
10:03  New attempt -> 201, normal flow

Provider rate limit (slower dispatch)

When it happens: lookup volume approaches the provider limit.

What the API answers: the cash-out is accepted normally (201) and may take up to 90 seconds longer to be sent to the provider. The result arrives by webhook as usual. The wait only applies to cash-outs with the recipient document provided, on accounts with asynchronous acceptance; cash-outs without a document are not delayed.

What to do: nothing. Do not resend the cash-out because of the delay: check its status first (see Status Monitoring).

10:00:00  Cash-out accepted -> 201 (PENDING)
10:01:20  Sent to the provider (delay of up to 90 s)
10:01:23  Webhook with the result

What changes for integrators

BeforeNow
Repeated non-existent key201 and failure webhook on every attemptImmediate 422 while the key record is active, no transaction, no webhook
Provider blocking lookups201 and failure webhook minutes laterImmediate 429, no transaction, no webhook, for almost all. Every 30 s one cash-out is let through as a test and may fail by webhook
Rate limit201 and webhook201 and webhook, dispatch up to 90 s slower

You must handle the synchronous rejection (422 and 429) in the request response itself. For 422 and 429 rejections no webhook will be sent, so do not wait for one. A 201 received during the block is a real cash-out (the provider test) and follows the normal flow, including the webhook.

Best practices

  • Treat PIX_KEY_NOT_FOUND with cached: true exactly like the key-not-found failure that would arrive by webhook: ask the user to check the key.
  • On PIX_KEY_NOT_FOUND, fix the key before resending. Do not automatically retry the same key and respect retryAfterSeconds.
  • Do not loop on the same invalid key: the answer stays the same until the period expires.
  • Respect Retry-After and details.retryAfterSeconds, with backoff if the rejection persists.
  • Send the recipient details.document whenever you know it: errors arrive immediately instead of minutes later by webhook.
  • Do not resend the same cash-out without checking its status. Idempotency is by externalId; see PIX Cash-Out.

FAQ

On this page