Saques
O termo de produto é Saque (
payout). O endpoint atual é/v1/withdrawals— o nome de fio em uso hoje, ainda não renomeado para/v1/payouts.
Tira saldo para fora da sua carteira Liqfy, rumo a um destino real — uma chave Pix ou uma conta bancária (TED). É o inverso do pagamento Pix: pagamento credita sua carteira, saque debita.
Consultar pelo número do pedido: envie esse número como Idempotency-Key ao criar o saque. Depois consulte GET /v1/withdrawals/by-idempotency-key/{numero-do-pedido} com uma chave de API da mesma conta. Um saque criado sem essa chave não pode ser encontrado pelo número do pedido. Veja os exemplos de consulta.
Comprovante: GET /v1/withdrawals/{id}/receipt?format=json devolve dados estruturados; sem format, continua devolvendo o PDF no envelope {contentType, base64}. Veja Comprovante e Contrato do comprovante JSON.
No callback payout.paid, o E2E Pix pode vir em data.object.settlement.end_to_end_id quando já foi confirmado. Em saques divididos, consulte os E2Es de settlement.legs[]; a ausência de E2E não indica falha do saque. Veja Webhooks.
A disponibilidade dos trilhos é por conta. TED depende de o módulo correspondente estar habilitado para você. Confirme com seu contato na Liqfy antes de construir em cima de um deles.
#O fluxo, de relance
1. POST /v1/withdrawals/validate-pix-key (opcional — checagem prévia)
2. POST /v1/withdrawals (cria a solicitação e reserva o valor na hora)
3. A Liqfy despacha — automaticamente ou após aprovação
4. O webhook payout.paid (ou payout.failed) chega no seu endpointSaque usa a mesma chave de API das cobranças — Authorization: Bearer <chave>, chamada do seu servidor, nunca do navegador ou do app do cliente final. (O cabeçalho apikey ainda é aceito.) Chamadas por chave de API também passam pela lista de IPs permitidos da sua conta. A carteira é debitada na criação; se o saque também precisa de aprovação depende da configuração da sua conta.
Mande sempre o cabeçalho Idempotency-Key na criação — ele é o canal principal de idempotência (veja Criar o saque).
#1. Validar uma chave Pix (recomendado)
Antes de criar o saque, confira se a chave de destino está bem formada.
POST /v1/withdrawals/validate-pix-key
{
"key": "12345678909",
"type": "CPF"
}Resposta 201 Created
{
"isValid": true,
"type": "CPF",
"normalized": "12345678909"
}Valores aceitos em type
| Tipo | Formato |
|---|---|
CPF | 11 dígitos (pontos e traços são removidos), dígito verificador mod-11. Números com todos os dígitos iguais são recusados. |
CNPJ | 14 dígitos (pontos, traços e barras são removidos), dígito verificador mod-11. Números com todos os dígitos iguais são recusados. |
EMAIL | Formato local@dominio.tld, convertido para minúsculas, até 77 caracteres. |
PHONE | +55 seguido de 10 ou 11 dígitos (+5511999999999). Espaços, traços e parênteses são removidos. |
RANDOM | Chave aleatória — UUID v4, em minúsculas. |
| (omitido) | Detectado automaticamente pelo valor da chave. |
O tipo não diferencia maiúsculas de minúsculas. Um tipo conhecido declarado é validado à risca — declarar CPF para um e-mail reprova. Declarar qualquer outro tipo (por exemplo EVP) é ignorado: a chave é detectada automaticamente e a resposta informa o tipo detectado.
Ordem da detecção automática: UUID v4 → RANDOM; começa com +55 → PHONE; formato de e-mail → EMAIL; 11 dígitos → CPF; 14 dígitos → CNPJ. Um telefone enviado sem +55 (11 dígitos) é, portanto, detectado como CPF — mande telefones em E.164 ou declare PHONE. Quando nada casa, a resposta é isValid: false com type: "CPF" como marcador e error: "Unable to detect PIX key type".
O endpoint responde 201 mesmo quando a chave é inválida — a falha mora no corpo, nunca no status HTTP. Só key ausente ou vazia dá 400.
Falha 201 Created
{
"isValid": false,
"type": "CPF",
"normalized": "12345678900",
"error": "CPF checksum invalid"
}Valores possíveis de error
error | Tipo |
|---|---|
CPF must be 11 digits / CPF checksum invalid | CPF |
CNPJ must be 14 digits / CNPJ checksum invalid | CNPJ |
Invalid email format / Email exceeds 77 characters | EMAIL |
Phone must start with +55 / Phone must have 10 or 11 digits after +55 | PHONE |
Random key must be a valid UUID v4 | RANDOM |
Unable to detect PIX key type | detecção automática falhou |
PIX key must not be empty | chave só com espaços |
#2. Cotação, taxa e saldo sacável
Três ajudas de leitura, com a mesma chave de API da criação. currency é opcional em todas (padrão BRL).
#Cotação — quanto enviar para o recebedor receber um valor exato
POST /v1/withdrawals/quote — corpo { value, currency? }, value inteiro maior que zero. Precificado como Pix. Corpo inválido → 400.
Resposta 201 Created
{
"value": 50000,
"fee": 100,
"total": 50100
}total é o bruto que você paga para o recebedor receber exatamente value — a taxa é gross-up, o destino nunca a absorve.
curl -X POST https://api.liqfy.com.br/v1/withdrawals/quote \
-H "Authorization: Bearer $LIQFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "value": 50000 }'import { LiqfyClient } from '@liqfy/node';
const client = new LiqfyClient({ apiKey: process.env.LIQFY_API_KEY! });
const quote = await client.payouts.quote({ value: 50000 });Python client.payouts.quote(value=50000), PHP $client->payouts->quote(50000), .NET client.Payouts.QuoteAsync(50000).
#Taxa — o que a criação cobraria
GET /v1/withdrawals/payout-fee
| Query | Obrigatório | Descrição |
|---|---|---|
amount | sim | Inteiro, 1 a 10.000.000.000 (menor unidade). |
currency | não | Padrão BRL. De 3 a 8 caracteres. |
type | não | PIX (padrão) ou TED. |
Qualquer valor fora desses limites → 400.
Resposta 200 OK
{
"amount": 50000,
"currency": "BRL",
"fee": 100,
"total": 50100
}fee é o que o POST /v1/withdrawals cobraria por esse valor (já incluindo o complemento de pernas acima do teto do Pix); total = amount + fee.
curl "https://api.liqfy.com.br/v1/withdrawals/payout-fee?amount=50000¤cy=BRL&type=PIX" \
-H "Authorization: Bearer $LIQFY_API_KEY"const fee = await client.payouts.payoutFee({ amount: 50000 });Python client.payouts.payout_fee(amount=50000), PHP $client->payouts->payoutFee(50000), .NET client.Payouts.PayoutFeeAsync(50000).
#Saldo sacável e parâmetros de taxa
GET /v1/withdrawals/payout-info — sem efeito colateral, feito para o botão "sacar tudo".
Resposta 200 OK
{
"currency": "BRL",
"available": 1000000,
"pendingAdjustment": 0,
"fee": { "fixed": 100, "variablePct": 0, "minimum": 100 },
"maxWithdrawable": 999900
}available é o saldo gastável. maxWithdrawable é o maior valor bruto pedível com a taxa já descontada. fee é o conjunto de parâmetros aplicado hoje (fixed, variablePct, minimum).
curl "https://api.liqfy.com.br/v1/withdrawals/payout-info?currency=BRL" \
-H "Authorization: Bearer $LIQFY_API_KEY"const info = await client.payouts.payoutInfo();Python client.payouts.payout_info(), PHP $client->payouts->payoutInfo(), .NET client.Payouts.PayoutInfoAsync().
#3. Criar o saque
POST /v1/withdrawals
Idempotência. Mande o cabeçalho Idempotency-Key — o canal principal, com espaços aparados e no máximo 255 caracteres (cabeçalho maior → 400 "Idempotency-Key must be at most 255 characters"). O campo idempotencyKey no corpo (de 1 a 255 caracteres) é um fallback para quem não usa SDK; quando os dois existem, vale o cabeçalho. A mesma chave devolve o mesmo saque: 201 de novo, sem segundo débito. Se a reserva falhar, a linha vira REJECTED e a chave é liberada — depois de depositar você pode repetir com a mesma chave.
Sem chave, a única proteção é um quase-duplicado: o mesmo type, amount e pixKey em 90 segundos responde 409 sem error.code estável. Mande uma chave de idempotência e você nunca depende disso.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | enum | sim | PIX ou TED. (CRYPTO existe, mas é só pelo painel — veja abaixo.) |
amount | inteiro | sim | Inteiro positivo, menor unidade da moeda. No modo padrão gross é o que o recebedor recebe; a taxa é debitada por cima. |
currency | string | não | Padrão BRL. |
pixKey | string | quando PIX | Chave Pix de destino. |
pixKeyType | enum | não | CPF, CNPJ, EMAIL, PHONE, RANDOM. Omitido ou desconhecido (por exemplo EVP): ignorado, a chave é detectada automaticamente. |
bankCode | string | quando TED | Código do banco (FEBRABAN). |
bankAgency | string | quando TED | Agência. |
bankAccount | string | quando TED | Número da conta. |
walletId | string | não | Carteira OPERATIONAL explícita e gastável em currency. Omitido → seleção automática (a carteira padrão da moeda se tiver saldo, senão qualquer carteira gastável com saldo suficiente). Desconhecida → WALLET_NOT_FOUND; não gastável → WALLET_NOT_SPENDABLE; nada cobre o débito → INSUFFICIENT_BALANCE. |
amountType | enum | não | gross (padrão) ou net. net é legado e ainda aceito: o débito é amount e o recebedor recebe amount − taxa (fee >= amount → 400). |
idempotencyKey | string | não | Fallback — prefira o cabeçalho Idempotency-Key. |
totpCode | string | não | Só no painel: o código 2FA quando a conta exige 2FA em saques. Ignorado em chamadas por chave de API. |
Chaves desconhecidas (description, cryptoAddress, cryptoNetwork, cryptoCurrency, qualquer outra) são removidas em silêncio — a API nunca guarda nem devolve esses campos. cryptoAddress, cryptoNetwork e cryptoCurrency ainda podem aparecer como campos de resposta legados, somente leitura; não são entrada de criação.
Saque cripto é só pelo painel.
type: "CRYPTO"(comcryptoWalletAddressIdecryptoEstimateId) só é aceito de um usuário logado no painel; com chave de API responde403com o tokenCRYPTO_PANEL_ONLY. O mesmo vale paraPOST /v1/withdrawals/crypto/estimate;GET /v1/withdrawals/crypto/availabilityresponde{ "enabled": false }para chave de API. No painel, o saque cripto precisa ser emBRL, a partir de R$ 5.000,00 (500000), semnete com os dois ids (senão400INVALID_CRYPTO_WITHDRAWAL), e sempre pede o código 2FA.
Exemplo — saque Pix
curl -X POST https://api.liqfy.com.br/v1/withdrawals \
-H "Authorization: Bearer $LIQFY_API_KEY" \
-H "Idempotency-Key: saque-2026-S17" \
-H "Content-Type: application/json" \
-d '{
"type": "PIX",
"amount": 50000,
"pixKey": "12345678909",
"pixKeyType": "CPF"
}'Exemplo — saque TED (os dados bancários são três strings planas)
curl -X POST https://api.liqfy.com.br/v1/withdrawals \
-H "Authorization: Bearer $LIQFY_API_KEY" \
-H "Idempotency-Key: saque-2026-S17-ted" \
-H "Content-Type: application/json" \
-d '{
"type": "TED",
"amount": 50000,
"bankCode": "001",
"bankAgency": "1234",
"bankAccount": "56789012"
}'const payout = await client.payouts.create(
{ type: 'PIX', amount: 50000, pixKey: '12345678909', pixKeyType: 'CPF' },
{ idempotencyKey: 'saque-2026-S17' },
);
// TED: { type: 'TED', amount: 50000, bankCode: '001', bankAgency: '1234', bankAccount: '56789012' }Python client.payouts.create(input, idempotency_key=…), PHP $client->payouts->create($input, $idempotencyKey), .NET client.Payouts.CreateAsync(input, idempotencyKey).
Resposta 201 Created
{
"id": "f6a7b8c9-d0e1-4234-9f01-234567890123",
"type": "PIX",
"status": "PENDING",
"amount": 50000,
"fee": 100,
"netAmount": 50000,
"currency": "BRL",
"source": "API",
"walletId": "2c9f8e7d-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"pixKey": "12345678909",
"pixKeyType": "CPF",
"idempotencyKey": "saque-2026-S17",
"createdAt": "2026-04-25T16:10:00.000Z",
"updatedAt": "2026-04-25T16:10:00.000Z"
}A carteira é debitada na criação. No modo gross (o padrão) o débito é amount + fee (aqui 50.100) e o recebedor recebe exatamente amount — por isso netAmount é igual a amount. O valor fica reservado a partir daqui; a aprovação só decide quando o saque é despachado.
Um 201 já pode voltar APPROVED ou PROCESSING: o auto-payout aprova e despacha na hora quando a política da sua conta permite. "Sempre começa PENDING" não é garantido.
Quando o saque não vai. O contrato completo da recusa — o
error.codeda recusa imediata por saldo, os códigos de falha (failure_code) e quais deles estornam sozinhos — está em Falhas de saque.
#Criar pelo painel
- No painel, só o dono ou um admin da conta cria saque (membro comum →
403,code=WITHDRAWAL_CANCEL_ROLE_REQUIRED). - Quando a conta exige 2FA em saques (e sempre, no cripto), o pedido precisa de um
totpCodeatual. Usuário sem 2FA ativado recebe403("Esta conta exige 2FA para saques…"); código ausente ou inválido recebe401("Código 2FA inválido ou ausente."). - Saques criados no painel têm
source: "PANEL"; por chave de API,source: "API".
#Pedido recusado deixa rastro
Toda recusa 4xx do POST /v1/withdrawals (saldo, KYC, validação, limites, regras…) é registrada no log de auditoria da conta como WITHDRAWAL_REJECTED e aparece na tela de saques, com valor, origem e motivo — por exemplo "Pedido de saque de R$ 500,00 recusado: saldo insuficiente (disponível R$ 120,00), via API". O mesmo pedido repetido com a mesma chave de idempotência e o mesmo código de recusa é registrado uma vez por hora. Recusas pela lista de IPs acontecem antes desta etapa e não entram aqui.
#4. Ciclo de vida
PENDING ─▶ APPROVED ─▶ PROCESSING ─▶ CONFIRMING ─▶ COMPLETED ✓ dinheiro entregue
└▶ PARTIALLY_COMPLETED entregue em partesDesfechos terminais incluem também FAILED, REJECTED, CANCELLED e REVERSED. Dez status no total; os seis terminais (COMPLETED, PARTIALLY_COMPLETED, FAILED, REJECTED, CANCELLED, REVERSED) não mudam mais.
| Status | Efeito na carteira |
|---|---|
PENDING | Debitado na criação (valor + taxa reservados) |
APPROVED | Reservado — aprovado, aguardando despacho |
PROCESSING | Reservado — enviado para liquidação |
CONFIRMING | Reservado — aguardando a confirmação da instituição de destino |
COMPLETED | Debitado (terminal) — entregue |
PARTIALLY_COMPLETED | Debitado (terminal) — parte das pernas entregue, parte falhou (veja legs); as pernas que falharam são estornadas |
FAILED | Estornado automaticamente — o despacho falhou (failure_code). O estorno pode ficar retido até a confirmação (veja Falhas de saque) |
REJECTED | Estornado — recusado na revisão, ou pela reserva (system:reserve-failed) |
CANCELLED | Estornado (valor + taxa) — cancelado por você antes do despacho, ou quando um admin desativa a conta (motivo "Cancelado pelo admin; valor devolvido ao saldo") |
REVERSED | Entregue e depois devolvido pelo recebedor (reversalReason, reversalAmount, reversalStatus). O valor devolvido é creditado automaticamente somente quando a marca habilita essa opção — veja Saques devolvidos |
REJECTED não é só "negado pelo admin": uma criação cuja reserva falha também vira REJECTED, com o valor devolvido.
Despacho incerto fica em PROCESSING. Quando a chamada ao provedor estoura o tempo, o Pix pode ou não ter saído. O saque (ou a perna) fica em PROCESSING enquanto a Liqfy confirma com o provedor — só falha e estorna com prova positiva de que o Pix não saiu. Quando a conferência prova isso, a mensagem de falha é "Saque não efetivado pelo provedor. O valor foi devolvido ao saldo."
#Campos de resposta além do corpo de criação
O GET /v1/withdrawals/{id}, a lista e a resposta do cancelamento acrescentam:
| Campo | O que é |
|---|---|
legs[] | { id, legIndex, amount, status, endToEndId, failureCode, refunded, reversalCredited, reversalEndToEndId, reversedAmount, reversedAt, createdAt, updatedAt }. Um saque Pix acima do teto por transação é dividido em N pernas na aprovação; legIndex começa em 1. Pernas substituídas por um reenvio interno não aparecem. |
legSummary | Agregado das legs: { count, paidOut, returned, awaitingReturn, pendingDecision, inFlight, total }. |
recipientName | Nome do titular informado pelo provedor na liquidação (prefere uma perna concluída), quando disponível. |
reversalReason, reversalAmount, reversalStatus | Presentes quando o saque foi revertido. |
Campos da perna
status—PENDING,PROCESSING,COMPLETED,FAILEDouREVERSED.endToEndId— o E2E Pix da própria perna, para conciliar com o extrato do banco.failureCode— de um conjunto fechado, nunca texto do provedor:INVALID_PIX_KEY,ACCOUNT_CLOSED,ACCOUNT_BLOCKED,KYC_REJECTED,LIMIT_REJECTED,DESTINATION_UNAVAILABLE,DESTINATION_NOT_ALLOWED,INSUFFICIENT_BALANCE,TIMEOUT,PROVIDER_CANCELLED,PROVIDER_REJECTED,UNKNOWN.nullquando a perna não falhou.refunded— só em pernaFAILED:truequando o crédito do estorno já existe no razão (estorno da perna ou do saque inteiro).falsesignifica estorno ainda retido.reversalCredited— só em pernaREVERSED:truequando o valor devolvido já foi creditado no seu saldo.reversalEndToEndId,reversedAmount,reversedAt— dados da devolução, em pernaREVERSED.
legSummary
| Campo | Soma de |
|---|---|
paidOut | Pernas COMPLETED — o que chegou ao destino. |
returned | Pernas FAILED já estornadas + pernas REVERSED já creditadas. Só o valor das pernas: quando o saque inteiro é estornado, a taxa devolvida não entra aqui. |
awaitingReturn | Pernas FAILED cujo estorno ainda não caiu. |
pendingDecision | Pernas REVERSED cujo valor devolvido ainda não foi creditado a você. |
inFlight | Pernas PENDING / PROCESSING. |
total | Todas as pernas — igual ao netAmount do saque. |
#Saques devolvidos
Quando o recebedor devolve um Pix pago, a perna vira REVERSED assim que a devolução é confirmada no extrato do provedor (só é aceita a devolução do valor integral da perna). O saque vira REVERSED quando todas as pernas ativas foram devolvidas; com parte das pernas devolvida, o saque mantém o status e as pernas devolvidas mostram REVERSED.
O dinheiro devolvido volta para a Liqfy, não direto para a sua carteira. Ele é creditado no seu saldo sem a taxa automaticamente só quando a sua marca habilita isso e a devolução não tem indício de fraude ou suspeita (códigos BE08, FR01); fora disso, uma pessoa decide creditar ou reter. Acompanhe legs[].reversalCredited e legSummary.pendingDecision.
reversalReason usa estes rótulos para os códigos de devolução do BACEN:
| Código | Rótulo |
|---|---|
BE08 | Devolução por suspeita de fraude |
FR01 | Conta do recebedor sob suspeita de fraude |
MD06 | Devolução solicitada pelo pagador |
AC03 | Chave/conta do recebedor inválida |
AC04 | Conta do recebedor encerrada |
AC06 | Conta do recebedor bloqueada |
AM09 | Valor incorreto |
SL02 | Devolução por solicitação do participante |
DUPL | Pagamento em duplicidade |
| outro | Devolução PIX (<código>), ou Devolução PIX sem código |
#Cancelar um saque pendente
POST /v1/withdrawals/{id}/cancel — corpo opcional { "reason": "..." } (aparado, até 500 caracteres; padrão "Cancelado pelo lojista."). Mesma autenticação da criação. Aceita o id puro ou o id po_.
- Só enquanto o saque está
PENDING(nada foi despachado). Ele viraCANCELLED,valor + taxavoltam para a carteira na mesma transação, e o desfecho dispara os webhooks abaixo. Resposta200com o saque (formato do retrieve). - Já aprovado, despachado ou encerrado →
409,code=WITHDRAWAL_NOT_CANCELLABLE. Linhas que só o suporte cancela (sourceMANUAL, acima do piso de aprovação manual, fora de BRL) também respondem409: cancele pelo suporte. - No painel, só o dono ou um admin da conta cancela (membro comum →
403,code=WITHDRAWAL_CANCEL_ROLE_REQUIRED). Pela API, vale a chave da conta. A conta recebe um e-mail "Saque cancelado". - Saque de outra conta →
404— e o404nunca confirma que o id existe.
curl -X POST https://api.liqfy.com.br/v1/withdrawals/f6a7b8c9-d0e1-4234-9f01-234567890123/cancel \
-H "Authorization: Bearer $LIQFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Pedido duplicado" }'await client.payouts.cancel('f6a7b8c9-d0e1-4234-9f01-234567890123', { reason: 'Pedido duplicado' });Python client.payouts.cancel(id, reason=…), PHP $client->payouts->cancel($id, $reason), .NET client.Payouts.CancelAsync(id, reason).
#Comprovante
GET /v1/withdrawals/{id}/receipt — não é só para COMPLETED: o documento existe para todo status terminal (COMPLETED, PARTIALLY_COMPLETED, FAILED, REJECTED, CANCELLED, REVERSED). Não terminal → 400. format opcional: pdf (padrão) ou json; qualquer outro valor → 400.
Resposta 200 OK
{
"contentType": "application/pdf",
"base64": "JVBERi0xLjQKJcTl8uXrp/Og0MTGCg=="
}Só o COMPLETED tem o título "Comprovante de Transferência PIX"; os demais documentos registram o desfecho e dizem que não são comprovante de pagamento:
| Status | Título | Rótulo do valor |
|---|---|---|
COMPLETED | Comprovante de Transferência PIX | Valor transferido |
PARTIALLY_COMPLETED | Transferência Parcialmente Concluída | Valor solicitado |
FAILED | Transferência Não Realizada | Valor solicitado |
REJECTED | Transferência Não Aprovada | Valor solicitado |
CANCELLED | Transferência Cancelada | Valor solicitado |
REVERSED | Transferência Devolvida | Valor da transferência |
O documento só diz que o valor voltou para o seu saldo quando esse crédito existe no razão — um saque cancelado sem estorno nunca afirma devolução. O E2E do BACEN só aparece em documento pago; o id da transação no provedor nunca aparece. CPF/CNPJ são mascarados; a chave Pix de destino só aparece quando faltam nome e documento do recebedor. A linha de motivo só aparece para motivos reconhecidos como seguros para o cliente (chave inválida, dados do titular que não conferem, limite, conta de destino não apta, cancelamento, indisponibilidade genérica); mensagem bruta do provedor nunca chega ao documento.
curl https://api.liqfy.com.br/v1/withdrawals/f6a7b8c9-d0e1-4234-9f01-234567890123/receipt \
-H "Authorization: Bearer $LIQFY_API_KEY"const receipt = await client.payouts.receipt('f6a7b8c9-d0e1-4234-9f01-234567890123');Python client.payouts.receipt(id), PHP $client->payouts->receipt($id), .NET client.Payouts.ReceiptAsync(id).
#5. Webhooks
Assine payout.created, payout.approved, payout.paid e payout.failed. Todos usam o envelope versionado evt_ e a assinatura com timestamp. A aprovação indica processing, antes da liquidação. Veja Webhooks.
{
"id": "evt_5f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c",
"object": "event",
"api_version": "2026-07-23",
"type": "payout.paid",
"created_at": "2026-04-25T16:11:42.000Z",
"data": {
"object": {
"id": "po_f6a7b8c9-d0e1-4234-9f01-234567890123",
"object": "payout",
"status": "paid"
}
}
}data.object.status usa o vocabulário público: pending, processing, paid, failed. FAILED, REJECTED e CANCELLED chegam todos como payout.failed com status: "failed".
payout.failed contém data.object.failure_code e data.object.failure_message quando disponíveis. Use o código estável para decidir como agir. destination_not_allowed significa que o provedor recusou esse tipo de destino; tente novamente só com outro tipo de chave Pix ou destino. Veja Falhas de saque.
#6. Listar seus saques
GET /v1/withdrawals — mesma chave de API do endpoint de criação.
| Query | Padrão | Descrição |
|---|---|---|
page | 1 | Número da página (inteiro; outra coisa → 400). |
limit | 20 | Inteiro. Teto rígido em 100 — valor maior é reduzido em silêncio, sem erro. |
status | — | CSV de qualquer um dos vocabulários (abaixo). Token desconhecido → 400 ("Invalid withdrawal status: …"). |
source | — | API, PANEL, MANUAL ou INDEFINIDO (linhas sem source). Qualquer outro valor é ignorado em silêncio e a lista não é filtrada. |
order | desc | Por data de criação. asc para o mais antigo primeiro; qualquer outra coisa é desc. |
idempotency_key | — | Chave exata, de 1 a 255 caracteres após aparar (senão 400). Zero ou um resultado. |
Os dois vocabulários valem em status, e os valores de fio na resposta são os internos em maiúsculas. Uma palavra pública em minúsculas vira o seu conjunto; qualquer outro token é comparado ao status interno sem diferenciar maiúsculas (PROCESSING = só PROCESSING, enquanto processing = APPROVED, PROCESSING e CONFIRMING; reversed = REVERSED).
| Filtro público | Valores de fio internos |
|---|---|
pending | PENDING |
processing | APPROVED, PROCESSING, CONFIRMING |
paid | COMPLETED, PARTIALLY_COMPLETED |
failed | FAILED, REJECTED, CANCELLED |
| (nenhum) | REVERSED |
curl "https://api.liqfy.com.br/v1/withdrawals?page=1&limit=20&status=paid&source=API&order=desc" \
-H "Authorization: Bearer $LIQFY_API_KEY"const page = await client.payouts.list({ limit: 20, status: 'paid', page: 1, source: 'API', order: 'desc' });Python client.payouts.list(limit=…, status=…, page=…, source=…, order=…), PHP $client->payouts->list([...]), .NET client.Payouts.ListAsync(...).
Resposta 200 OK
{
"data": [
{
"id": "f6a7b8c9-d0e1-4234-9f01-234567890123",
"type": "PIX",
"status": "COMPLETED",
"amount": 50000,
"fee": 100,
"netAmount": 50000,
"currency": "BRL",
"source": "API",
"completedAt": "2026-04-25T16:11:42.000Z",
"recipientName": "Maria Silva",
"legs": [
{
"id": "c1d2e3f4-0000-0000-0000-000000000001",
"legIndex": 1,
"amount": 50000,
"status": "COMPLETED",
"endToEndId": "E12345678202604251611abcdef123456",
"failureCode": null,
"refunded": false,
"reversalCredited": false
}
],
"legSummary": { "count": 1, "paidOut": 50000, "returned": 0, "awaitingReturn": 0, "pendingDecision": 0, "inFlight": 0, "total": 50000 },
"...": "..."
}
],
"total": 17,
"page": 1,
"limit": 20
}#7. Limites e regras
- Teto por transação no Pix — R$ 15.000,00 por padrão (configuração da plataforma). Acima do teto o saque é dividido em N
legsna aprovação: cada perna é preenchida até o teto e o resto vai na última (R$ 110.000,00 → 7 × R$ 15.000,00 + 1 × R$ 5.000,00). Cada perna é um Pix próprio, com E2E próprio, e pode falhar de forma independente. - Limites de saque por conta —
minWithdrawalAmount/maxWithdrawalAmountpor solicitação,dailyPayoutMaxAmount/dailyPayoutMaxCountpor dia (null = sem limite). Fora dos limites:AMOUNT_BELOW_MINIMUM,AMOUNT_ABOVE_MAXIMUMouDAILY_PAYOUT_LIMIT_EXCEEDED. - A carteira precisa cobrir valor + taxa — débito parcial nunca acontece; a solicitação é recusada de uma vez.
- Aprovação — saques a partir de
manualApprovalThresholdCents(padrão R$ 15.001,00 — um centavo acima do teto do Pix, então qualquer Pix de perna única até o teto pode aprovar automaticamente), todo saque fora de BRL e linhas enviadas para revisão manual precisam de aprovação. O auto-payout é política de conta (autoPayoutEnabled,autoWithdrawPct— trava por percentual do saldo, padrão 50, tipos de saque elegíveis, teto do saque automático e se todos os saques exigem aprovação manual) — peça ao suporte para revisar a sua. Um saque elegível é despachado em segundos na criação; uma varredura a cada 5 minutos reavalia, pela mesma política, os saques que continuamPENDING. - Freio de velocidade — uma rajada fora do normal de saques da conta, ou várias contas pagando a mesma chave Pix em pouco tempo, pode mandar o saque para aprovação manual em vez do auto-payout. Ele nunca recusa o saque.
- Regras de saque — regras da plataforma podem recusar um saque (a recusa traz
ruleIdereasonKey:withdrawal.rule.pixKeyTypeNotAllowedouwithdrawal.rule.notAllowed) ou exigir aprovação para ele. - TED — restrito a bancos brasileiros (lista de códigos FEBRABAN). Liquidação no mesmo dia útil se aprovado até 16:30 (horário de Brasília).
#Códigos de erro
Ramifique em error.code. A message é para log; o código é o contrato. Exceção: alguns tokens (WALLET_NOT_FOUND, WALLET_NOT_SPENDABLE, TENANT_KYC_REQUIRED, CRYPTO_PANEL_ONLY) são texto puro de mensagem — as linhas deles mostram o error.code de fallback, e você casa o token em error.message. Mapa completo: Erros.
| HTTP | error.code | Quando |
|---|---|---|
| 400 | INSUFFICIENT_BALANCE | Nenhuma carteira cobre o débito (details: available, requested, currency). 400 neste caminho — não é 422. |
| 400 | invalid_request | O walletId explícito é desconhecido, inativo, de outra moeda ou não gastável. Token em error.message: WALLET_NOT_FOUND / WALLET_NOT_SPENDABLE. |
| 400 | invalid_request | Corpo inválido, Idempotency-Key com mais de 255 caracteres, filtro status inválido ou chave de idempotência inválida na consulta. |
| 400 | AMOUNT_BELOW_MINIMUM / AMOUNT_ABOVE_MAXIMUM | Fora dos limites de saque da conta. |
| 422 | DAILY_PAYOUT_LIMIT_EXCEEDED | O teto diário de valor ou de quantidade da conta foi atingido. |
| 403 | account_suspended | Conta suspensa ou bloqueada. |
| 403 | forbidden | O KYC da conta não está ativo (quando o gate de KYC está em modo de exigência). Token em error.message: TENANT_KYC_REQUIRED. |
| 403 | forbidden | type: "CRYPTO" (ou a estimativa cripto) chamado com chave de API. Token em error.message: CRYPTO_PANEL_ONLY. |
| 403 | WITHDRAWAL_CANCEL_ROLE_REQUIRED | O usuário não é dono nem administrador da conta: criação ou cancelamento de saque, transferência de saldo ou criação, edição e exclusão de destinos de saque salvos. Saques em moeda fiduciária por chave de API mantêm o acesso existente. |
| 404 | not_found | Id desconhecido, ou saque de outra conta — o 404 nunca confirma que o id existe. |
| 409 | WITHDRAWAL_NOT_CANCELLABLE | Cancelamento de saque que não está PENDING, ou linha que só o suporte cancela. |
| 409 | (sem código estável) | Quase-duplicado de criação sem chave em 90 s. Mande uma Idempotency-Key e repita. |
#Dúvidas
P: Criei um saque e ele está preso em PENDING.
R: Ele está aguardando aprovação — o despacho imediato só cobre o que a política da sua conta permite. Motivos comuns: auto-payout desligado na conta, tipo que não é pago automaticamente (por padrão só Pix), valor acima do teto do auto-payout ou a partir de manualApprovalThresholdCents (padrão R$ 15.001,00), a trava de saldo (autoWithdrawPct), o freio de velocidade ou uma regra que exige aprovação. Aprove pelo painel ou fale com o suporte sobre auto-payout.
P: Meu saque deu FAILED — a taxa foi cobrada?
R: Não. O FAILED dispara um estorno automático e atômico de valor + taxa na carteira. O estorno cai assim que o lançamento na carteira acontece; quando a failure_message é "Estamos confirmando com o banco; você será avisado.", o estorno está retido (legs[].refunded é false) — o dinheiro ainda não voltou, aguarde o crédito e o aviso.
P: Meu saque ficou REVERSED — o dinheiro voltou para o meu saldo?
R: Não necessariamente. O valor devolvido é creditado sem a taxa automaticamente só quando a sua marca habilita isso e a devolução não tem indício de fraude; senão, aguarda decisão. Confira legs[].reversalCredited e legSummary.pendingDecision.
P: Dá para cancelar um saque?
R: Só enquanto estiver PENDING, com POST /v1/withdrawals/{id}/cancel ou o botão Cancelar saque no painel. Depois de aprovado, o dinheiro já está em trânsito.
P: Minha chave CNPJ foi recusada como inválida, mas meu banco aceita. R: O dígito verificador mod-11 é exigido e não existe parâmetro de bypass. Se a sua chave reprova na checagem, fale com o suporte.
P: Minha chave de telefone foi validada como CPF.
R: Sem +55, um telefone de 11 dígitos parece CPF para a detecção automática. Mande o telefone como +55… ou declare type: "PHONE".
P: Meu banco não aparece na lista de TED. R: Usamos a lista de códigos FEBRABAN do Bacen. Abra um chamado se faltar algum — normalmente resolvemos em 24h.
#Identificadores de liquidação e consulta
charge.paid e payout.paid podem incluir data.object.settlement.end_to_end_id somente quando há E2E Pix BACEN válido. O identificador do provedor não é E2E. Em saques com múltiplas pernas, um identificador no topo não substitui os das pernas; o comprovante JSON expõe E2Es válidos das pernas concluídas quando há múltiplas pernas ativas. A ausência não significa falha do pagamento.
A retenção do webhook pago é opt-in por lojista. No modo estrito, o webhook não sai sem E2E e pode ficar retido indefinidamente se o provedor nunca o entregar; o crédito na carteira não espera. Restrinja o roteamento às rotas verificadas que entregam E2E para uma garantia prática. A liberação por prazo pode entregar sem E2E e não oferece a garantia estrita.
Consulte recursos criados pela sua integração em GET /v1/charges/by-idempotency-key/{key} ou GET /v1/withdrawals/by-idempotency-key/{key}, com a chave codificada em percent-encoding. Para /, ?, # ou espaços, use o filtro de lista ?idempotency_key= com codificação de URL. A lista tem zero ou um resultado na sua conta. A chave é obrigatória em cobranças; envie-a sempre no saque, pois saques sem chave não podem ser encontrados assim. Uma rejeição durante a reserva libera a chave do saque.
Consulta, cancelamento e comprovante de saque aceitam IDs po_ dos webhooks. GET /v1/withdrawals/{id}/receipt?format=json retorna dados para todos os estados terminais: completed, partially_completed, failed, rejected, cancelled e reversed. Apenas completed é comprovante de pagamento. Estados não terminais retornam 400. O padrão continua sendo o envelope PDF {contentType, base64}; Accept não seleciona o formato. Documentos são mascarados e a chave Pix aparece somente quando faltam nome e documento do recebedor.
#Exemplos de consulta
Nos exemplos, API_URL é a origem da API e API_KEY é a chave usada no servidor. A chave deve ter de 1 a 255 caracteres após remover espaços nas extremidades; uma chave vazia ou maior retorna 400. A rota por caminho retorna 404 neutro quando não há recurso da sua conta, inclusive quando outra conta usou a mesma chave. O filtro retorna o envelope normal da lista com zero ou um item.
# API_URL é a origem da API; API_KEY é a chave usada no servidor.
curl "$API_URL/v1/charges/by-idempotency-key/order-42" \
-H "Authorization: Bearer $API_KEY"
curl "$API_URL/v1/withdrawals/by-idempotency-key/order-42" \
-H "Authorization: Bearer $API_KEY"
# A codificação de URL é especialmente útil para chaves com /, ?, # ou espaços.
curl --get "$API_URL/v1/charges" \
-H "Authorization: Bearer $API_KEY" \
--data-urlencode "idempotency_key=order/42 ?#"
curl --get "$API_URL/v1/withdrawals" \
-H "Authorization: Bearer $API_KEY" \
--data-urlencode "idempotency_key=order/42 ?#"
curl "$API_URL/v1/withdrawals/po_f6a7b8c9-d0e1-4234-9f01-234567890123/receipt?format=json" \
-H "Authorization: Bearer $API_KEY"#Contrato do comprovante JSON
A resposta abaixo ilustra um saque Pix concluído. Os valores usam a menor unidade da moeda (150000 equivale a R$ 1.500,00). Os textos do comprovante são em português, como no PDF; issuer e o rodapé dependem da marca e do desfecho.
{
"object": "payout_receipt",
"payout_id": "po_f6a7b8c9-d0e1-4234-9f01-234567890123",
"status": "paid",
"outcome": "completed",
"is_payment_proof": true,
"title": "Comprovante de Transferência PIX",
"amount": 150000,
"amount_label": "Valor transferido",
"currency": "BRL",
"transferred_at": "2026-10-28T14:33:02.000Z",
"end_to_end_id": "E12345678202610281433Zx9Yw8Vu7Ts",
"payer": { "name": "Example Store", "document": "12.345.678/****-**" },
"receiver": { "name": "Maria da Silva", "document": "***.456.789-**", "pix_key": null },
"refunded_to_balance": false,
"what_happened": "A transferência foi concluída com sucesso.",
"what_to_do": null,
"reason": null,
"legs": null,
"issuer": "Example Brand",
"footer": "O identificador E2E acima é registrado no Banco Central do Brasil e pode ser usado para confirmar esta transferência em qualquer instituição financeira participante do PIX.",
"issued_at": "2026-10-28T15:00:00.000Z"
}statusépaidnos desfechoscompletedepartially_completedefailedem todos os outros — inclusivereversed.outcomeidentifica o estado terminal. Em particular,partially_completedtem statuspaid, masis_payment_prooféfalse.titleeamount_labelseguem a tabela de desfechos em Comprovante.- Para desfechos diferentes de
completed,transferred_ate oend_to_end_iddo topo sãonull. Mesmo em um saque concluído,end_to_end_idénullquando não há E2E Pix válido.what_to_doereasonpodem sernull;reasoné uma explicação segura para o cliente, sem erro bruto do provedor. footer: com E2E, o texto do BACEN acima; pago sem E2E, "Comprovante emitido pela Liqfy com base nos registros da transferência."; nos demais, "Documento emitido pela Liqfy com base nos registros desta solicitação. NÃO é comprovante de pagamento."refunded_to_balancereflete um crédito efetivo no ledger; não é deduzido do status. Os nomes e documentos empayerereceiverpodem sernull; CPF/CNPJ são mascarados.receiver.pix_keysó aparece preenchida quando faltam tanto o nome quanto o documento do recebedor.legsénullquando há zero ou uma perna ativa. Com múltiplas pernas ativas, contém índice, valor, status em minúsculas e E2E; pernas substituídas são excluídas. Somente pernas concluídas podem ter E2E válido, inclusive em um saque parcialmente concluído; nas demais, o E2E énull. Por exemplo:
{
"legs": [
{ "index": 0, "amount": 60000, "status": "completed", "end_to_end_id": "E12345678202610281433Zx9Yw8Vu7Ts" },
{ "index": 1, "amount": 90000, "status": "failed", "end_to_end_id": null }
]
}#Exemplos nos SDKs
Os exemplos supõem que client já está configurado, payoutId / payout_id contém o ID do saque e key é a chave original de idempotência.
Node.js
const receipt = await client.payouts.receipt(payoutId, { format: 'json' });
const charge = await client.charges.retrieveByIdempotencyKey(key);
const payout = await client.payouts.retrieveByIdempotencyKey(key);
const charges = await client.charges.list({ idempotency_key: key });
const payouts = await client.payouts.list({ idempotency_key: key });Python
receipt = client.payouts.receipt(payout_id, format="json")
charge = client.charges.retrieve_by_idempotency_key(key)
payout = client.payouts.retrieve_by_idempotency_key(key)
charges = client.charges.list(idempotency_key=key)
payouts = client.payouts.list(idempotency_key=key)PHP
$receipt = $client->payouts->receipt($payoutId, ['format' => 'json']);
$charge = $client->charges->retrieveByIdempotencyKey($key);
$payout = $client->payouts->retrieveByIdempotencyKey($key);
$charges = $client->charges->list(['idempotency_key' => $key]);
$payouts = $client->payouts->list(['idempotency_key' => $key]);.NET
var receipt = await client.Payouts.ReceiptJsonAsync(payoutId);
var charge = await client.Charges.RetrieveByIdempotencyKeyAsync(key);
var payout = await client.Payouts.RetrieveByIdempotencyKeyAsync(key);
var charges = await client.Charges.ListByIdempotencyKeyAsync(key);
var payouts = await client.Payouts.ListByIdempotencyKeyAsync(key);