BRL ↔ USDT · producción
BRL ↔ USDT por API
Ambos sentidos, con la misma clave y un precio que se puede consultar antes de asumir cualquier compromiso. La cotización es una llamada que no crea una orden ni consume límite, y devuelve el mismo cálculo que se aplicará.
A quién va dirigido
- Fintechs o empresas que necesitan BRL → USDT y USDT → BRL funcionando en producción, más allá de una prueba piloto.
- Equipos de tesorería que convierten a diario y quieren ver el precio antes de decidir.
- Quienes hoy usan una plataforma de intercambio manualmente y quieren el mismo resultado con contrato, estado y comprobante.
El problema
Convertir BRL y USDT de forma previsible tiene dos obstáculos: un precio que cambia entre la decisión y la ejecución, y un proceso que depende de que alguien inicie sesión en algún sitio. El primero provoca pérdidas silenciosas; el segundo crea dependencia del horario comercial.
La respuesta es separar cotizar de ejecutar. La vista previa devuelve el mismo cálculo que la orden real —misma vía, mismos mínimos y mismos rechazos—, pero no guarda nada. Muestras el importe, el usuario decide y solo entonces se crea la orden con el importe fijado.
El flujo de principio a fin
- Cotizar:
POST /cashin/preview(reales brasileños → criptomonedas) oPOST /cash-outs/preview(criptomonedas → reales brasileños). No se crea nada. - La vista previa devuelve la vía (
path), la cantidad y, en las compras, el mínimo en reales brasileños de esa ruta. - Comprar:
POST /cashin/chargegenera el PIX; una vez pagado, las criptomonedas se envían a la billetera indicada. - Vender:
POST /cash-outs+/acceptdevuelve la dirección; una vez recibido el depósito, se envía el PIX. - Cada operación tiene
external_id, un webhook firmado y un comprobante; la conciliación es automática.
Endpoints utilizados
| Endpoint | Para qué |
|---|---|
POST /cashin/preview | Precio de compra y mínimo en reales brasileños de la ruta. No crea nada. |
POST /cash-outs/preview | Precio de venta en ambos sentidos, sin crear una orden ni consumir límite. |
POST /cashin/charge | Compra: genera el PIX identificado y entrega las criptomonedas. |
POST /cash-outs + /accept | Venta: fija la cotización y devuelve la dirección de depósito. |
GET /keys/me | Límites por operación y tramos vigentes de tu clave. |
Ejemplo ejecutable
# precio antes de cualquier orden — ambos sentidos
curl -X POST https://api.luniumpay.com/cashin/preview -H "X-API-Key: $LUNIUM_KEY" \
-H "Content-Type: application/json" \
-d '{"amount_cents":100000,"asset":"usdt","chain":"polygon"}'
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","amount":"1000"}'const h = { "X-API-Key": process.env.LUNIUM_KEY, "Content-Type": "application/json" };
// BRL -> USDT: cuántas criptomonedas se reciben por R$ X (con el mínimo de la ruta)
export async function comprar(cents) {
const p = await (await fetch(`${API}/cashin/preview`, { method: "POST", headers: h,
body: JSON.stringify({ amount_cents: cents, asset: "usdt", chain: "polygon" }) })).json();
return { usdt: p.usdt_amount ?? p.amount, minimoBrlCents: p.min_brl_cents };
}
// USDT -> BRL: cuánto PIX se recibe por N USDT, sin crear una orden
export async function vender(qtd) {
const p = await (await fetch(`${API}/cash-outs/preview`, { method: "POST", headers: h,
body: JSON.stringify({ asset: "USDT", network: "polygon", amount: String(qtd) }) })).json();
return { brl: p.brl_amount, trilho: p.path };
}import os, requests
API, H = "https://api.luniumpay.com", {"X-API-Key": os.environ["LUNIUM_KEY"]}
def comprar(cents):
p = requests.post(f"{API}/cashin/preview", headers=H, timeout=30, json={
"amount_cents": cents, "asset": "usdt", "chain": "polygon"}).json()
return p.get("usdt_amount") or p.get("amount"), p.get("min_brl_cents")
def vender(qtd):
p = requests.post(f"{API}/cash-outs/preview", headers=H, timeout=30, json={
"asset": "USDT", "network": "polygon", "amount": str(qtd)}).json()
return p["brl_amount"], p["path"]Sandbox: prueba antes de gastar
Una llamada, sin aprobación previa ni tarjeta; solo es obligatorio tu correo (al que enviamos el manual, el diagrama de flujo y la guía de integración paso a paso). La clave lun_test_ permite probar toda la interfaz sin mover dinero.
curl -X POST https://api.luniumpay.com/keys/sandbox \
-H "Content-Type: application/json" \
-d '{"email":"voce@empresa.com","name":"meu-teste"}'
Lo que el sandbox no demuestra: liquidación real. No se mueven criptomonedas ni se paga ningún PIX; sirve para ajustar el contrato, no para medir la vía de procesamiento.
Activos y redes
El catálogo de entrada de criptomonedas es GET /catalog; el de entrega tras Pix es GET /cashin/catalog. Consulta el activo y la red del flujo elegido antes de cotizar. La disponibilidad y los mínimos pueden cambiar.
Usa deposit_tag en el depósito cuando la aceptación indique un memo. En la entrega, envía payout_tag cuando lo requiera la ruta. El manual de la API describe cada campo.
Límites y retenciones
Consulta GET /keys/me para conocer los límites y las tarifas de la cuenta, GET /cashin/limits?payer_tax=... para el pagador y las vistas previas para el mínimo de la ruta. No uses un límite máximo fijo compartido entre cash-in, cash-out y payout.
Una solicitud de cobro pagada puede quedar retenida antes de la liquidación. Usa los estados, la cronología y los eventos firmados para distinguir entre cobro, saldo disponible y entrega final. La validez de la operación figura en expires_at.
Mediciones, no promesas
El plazo depende del flujo, la ruta disponible, las confirmaciones de la red y las posibles retenciones. Sigue la operación hasta su estado final y consulta el estado del servicio. El tiempo de una simulación no mide una liquidación real.
Idempotencia
Envía external_id en cada operación que mueve dinero. Repetir la misma llamada devuelve la misma operación, nunca una segunda; los mismos parámetros con un id distinto crean dos, y el mismo id con parámetros distintos devuelve 409 external_id_divergente en lugar de intentar adivinar.
Un timeout no es un rechazo. Si la llamada no respondió, consulta por el external_id antes de repetirla: la operación puede existir.
Webhooks y conciliación
Cada transición genera un evento firmado con HMAC SHA-256 y una marca de tiempo (cabecera X-Lunium-Signature, formato t=…,v1=…). La marca de tiempo impide reutilizar una entrega capturada. Verifica la firma antes de confiar en el cuerpo.
El webhook es la señal principal; el polling es opcional. Cuando necesites consultar, el estado incluye la timeline marcada por el servidor — representa la etapa a partir de ella, porque el cronómetro de tu polling mide tu red, no la operación.
¿No respondió con 2xx? Se reintenta la entrega con esperas progresivas. Puedes reenviarla manualmente en POST /webhooks/deliveries/{event_id}/retry y probar tu URL en POST /webhooks/test.
Cuando algo falla
Por debajo del mínimo: el rechazo indica el importe; en la compra, min_brl_cents aparece en la propia vista previa, de modo que la pantalla muestra el mínimo antes de que el usuario escriba.
Por encima del máximo: valor_acima_do_maximo con el límite máximo vigente. Léelo de GET /keys/me en lugar de fijarlo en el código: los límites cambian según el contrato.
Cotización caducada: la orden tiene expires_at. Después de ese plazo, el depósito llega sin una orden vigente; vuelve a cotizar en lugar de depositar tarde.
Seguridad
- webhook firmado — Cada webhook se firma con HMAC SHA-256 y una marca de tiempo (cabecera X-Lunium-Signature, formato t=…,v1=…), lo que impide reutilizar una entrega capturada.
- idempotencia — external_id en cash-in, cash-out y payout: repetir la misma llamada devuelve la MISMA operación, nunca una segunda. Los parámetros distintos con el mismo external_id devuelven 409.
- tráfico de salida validado — La URL del webhook se resuelve y valida antes de cada entrega: se rechazan direcciones internas, metadatos de la nube y redirecciones a redes privadas, y la conexión se fija en la IP validada.
- retención con bloqueo ante fallos — La retención se verifica al liquidar y de nuevo al reclamar la operación en SQL: ninguna vía (worker, consulta de estado o cola) puede liquidar antes de tiempo.
- payout duradero — El retiro registra la operación y debita el saldo en la MISMA transacción, antes de cualquier llamada al proveedor: no existe un débito sin una orden que conciliar.
- verificación abierta — GET /v1/verificar/{e2e} comprueba un PIX liquidado sin ninguna clave; la contraparte no tiene que confiar en nuestra palabra.
La verificación abierta es especialmente importante en una negociación: tu contraparte comprueba el PIX por su cuenta, sin clave y sin depender de nuestra palabra.
Tiempo hasta la primera integración
La primera integración útil es solo la vista previa: una llamada sin efectos secundarios que ya puedes incorporar a una pantalla. La ejecución llega después, con el mismo contrato que ya has probado.
Preguntas de los integradores
¿La vista previa consume límite o crea una orden?
Ninguna de las dos cosas. Ejecuta el mismo cálculo que la cotización real y devuelve la misma vía de procesamiento y los mismos errores, sin guardar nada; está pensada para pantallas que cotizan con cada pulsación.
¿El precio queda fijado?
En la venta, sí: la aceptación fija la cotización y el expires_at indica hasta cuándo es válida. En la compra, el importe del PIX es el que has definido y las criptomonedas entregadas siguen la cotización de la operación.
¿Qué pares funcionan?
Consulta el catálogo del flujo elegido y la vista previa de la operación. Las rutas, los mínimos y los plazos dependen de la disponibilidad vigente; la entrega en el sandbox es simulada.
¿Puedo operar fuera del horario bancario?
Sí. PIX liquida las 24 horas del día, todos los días, y la API no tiene una franja horaria; lo que varía es el tiempo de confirmación de la red elegida.
Contrato OpenAPI vigente · datos de esta página en product-truth.json · estado