Solicitar saque
Um POST, com Idempotency-Key obrigatória, e o dinheiro sai para a chave PIX escolhida.
#!/usr/bin/env bash
# Saca R$ 50,00 para uma chave PIX já cadastrada e verificada.
# O id da chave sai de `GET /v1/pix-keys`.
# LM_PIX_KEY_ID=pk_01j... ./create-withdrawal.sh
set -euo pipefail
curl --fail-with-body -sS "$LM_API_URL/v1/withdrawals" \
-H "Authorization: Bearer $LM_API_KEY" \
-H "Idempotency-Key: saque-2026-09-17-001" \
-H "Content-Type: application/json" \
-d "{
\"amount\": 5000,
\"pix_key_id\": \"$LM_PIX_KEY_ID\",
\"description\": \"repasse semanal\"
}"
O fluxo completo, escolhendo a chave e conferindo o saldo antes:
// Saque completo: escolhe a chave PIX, confere o saldo e pede o saque.
// LM_API_URL=... LM_API_KEY=... node create-withdrawal.mjs
const apiUrl = process.env.LM_API_URL;
const apiKey = process.env.LM_API_KEY;
const auth = { Authorization: `Bearer ${apiKey}` };
// `GET /v1/pix-keys` só devolve chave verificada e ativa, que é a única que saca.
const chaves = await (await fetch(`${apiUrl}/v1/pix-keys`, { headers: auth })).json();
const chave = chaves.data[0];
if (chave === undefined) {
console.error('nenhuma chave PIX verificada; cadastre uma no painel antes de sacar');
process.exit(1);
}
const saldo = await (await fetch(`${apiUrl}/v1/balance`, { headers: auth })).json();
console.log('disponível:', saldo.available, 'em reserva:', saldo.reserve);
const res = await fetch(`${apiUrl}/v1/withdrawals`, {
method: 'POST',
headers: {
...auth,
// Sem este header o pedido é recusado com 400 missing_idempotency_key.
'Idempotency-Key': 'saque-2026-09-17-001',
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 5000,
pix_key_id: chave.id,
description: 'repasse semanal',
}),
});
const saque = await res.json();
if (!res.ok) {
// `details` traz o número que faltou: saldo disponível, taxa, limite usado.
console.error(res.status, saque.error.code, saque.error.details ?? {});
process.exit(1);
}
// A taxa é cobrada por fora: sai `total` do saldo, não `amount`.
console.log(saque.id, saque.status);
console.log('valor:', saque.amount, 'taxa:', saque.fee, 'sai do saldo:', saque.total);
A taxa é cobrada por fora
amount é o que chega na sua conta bancária. fee é a taxa do saque. total, que é a soma dos
dois, é o que sai do seu saldo.
Então pedir o saque do seu available inteiro falha: falta a taxa. O que cabe é
available menos fee.
As pré-condições
Todas são conferidas no momento do pedido, e cada uma tem o seu erro:
- Conta apta a operar. Senão
403 seller_not_allowed. - KYC aprovado, em produção. Senão
403 kyc_required. No ambiente de teste o KYC não é exigido. - Saque não travado pela nossa operação. Senão
403 payout_locked. - Chave PIX verificada e ativa. Senão
409 pix_key_not_verified. Ver Chave PIX. - Valor mínimo de R$ 1,00 e saldo suficiente para valor mais taxa. Senão
409 insufficient_balance, com o disponível e a taxa emdetails. - Dentro do limite diário, em valor e em quantidade (no máximo 20 saques por dia). Senão
409 daily_limit_exceeded, edetails.reasondiz qual dos dois estourou. - Nenhum outro saque em andamento. Senão
409 invalid_statecomdetails.reasonigual awithdrawal_in_progress.
Um saque por vez
Essa última merece parágrafo próprio, porque é a que mais surpreende. Enquanto um saque seu não chegar ao fim, nenhum outro é aceito.
O motivo é honesto: dois saques concorrentes contra o mesmo saldo viram corrida, e corrida com dinheiro vira saque a descoberto. Com um por vez, a conta fecha sempre.
Na prática, se você faz repasses em lote, enfileire e mande um de cada vez, esperando o estado final de cada um.
O ciclo de vida
| Status | O que quer dizer |
|---|---|
awaiting |
Recebido, aguardando aprovação. |
approved |
Aprovado, na fila para ser enviado. |
rejected |
Recusado na aprovação. O valor volta para o saldo. |
processing |
Enviado ao processador, aguardando a liquidação. |
successful |
Pago. end_to_end_id traz o identificador da transferência PIX. |
failed |
Não foi pago. O valor volta para o saldo, e failure_reason diz o motivo. |
cancelled |
Cancelado por você enquanto ainda estava em awaiting. |
Dependendo da configuração da sua conta, o saque nasce direto em approved e pula a espera.
Cada transição entrega um evento no seu webhook: withdrawal.awaiting, withdrawal.approved,
withdrawal.rejected, withdrawal.processing, withdrawal.successful, withdrawal.failed e
withdrawal.cancelled. successful e failed são os dois estados finais que interessam.
Cancelar
Só em awaiting, e não precisa de Idempotency-Key:
POST /v1/withdrawals/{id}/cancel
Saque que já saiu de awaiting responde 409 invalid_state. Não existe cancelamento depois do
envio ao processador: a partir de processing, o resultado é successful ou failed, e você
espera.
Quando o resultado demora
processing pode durar. Enquanto o processador não confirmar, o saque fica nesse estado e nós
continuamos consultando.
O que fazer: nada, além de escutar o evento. Não crie outro saque, não pergunte em laço apertado,
não trate demora como falha. O valor está preso em pending_withdraw e vai para um dos dois estados
finais.
Próximo passo
Você já tem o ciclo do dinheiro inteiro. Para produção, veja Webhooks e depois o checklist de go-live.
Referência: Solicita um saque (exige o header , Idempotency-Key)Consulta um saque,
Lista os saques do seller no mode atual e Cancela um saque ainda em .awaiting