> ## Documentation Index
> Fetch the complete documentation index at: https://docs.purin.cash/llms.txt
> Use this file to discover all available pages before exploring further.

# Saques

> Tirar o saldo por PIX, LTC ou USDT sem sair do código. O PIX pode sair na hora, e aí não tem volta.

Um endpoint só, `POST /v1/payouts`. O que muda é o `method`: chave PIX, código copia e
cola, LTC ou USDT.

> [card] PIX
>
> Sai na hora ou entra na fila de aprovação. O `status` da resposta diz qual foi.

> [card] LTC
>
> Debita o saldo em Litecoin e envia na hora, da carteira da sua loja.

> [card] USDT
>
> Converte BRL e entrega USDT na rede BEP20 em segundos.

> [!WARNING]
>
> **Limite padrão de 10 saques por hora, por conta** (todas as chaves somam no mesmo
> balde). O envio imediato tem um teto próprio de 5 por hora, que corre por dentro desse.

## Saque PIX

```bash theme={null}
curl -X POST https://api.purin.cash/v1/payouts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "pix",
    "amount": 100.00,
    "walletAddress": "loja@exemplo.com"
  }'
```

```json Resposta 201 theme={null}
{
  "id": "SAQ-API-A1B2C3D4",
  "code": "SAQ-API-A1B2C3D4",
  "method": "pix",
  "amount": 100.00,
  "status": "pending",
  "turbo": false,
  "sandbox": false
}
```

**method** `string` *required*

`pix`, `pix_code` (código copia e cola), `ltc` ou `usd`.

**amount** `number` *required*

Valor em reais. O mínimo é o configurado para a **sua conta** (R\$ 5,00 por padrão;
o erro de valor baixo devolve `minAmount` com o mínimo vigente). Máximo de
R\$ 999.999,99, sempre limitado ao `withdrawable` da [carteira](https://docs.purin.cash/guias/carteira.md).

**walletAddress** `string` *required*

Chave PIX, endereço LTC ou endereço USDT BEP20, conforme o `method`.

**cryptoAmount** `number`

Quantidade em LTC. Obrigatório quando `method` é `ltc`.

> [!NOTE]
>
> A chave PIX precisa estar verificada no painel antes do primeiro saque. Sem isso a
> resposta é `403`.

## Saque LTC

```bash theme={null}
curl -X POST https://api.purin.cash/v1/payouts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "ltc",
    "amount": 100.00,
    "cryptoAmount": 0.5,
    "walletAddress": "ltc1q..."
  }'
```

O `cryptoAmount` é obrigatório aqui e precisa ser maior que zero. O saldo debitado é o
`cryptoBalanceLtc` da carteira, não o saldo em reais.

O saque LTC sai **na hora**, da carteira da sua loja, sem passar por aprovação. A
resposta diz como ele terminou:

| `status` | O que significa | O que fazer |
| - | - | - |
| `completed` | A transação foi para a rede. O `txHash` dela vem na resposta | Conferir no explorer, se quiser |
| `processing` | Enviando, ou transmitido sem confirmação da rede | **Não repita o pedido**: a transação pode já estar na rede |
| `pending` | Não saiu agora. Nada foi transmitido | Nada: o envio tenta de novo sozinho em alguns minutos |

> [!WARNING]
>
> Transação cripto confirmada na rede não volta. Confira o endereço e a rede antes de
> pedir: endereço de outra rede perde o valor. O mesmo saque repetido em 10 segundos é
> recusado com `429`, para um retry automático não virar dois envios.

## Ler um código antes de pagar

> [!NOTE]
>
> **O caminho recomendado tem três passos:** `POST /v1/payouts/decode` para ler o código,
> a sua confirmação (tela, aprovação interna, o que fizer sentido) e só então
> `POST /v1/payouts` com `method: "pix_code"`. Pagar direto funciona, mas aí você só
> descobre destino e valor depois que o PIX saiu, e ele não volta.

`POST /v1/payouts/decode` diz o que tem dentro do BR Code sem mover dinheiro: destino,
valor, se o valor é fixo e a taxa que entra por cima. É com ela que você monta a tela de
confirmação, ou a checagem automática que compara o que o código diz com o que você
esperava pagar.

```bash theme={null}
curl -X POST https://api.purin.cash/v1/payouts/decode \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "brCode": "00020126..." }'
```

```json theme={null}
{
  "valid": true,
  "isDynamic": false,
  "amount": 149.90,
  "amountLocked": true,
  "pixKey": "financeiro@fornecedor.com",
  "merchantName": "FORNECEDOR LTDA",
  "receiverName": "FORNECEDOR LTDA",
  "receiverDocument": "12.***.***/0001-**",
  "expiresAt": "",
  "txid": "PEDIDO123",
  "dynamicResolved": false,
  "dynamicError": "",
  "fee": 1.00
}
```

| Campo | |
| - | - |
| `isDynamic` | `true` quando o código aponta para uma cobrança no banco em vez de carregar os dados. Nesse caso é a leitura que resolve valor e destino |
| `amountLocked` | O código FIXA o valor. Quando `false`, é você quem diz quanto no pagamento |
| `dynamicResolved` | Num dinâmico, se o valor veio de fonte autoritativa. `false` com `amount` nulo significa "não deu pra ler agora", que é bem diferente de "esse QR não tem valor" |
| `fee` | A taxa, que é SOMADA ao valor do código. O QR diz quanto o recebedor recebe; o débito é valor mais taxa |
| `receiverName` / `receiverDocument` | Quem recebe, confirmado na leitura. O documento vem **mascarado** (`12.***.***/0001-**`): serve pra pessoa conferir na tela, não pra cadastro |
| `amount` | Valor do código, em reais. `null` quando o código não traz valor e a leitura também não resolveu |
| `expiresAt` / `txid` | Vencimento e identificador da cobrança, quando o código os carrega |

Precisa da mesma permissão do pagamento (`saques.pix`): quem pode pagar pode inspecionar.
Não existe em sandbox, porque a leitura consulta o banco sobre uma cobrança de verdade.

Código inválido, ou destino que não pode ser pago por aqui, vem como `400` com
`valid: false` e o motivo.

## Pagar um código copia e cola

`method: "pix_code"` paga um BR Code (o "copia e cola" do PIX) com o saldo da carteira.
Serve pra quitar cobrança de fornecedor, boleto-QR, conta de serviço: em vez de você
informar a chave do destino, o **código** carrega o destino e o valor.

Antes disto, [leia o código](#ler-um-codigo-antes-de-pagar). O pagamento é imediato e
não tem cancelamento.

```bash theme={null}
curl -X POST https://api.purin.cash/v1/payouts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "pix_code",
    "brCode": "00020126580014BR.GOV.BCB.PIX0136..."
  }'
```

```json Resposta 200 theme={null}
{
  "success": true,
  "payment": {
    "id": "665f1a2b3c4d5e6f7a8b9c0d",
    "code": "PAY-A1B2C3D4",
    "amount": 100.00,
    "fee": 1.00,
    "debited": 101.00,
    "status": "completed",
    "endToEndId": "E1234567890...",
    "txId": "...",
    "merchantName": "NOME DO RECEBEDOR",
    "recipientDocument": "12345678909",
    "isDynamic": false
  }
}
```

> [!WARNING]
>
> **A taxa é somada, não descontada.** O código diz quanto o recebedor recebe, e a taxa
> vem por cima: sai da carteira `amount + fee`, que é o campo `debited`. São esses três
> números que precisam bater no seu financeiro.

| Campo da resposta | |
| - | - |
| `id` / `code` | Identificadores do pagamento. O `code` (`PAY-...`) é o que aparece em `GET /v1/payouts` |
| `amount` / `fee` / `debited` | Quanto o recebedor recebe, a taxa e o total que saiu da carteira |
| `status` | `completed` ou `processing`. Os dois significam que o PIX saiu |
| `endToEndId` | O E2E do PIX, o mesmo que aparece no extrato de quem recebeu. Pode vir nulo enquanto o banco não devolve |
| `merchantName` / `recipientDocument` | Quem recebeu, como o banco confirmou. Aqui o documento vem completo, diferente do decode, porque é o dado do comprovante |
| `isDynamic` | Se o código pagava uma cobrança em vez de carregar os dados |

### Quem manda no valor

Quase sempre o código, não você:

| Tipo de código | De onde vem o valor | `amount` no corpo |
| - | - | - |
| QR com valor fixo | Do próprio código | Opcional. Se enviar e divergir, é `400` |
| QR dinâmico | Da cobrança, lida no banco na hora do pagamento | Opcional. Se enviar e divergir, é `400` |
| QR estático sem valor | Só aí é você quem diz | **Obrigatório** |

**brCode** `string` *required*

O copia e cola completo. Quebras de linha são ignoradas; o resto vai íntegro, porque é
ele que carrega o destino.

**amount** `number`

Valor em reais. Só entra em QR estático sem valor: nos demais, o valor é o do código,
e enviar um número diferente devolve `400` em vez de pagar.

Limites: de R\$ 1,00 a R\$ 5.000,00 **por código**, sobre o valor que o recebedor recebe
(a taxa corre por fora). Precisa da permissão `saques.pix` na chave.

### Como saber o desfecho

Pagamento de código não é saque, então ele tem família própria de evento:
`pix_payment.completed` e `pix_payment.failed`. Eles carregam `paymentId`, que é o mesmo
`payment.id` devolvido pelo pagamento, pra amarrar os dois sem tabela de-para.

Isso importa porque a resposta do pagamento pode vir `processing`: o banco não confirmou
ainda, e quem fecha é a conciliação minutos depois. Sem escutar o evento, esse pagamento
fica sem desfecho do seu lado. Nunca repita um `processing`: ele pode ter saído.

O corpo do evento, campo a campo, incluindo E2E, recebedor e a URL do comprovante, está
em [webhooks de saque](https://docs.purin.cash/webhooks/saques.md).

### O que pode barrar antes de pagar

| Código | Situação |
| - | - |
| `403` | A conta ainda não fez o primeiro saque PIX. Faça um saque comum antes |
| `403` | Chave PIX da conta não verificada |
| `400` com `blocked: true` | O código aponta para um destino da própria plataforma. Para adicionar saldo, use um depósito |
| `400` | Código inválido, valor fora da faixa, ou saldo insuficiente (a resposta traz `available`, `required` e `fee`) |
| `409` | Já existe um pagamento em processamento |
| `429` | Pagamento idêntico (mesmo código e valor) nos últimos 90s, ou outro em andamento |
| `503` | Pagamento por código indisponível no momento. Nada foi debitado |

> [!NOTE]
>
> Não funciona em sandbox: o pagamento lê um código real e envia PIX real. Com chave
> `ps_test_` a resposta é `400`, tanto aqui quanto no decode.

### Quando o banco não confirma

As mesmas três saídas do envio imediato, pelo mesmo motivo:

**`200`, `status: "completed"` ou `"processing"`.** O PIX saiu. Em `processing` só falta a
confirmação do banco: trate como pago e **não reenvie**.

**`400`, `refunded: true`.** O banco recusou e o saldo voltou inteiro, taxa junto: não
houve serviço pra cobrar.

**`202`, `requiresReview: true`.** Não veio resposta do banco. O pagamento **pode ter
saído**, então o valor continua debitado até a verificação. Não repita: acompanhe
pelo `code` (`PAY-...`) em `GET /v1/payouts`.

## Quando o PIX sai na hora

O saque PIX pode ser enviado dentro da própria requisição, sem passar por fila. É a
mesma chamada de sempre: a plataforma decide a rota e o `status` da resposta diz o que
aconteceu.

```json Resposta 201 theme={null}
{
  "id": "TURBO-A1B2C3D4",
  "code": "TURBO-A1B2C3D4",
  "method": "pix_turbo",
  "amount": 100.00,
  "fee": 1.00,
  "netAmount": 99.00,
  "status": "completed",
  "txId": "...",
  "recipientName": "NOME DO RECEBEDOR",
  "turbo": true,
  "sandbox": false
}
```

> [!WARNING]
>
> **Envio imediato não tem volta.** O dinheiro sai no mesmo request e não existe
> cancelamento depois. Quem tem a sua chave de API consegue mandar dinheiro pra fora
> sem passar por nenhuma aprovação. Guarde a chave como você guardaria a senha do painel.

Quando o envio é imediato, valem estas regras:

| Regra | Detalhe |
| - | - |
| Método | Só `pix`. `ltc` e `usd` seguem o fluxo próprio deles |
| Valor | Do mínimo da sua conta (R\$ 5,00 por padrão) a R\$ 5.000,00 por saque |
| Limite | 5 por hora, dentro do limite geral de 10 |
| Taxa | Descontada do envio (veja `fee` e `netAmount` na resposta) |
| Concorrência | Um por vez. Envio simultâneo ou em sequência curta devolve `429`; com um envio recente ainda `processing`, `409` |
| Falha | Devolve **erro** com `refunded`. Vira pendente só nos casos com `turboReason` abaixo, nunca por falha |

É a mesma API do saque comum. **Quem decide a rota é a plataforma**, e o seu código
trata **pelo `status` da resposta**, nunca presumindo qual foi.

Todo saque PIX pela API sai imediato por padrão. Três casos fogem disso, e em todos
o pedido continua valendo: ele entra na fila de aprovação em vez de virar erro. A
resposta é `201` com `status: "pending"` e um `turboReason` dizendo por quê:

| `turboReason` | Quando | O que fazer |
| - | - | - |
| `first_withdrawal_manual` | Primeiro saque PIX da conta. Acontece uma vez só | Nada. Depois dele, saem imediatos os saques pra chaves já aprovadas na conta |
| `new_pix_key_manual` | A chave PIX ainda não foi aprovada na sua conta, e a conta não tem o envio imediato liberado pra qualquer chave | Nada. Acompanhe pelo webhook. Pra chaves novas saírem imediatas, peça ao suporte a liberação pra qualquer chave |
| `turbo_maintenance` | Janela de manutenção do envio imediato | Nada. Acompanhe pelo webhook, como qualquer saque pendente |
| `risco_conta` | A análise automática de risco segurou o saque da conta para conferência (padrão de movimentação fora do habitual) | Nada. A conferência é feita pela equipe e o saque sai em seguida, ou é devolvido ao saldo. Acompanhe pelo webhook |
| `risco_subconta` | O mesmo, para um saque de subconta. Só aquela subconta fica com o saque suspenso até a conferência | Nada. Acompanhe pelo webhook. As outras subcontas e a conta seguem normais |

Em todos o saldo já foi debitado no `201`, igual ao envio imediato. O que muda é
quando o dinheiro chega ao destino.

### O que cada resposta significa

Escreva o seu handler pra estes casos e ele cobre as duas rotas de uma vez:

**`201`, `status: "pending"`.** Entrou na fila de aprovação, pelo motivo que vem em
`turboReason`. Você recebe [`withdrawal.completed` ou
`withdrawal.denied`](https://docs.purin.cash/webhooks/saques) quando resolver. Não reenvie: o saldo já saiu
da carteira e um segundo pedido vira um segundo saque.

**`201`, `status: "completed"`.** Pago e confirmado. O `recipientName` é o nome que
o banco registrou como dono da chave: confira que pagou a pessoa certa.

**`201`, `status: "processing"`.** O PIX **já saiu**; só falta a confirmação do
banco, que chega pelo webhook. Trate como pago, não como pendente, e não reenvie.

**`400`.** O pedido está errado e nada se moveu (ou o saldo já voltou, quando a
resposta traz `refunded: true`). A mensagem diz o quê: valor fora da faixa (o erro
de mínimo traz `minAmount`), chave vazia, saldo insuficiente (`available` na
resposta), ou o banco recusou a chave do recebedor:

```json theme={null}
{
  "error": "A chave PIX foi recusada pelo banco. Confira a chave do recebedor e tente de novo. Banco: Invalid Pix Key. Seu saldo foi restaurado.",
  "bankMessage": "Invalid Pix Key",
  "bankErrorCode": "chave_invalida",
  "definitiveRejection": true,
  "refunded": true,
  "code": "TURBO-A1B2C3D4",
  "turbo": true
}
```

`definitiveRejection: true` significa que repetir o mesmo request **nunca** vai
funcionar: conserte a chave antes de tentar de novo.

**`502`, `refunded: true`.** O envio não pôde ser concluído e o saldo já voltou.
Nada saiu. Tente de novo mais tarde.

**`202`, `requiresReview: true`.** O único caso que exige cuidado: a transferência
**pode ter sido enviada** e a confirmação não chegou. O valor permanece debitado até
a verificação: é isso que garante que o mesmo saque nunca vira dois PIX. Não
repita o request; acompanhe o `code` em `GET /v1/payouts` e pelo webhook.

**`409` / `429`.** Já existe um envio em andamento (ou requests demais em
sequência). Dinheiro pode estar em trânsito: aguarde, consulte `GET /v1/payouts` e
só então decida reenviar.

**`403`.** Chave PIX pendente de verificação. Resolve no painel, em Carteira.

**`503`.** A plataforma não conseguiu confirmar as condições do saque e **nada foi
debitado**. Repita. Janela de manutenção do envio imediato não cai aqui: ela volta
`201` com `turboReason: "turbo_maintenance"`, porque o pedido foi aceito.

Regra de bolso: `201` olhe o `status`; `400` conserte e repita; `502`/`503` repita
mais tarde; `202`/`409`/`429` congele e consulte; `refunded: true` diz se o saldo já
voltou.

## Saque em USDT

Converte o saldo em reais e envia USDT na rede BEP20 na hora.

```bash theme={null}
curl -X POST https://api.purin.cash/v1/payouts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "usd",
    "amount": 100.00,
    "walletAddress": "0xAbC1230000000000000000000000000000000000",
    "confirmNotCoinbase": true
  }'
```

```json Resposta 201 theme={null}
{
  "success": true,
  "withdrawal": {
    "id": "665f1a2b3c4d5e6f7a8b9c0d",
    "code": "SAQ-U-1A2B3C4D5E6F7A8B",
    "amountBRL": 100.00,
    "receiveUSDT": 17.42,
    "rateBRLPerUSDT": 5.74,
    "status": "processando",
    "estimatedTime": "menos de 30 segundos"
  }
}
```

> [!WARNING]
>
> `confirmNotCoinbase: true` é obrigatório. Depósito da Coinbase não aceita USDT BEP20, e
> o valor enviado para lá é perdido, sem recuperação. A confirmação existe para você parar
> e conferir a rede do endereço antes de mandar.

> [!NOTE]
>
> Por segurança contra fraude, o saque em USDT só é liberado depois que a loja fizer o
> primeiro saque em PIX. Antes disso a resposta é `403`.

## Status

**pending** `PIX comum`

Aguardando aprovação.

**completed** `todos`

Pago.

**denied** `todos`

Negado. O valor volta para o saldo.

**processing** `turbo e USDT`

O pagamento foi enviado e a confirmação ainda não voltou. **O dinheiro já saiu.** Não
repita o request.

## Erros e o que fazer

| Código | Situação | O que fazer |
| - | - | - |
| `400` | Saldo insuficiente, valor abaixo do mínimo da conta (`minAmount` na resposta), endereço inválido, `confirmNotCoinbase` ausente, ou recusa **definitiva** do banco no turbo (`definitiveRejection: true`) | Corrija e reenvie. Recusa definitiva não melhora com retry: conserte a chave |
| `403` | Chave PIX não verificada, ou USDT antes do primeiro saque PIX | Resolva no painel |
| `409` | Já existe um saque turbo em processamento | Espere e tente de novo |
| `429` | Passou de 10 por hora, 5 no turbo, ou request turbo duplicado em 10s | Espere a janela virar |
| `502` | O envio imediato não pôde ser concluído. O saldo é devolvido (`"refunded": true`) | Use o saque comum; turbo mais tarde |
| `503` | Turbo ou saque em cripto indisponível no momento | Tente mais tarde ou use o saque normal |
| `202` | Sem resposta do banco no envio imediato | **Não repita.** O PIX pode ter saído e passa por verificação |

> [!WARNING]
>
> `202` e `processing` são os dois casos em que reenviar o request paga duas vezes.
> Trate ambos como sucesso provisório e reconcilie por `GET /v1/payouts`.

## Listando

```bash theme={null}
curl "https://api.purin.cash/v1/payouts?limit=20&status=pendente" \
  -H "Authorization: Bearer $PURINCASH_KEY"
```

```json theme={null}
{
  "payouts": [
    {
      "id": "SAQ-API-A1B2C3D4",
      "code": "SAQ-API-A1B2C3D4",
      "method": "pix",
      "amount": 100.00,
      "cryptoAmount": null,
      "walletAddress": "12345***",
      "status": "pendente",
      "createdAt": "2026-04-27T12:00:00.000Z"
    }
  ]
}
```

> [!NOTE]
>
> `walletAddress` volta sempre mascarado, com os primeiros caracteres e `***`. Dado
> sensível (CPF, chave PIX completa) não aparece em resposta nem em log.

> [!NOTE]
>
> A listagem devolve o status cru, em português: `pendente`, `concluido`, `negado`,
> `processing` (turbo aguardando o banco) e `falhou`. São esses os valores do filtro
> `?status=`. O `status` em inglês (`pending`/`completed`/`processing`) aparece só na
> resposta do POST.

Cada mudança de status também dispara um [webhook de saque](https://docs.purin.cash/webhooks/saques.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.