Payout · PIX direto em reais
API de payout PIX
Pagar em reais sem perna cripto: você manda o valor e a chave, o PIX sai e você recebe o comprovante. Serve para pagar usuários, fornecedores, comissões e reembolsos.
Para quem é
- Plataforma que paga muita gente e hoje faz isso por planilha e internet banking.
- Marketplace que precisa repassar vendedores com rastro e comprovante.
- Operação que já tem saldo em reais conosco e quer distribuir por API.
O problema
Pagamento em massa costuma morar fora do sistema: alguém exporta uma planilha, sobe num banco, confere na mão e depois tenta explicar cada linha. Não há idempotência, não há estado e o comprovante é uma imagem.
Um payout por API resolve as três coisas ao mesmo tempo: external_id impede pagar duas vezes, o estado é
consultável, e o comprovante é o EndToEndId — que a outra ponta confere sozinha no verificador aberto.
O fluxo, ponta a ponta
- Você tem saldo em reais na chave (por depósito PIX com
destino: "saldo"ou por venda de cripto). - Antes de pagar, confira o titular da chave em
GET /pix/keys/lookup: o banco recusa chave que não pertence ao documento informado. POST /payoutscom valor, chave, tipo, o CPF/CNPJ de quem recebe e oexternal_id.- A operação é gravada e o saldo debitado na mesma transação, antes de qualquer chamada ao provedor — não existe débito sem ordem.
- Chega
payout.sentcome2eeverify_url. Falha explícita estorna o valor e a taxa.
Endpoints usados
| Endpoint | Para quê |
|---|---|
GET /pix/keys/lookup?key= | Titular da chave (nome, documento mascarado, banco) antes de enviar. |
POST /payouts | Envia o PIX debitando do saldo. Idempotente por external_id. |
GET /payouts/{payout_id} | Estado, taxas discriminadas, EndToEndId e link do comprovante. |
GET /saldo | Disponível, bloqueado, carência e taxas vigentes da sua chave. |
GET /v1/verificar/{e2e} | Verificação pública do PIX pago — quem recebeu confere sem chave. |
Exemplo que roda
# 1. confira o titular da chave ANTES de mandar
curl "https://api.luniumpay.com/pix/keys/lookup?key=11122233344&type=cpf" -H "X-API-Key: $LUNIUM_KEY"
# 2. envie o PIX
curl -X POST https://api.luniumpay.com/payouts -H "X-API-Key: $LUNIUM_KEY" \
-H "Content-Type: application/json" \
-d '{"amount_cents":50000,"pix_key":"11122233344","pix_key_type":"cpf",
"tax_number":"11122233344","beneficiary_name":"Joao P.",
"customer_ref":"fornecedor_31","external_id":"pgto-2026-0918"}'async function pagarPix({ cents, chave, tipo, doc, nome, ref, idempotencia }) {
const h = { "X-API-Key": process.env.LUNIUM_KEY, "Content-Type": "application/json" };
const titular = await (await fetch(
`${API}/pix/keys/lookup?key=${encodeURIComponent(chave)}&type=${tipo}`, { headers: h })).json();
if (titular.verified && titular.owner_tax_number && !bate(titular.owner_tax_number, doc)) {
throw new Error("a chave não pertence a este CPF/CNPJ");
}
const r = await fetch(`${API}/payouts`, { method: "POST", headers: h, body: JSON.stringify({
amount_cents: cents, pix_key: chave, pix_key_type: tipo,
tax_number: doc, beneficiary_name: nome,
customer_ref: ref, external_id: idempotencia,
})});
const p = await r.json();
if (r.status === 502 && p.erro === "provedor_sem_resposta") {
return { indefinido: true, consultar: `/payouts/${p.payout_id ?? idempotencia}` };
}
return p;
}import os, requests
API, H = "https://api.luniumpay.com", {"X-API-Key": os.environ["LUNIUM_KEY"]}
def pagar_pix(cents, chave, tipo, doc, nome, ref, idempotencia):
titular = requests.get(f"{API}/pix/keys/lookup", headers=H, timeout=20,
params={"key": chave, "type": tipo}).json()
if titular.get("verified") and titular.get("owner_tax_number") and not bate(titular["owner_tax_number"], doc):
raise RuntimeError("a chave nao pertence a este CPF/CNPJ")
r = requests.post(f"{API}/payouts", headers=H, timeout=60, json={
"amount_cents": cents, "pix_key": chave, "pix_key_type": tipo,
"tax_number": doc, "beneficiary_name": nome,
"customer_ref": ref, "external_id": idempotencia})
p = r.json()
if r.status_code == 502 and p.get("erro") == "provedor_sem_resposta":
return {"indefinido": True} # consulte, nao repita
return pSandbox: teste antes de gastar
Uma chamada, sem cadastro, sem cartão. A chave lun_test_ exercita a superfície inteira
sem mover dinheiro nenhum.
curl -X POST https://api.luniumpay.com/keys/sandbox \
-H "Content-Type: application/json" \
-d '{"name":"meu-teste"}'
O que o sandbox não prova: liquidação real. Nenhuma cripto se move e nenhum PIX é pago — ele serve para acertar o contrato, não para medir o trilho.
Ativos e redes
Instantâneo (trilho próprio): USDT na Polygon e USDC na Polygon — liquidação em segundos, sem corretora no caminho.
Via corretora: 1.478 ativos em 241 redes na venda (1.668 pares ativo×rede) e 1.795 rotas na compra. O prazo é o número de confirmações que a rede exige, não uma escolha nossa.
Redes de endereço compartilhado (ALGO, ATOM, EOS, HBAR, KAVA, LUNA, TON, XLM, XRP): o depósito precisa levar o memo. Na venda ele vem
em deposit_tag no aceite; na compra você manda payout_tag junto do endereço.
O catálogo muda sozinho conforme redes entram e saem. Leia de GET /catalog e GET /cashin/catalog em vez de manter lista no código.
Limites e retenções
| O quê | Valor |
|---|---|
| Compra (cash-in) e saque PIX, por operação | R$ 1,00 a R$ 6.000,00 |
| Venda (cash-out), por operação | a partir de R$ 6,00 |
| Validade do QR | 15 minutos |
| Escada por pagador (CPF/CNPJ de quem paga) | R$ 60,00 na estreia → R$ 200,00 nas primeiras 24 h → R$ 6.000,00/dia |
| Acima da escada | o QR ainda é aceito e o provedor retém 24 h (D+1), até R$ 6.000,00/dia por pagador |
A escada é por documento de quem paga, não por conta que recebe: dez pagadores são dez limites
independentes. É isso que faz o produto servir a e-commerce e marketplace. Consulte o disponível de um
pagador antes de cobrar em GET /cashin/limits?payer_tax=, e leia os limites da sua chave em
GET /keys/me — tetos acima do tier são contratados.
Medições, não promessas
Medido em produção, janela de 90 dias, em 07/09/2026:
| Trecho | Mediana | p90 | Amostra |
|---|---|---|---|
| PIX recebido → stablecoin entregue (Polygon) | 6 s | 70 s | 112 operações |
| Aceite da cotação → PIX pago (Polygon) | 81 s | 580 s | 27 operações |
Fora da Polygon o relógio é o da rede: o prazo é o número de confirmações que a corretora
exige, e está em eta no catálogo. Não prometemos “instantâneo” fora do trilho próprio.
Idempotência
Mande external_id em toda operação que move dinheiro. Repetir a mesma chamada devolve a
mesma operação, nunca uma segunda; os mesmos parâmetros com id diferente criam duas, e o mesmo id
com parâmetros diferentes responde 409 external_id_divergente em vez de adivinhar.
Timeout não é recusa. Se a chamada não respondeu, consulte pelo external_id
antes de repetir — a operação pode existir.
Webhooks e reconciliação
Cada transição dispara um evento assinado em HMAC SHA-256 com timestamp (header
X-Lunium-Signature, formato t=…,v1=…) — o timestamp é o que impede replay de uma
entrega capturada. Confira a assinatura antes de confiar no corpo.
Webhook é o sinal principal; polling é opcional. Quando precisar pesquisar, o status traz a
timeline carimbada pelo servidor — desenhe a etapa a partir dela, porque o cronômetro do
seu polling mede a sua rede, não a operação.
Não respondeu 2xx? A entrega é retentada com backoff. Você reenvia à mão em
POST /webhooks/deliveries/{event_id}/retry e testa a sua URL em POST /webhooks/test.
Quando dá errado
Saldo insuficiente: 402 saldo_insuficiente com o disponível e o bloqueado. Nada é debitado.
Sem resposta do provedor: 502 provedor_sem_resposta com acao: "parar". O PIX pode ter
saído: consulte GET /payouts/{id}, não repita. A ordem existe no nosso lado mesmo nesse caso — é o que
permite reconciliar em vez de adivinhar.
Recusa explícita do banco: o valor e a taxa voltam ao saldo, e o motivo fica em error.
Primeiro depósito da subconta: 423 carencia_primeiro_deposito com libera_em.
Segurança
- webhook assinado — Todo webhook vai assinado em HMAC SHA-256 com timestamp (header X-Lunium-Signature, formato t=…,v1=…), o que impede replay de uma entrega capturada.
- idempotencia — external_id em cash-in, cash-out e payout: repetir a mesma chamada devolve a MESMA operação, nunca uma segunda. Parâmetros diferentes no mesmo external_id respondem 409.
- egress validado — A URL de webhook é resolvida e validada antes de cada entrega: endereço interno, metadata de nuvem e redirect para rede privada são recusados, e a conexão é fixada no IP validado.
- retencao fail closed — A retenção é verificada na liquidação e repetida na reivindicação SQL: nenhum caminho (worker, consulta de status, fila) consegue liquidar antes da hora.
- payout duravel — O saque grava a operação e debita o saldo na MESMA transação, antes de qualquer chamada ao provedor: não existe débito sem ordem para reconciliar.
- verificacao aberta — GET /v1/verificar/{e2e} confere um PIX liquidado sem chave nenhuma — a contraparte não precisa confiar na nossa palavra.
A verificação aberta é a que mais importa numa negociação: a sua contraparte confere o PIX sozinha, sem chave e sem depender da nossa palavra.
Tempo até a primeira integração
Uma chamada, mais a consulta de titular que evita a recusa mais comum. Em produção o PIX costuma cair em poucos minutos; o prazo máximo declarado é de uma hora.
Perguntas que os integradores fazem
Como as taxas aparecem?
Separadas: fee_service_cents é a taxa da sua chave e fee_provider_cents é a tarifa do provedor. A soma é fee_cents, debitada junto do valor. Uma recusa estorna as duas.
Posso pagar terceiros?
Sim. tax_number é o documento de quem recebe e precisa ser o dono da chave — por isso a consulta de titular antes de enviar.
O que acontece se eu repetir a chamada?
Com o mesmo external_id, você recebe a mesma operação de volta. Sem ele, criaria um segundo pagamento — mande sempre.
De onde sai o dinheiro?
Do saldo custodial em reais da sua chave. Você abastece por depósito PIX com destino: "saldo" ou vendendo cripto para o próprio saldo.
Contrato OpenAPI 1.25.0 · fatos desta página em product-truth.json · status