Cash-out · stablecoin → PIX
API USDT para PIX
Você envia cripto para um endereço que nasce por ordem. Uma chave PIX recebe reais, com comprovante que a contraparte confere sozinha. Cotação, aceite e depósito — três chamadas.
Para quem é
- Quem precisa transformar saldo em stablecoin em reais na conta de alguém, sem passar por corretora manual.
- Plataforma que paga usuários, prestadores ou parceiros no Brasil a partir de tesouraria em cripto.
- Produto que já tem off-ramp e quer um com valor travado na cotação, estados explícitos e devolução automática quando falha.
O problema
Vender cripto e fazer o PIX cair na conta certa costuma ser um processo manual: alguém envia para uma corretora, espera a venda, saca para um banco e transfere. Cada passo tem um horário, um limite e um humano. Quando falha, o dinheiro fica num lugar que ninguém consegue apontar.
Aqui a ordem tem estado. Você sabe onde ela está, o valor é travado na cotação que você aceitou, e uma falha tem destino definido: a cripto volta para o endereço de devolução que você informou.
O fluxo, ponta a ponta
- Cote com
POST /cash-outs/previewnos dois sentidos: por valor em reais ou por quantidade de cripto. Nada é criado. - Crie a ordem com
POST /cash-outsinformando a chave PIX que recebe — ou o copia e cola de uma cobrança. - Aceite com
POST /cash-outs/{id}/accept. A resposta traz o endereço de depósito, o valor exato e oexpires_at. - Envie exatamente a quantidade cotada, na rede cotada. Em redes de endereço compartilhado, inclua o
deposit_tag. - O PIX sai sozinho.
cashout.completedtraz opix_e2ee a URL do comprovante.
Endpoints usados
| Endpoint | Para quê |
|---|---|
POST /cash-outs/preview | Cotação nos dois sentidos, sem criar ordem nem consumir limite. Serve para tela que cota a cada tecla. |
POST /cash-outs | Cria a cotação com a chave PIX de destino, ou com br_code para pagar uma cobrança. |
POST /cash-outs/{id}/accept | Trava a cotação e devolve o endereço de depósito e o memo quando a rede exige. |
GET /cash-outs/{id} | Estado, linha do tempo carimbada pelo servidor e comprovante. |
GET /pix/keys/lookup?key= | Titular da chave antes do aceite: mostre "você vai pagar Fulano · Banco" e evite recusa. |
GET /catalog | Ativos, redes e mínimos por rota. Fonte da verdade, muda sozinho. |
Exemplo que roda
# 1. cotação (não cria ordem)
curl -X POST https://api.luniumpay.com/cash-outs/preview \
-H "X-API-Key: $LUNIUM_KEY" -H "Content-Type: application/json" \
-d '{"asset":"USDT","network":"polygon","brl_amount":"500.00"}'
# 2. cria a ordem e aceita
ID=$(curl -sX POST https://api.luniumpay.com/cash-outs \
-H "X-API-Key: $LUNIUM_KEY" -H "Content-Type: application/json" \
-d '{"asset":"USDT","network":"polygon","brl_amount":"500.00",
"pix_key":"loja@exemplo.com","pix_key_type":"email",
"external_id":"saque-4412"}' | jq -r .cashout_id)
curl -X POST https://api.luniumpay.com/cash-outs/$ID/accept -H "X-API-Key: $LUNIUM_KEY"const cabecalho = { "X-API-Key": process.env.LUNIUM_KEY, "Content-Type": "application/json" };
const previa = await (await fetch(`${API}/cash-outs/preview`, {
method: "POST", headers: cabecalho,
body: JSON.stringify({ asset: "USDT", network: "polygon", brl_amount: "500.00" }),
})).json();
const ordem = await (await fetch(`${API}/cash-outs`, {
method: "POST", headers: cabecalho,
body: JSON.stringify({
asset: "USDT", network: "polygon", brl_amount: "500.00",
pix_key: "loja@exemplo.com", pix_key_type: "email", external_id: "saque-4412",
}),
})).json();
const aceita = await (await fetch(`${API}/cash-outs/${ordem.cashout_id}/accept`, {
method: "POST", headers: cabecalho,
})).json();
console.log(aceita.deposit_address, aceita.deposit_tag, aceita.expires_at);import os, requests
API = "https://api.luniumpay.com"
h = {"X-API-Key": os.environ["LUNIUM_KEY"]}
previa = requests.post(f"{API}/cash-outs/preview", headers=h, timeout=30, json={
"asset": "USDT", "network": "polygon", "brl_amount": "500.00"}).json()
ordem = requests.post(f"{API}/cash-outs", headers=h, timeout=30, json={
"asset": "USDT", "network": "polygon", "brl_amount": "500.00",
"pix_key": "loja@exemplo.com", "pix_key_type": "email",
"external_id": "saque-4412"}).json()
aceita = requests.post(f"{API}/cash-outs/{ordem['cashout_id']}/accept",
headers=h, timeout=60).json()
print(aceita["deposit_address"], aceita.get("deposit_tag"), aceita["expires_at"])Sandbox: 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
Depósito com valor diferente do cotado: a ordem não casa e vai para revisão em vez de pagar um
valor que ninguém reconhece. Envie o amount exato da resposta.
Rede errada ou depois do expires_at: o depósito chega órfão e vira revisão manual.
Sempre deposite antes do vencimento, na rede cotada.
O banco recusa a chave: a cripto volta para o refund_address que você informou, com
o estado REFUNDED e o hash da devolução — não fica saldo pendurado com a gente.
Rede com memo sem o memo: o valor cai na corretora sem dono. Por isso o aceite devolve
deposit_tag nessas redes e a documentação insiste: se veio preenchido, ele é obrigatório.
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
Três chamadas e um depósito. Quem só quer ver o preço para na primeira, que não cria nada e não consome limite nenhum.
O trilho próprio (Polygon) é o previsível: mediana de 81 s e p90 de 580 s do aceite
ao PIX pago. Nas outras redes o relógio é o das confirmações — está em eta no catálogo.
Perguntas que os integradores fazem
O valor pode mudar entre a cotação e o depósito?
Não. O aceite trava a cotação e o expires_at diz até quando ela vale. Depositar depois disso é o que quebra o casamento — não uma variação de preço.
Consigo pagar um QR code em vez de uma chave?
Sim. Mande br_code (o copia e cola inteiro) no lugar de pix_key, com USDT ou USDC em qualquer rede do catálogo. O brl_amount da ordem passa a ser exatamente o valor do QR, e você vê o lojista antes de confirmar mandando o mesmo br_code na prévia.
Como a outra ponta confere que o PIX foi pago?
Pelo EndToEndId, no verificador aberto: GET /v1/verificar/{e2e} responde sem chave nenhuma. A contraparte não precisa confiar na sua palavra nem na nossa.
Posso pagar para terceiros?
Sim, a chave não precisa ser sua. O limite diário de recebimento é por CPF/CNPJ de quem recebe e está em GET /keys/me.
Quais redes liquidam mais rápido?
Polygon, no trilho próprio, em segundos. Fora dela o prazo é o das confirmações que a corretora exige, listado por rota em GET /catalog.
Contrato OpenAPI 1.25.0 · fatos desta página em product-truth.json · status