Cash-in · PIX → stablecoin

PIX to USDT API

Your customer pays an ordinary PIX. You receive USDT or USDC in the wallet you name, with no connected wallet and no friction for the payer. One call creates the charge; the rest is a webhook.

Create a sandbox key Talk to integration Full documentation

Who this is for

The problem

Accepting PIX and ending up with stablecoin usually takes three parts that do not talk to each other: an acquirer for the PIX, a Brazilian bank account for the money to land in, and an exchange to convert. Each with its own onboarding, its own limit and its own reconciliation — with the money idle in between.

Here it is one call. The PIX comes in, the conversion happens, and the crypto goes out to the wallet you named. There is no balance waiting to be withdrawn and no manual step in the middle.

The flow, end to end

  1. You call POST /cashin/charge with the amount, asset, network, your wallet and the payer's tax id.
  2. The response carries qr_copypaste: that is the PIX you show the customer. It is valid for 15 minutes.
  3. The customer pays in their bank. The cashin.paid webhook arrives.
  4. Conversion and delivery happen on their own. cashin.settled arrives with the on-chain hash.
  5. Need to check? GET /cashin/{cashin_id}/status carries a server-stamped timeline.

Endpoints used

EndpointWhat for
POST /cashin/previewHow much crypto comes out for an amount in reais, with the route minimum. Creates nothing.
POST /cashin/chargeCreates the charge and returns the copy-and-paste code.
GET /cashin/{cashin_id}/statusCurrent state, delivery hash and timeline.
GET /cashin/limits?payer_tax=How much that payer can pay right now, before you charge.
GET /cashin/catalogAssets, networks, timing and delivery minimums.

A example that runs

curl -X POST https://api.luniumpay.com/cashin/charge \
  -H "X-API-Key: $LUNIUM_KEY" -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 25000,
    "asset": "usdt",
    "chain": "polygon",
    "payout_address": "0xSuaCarteira",
    "payer_tax_number": "12345678909",
    "external_id": "pedido-7781"
  }'
const r = await fetch("https://api.luniumpay.com/cashin/charge", {
  method: "POST",
  headers: { "X-API-Key": process.env.LUNIUM_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    amount_cents: 25000,
    asset: "usdt",
    chain: "polygon",
    payout_address: "0xSuaCarteira",
    payer_tax_number: "12345678909",
    external_id: "pedido-7781",
  }),
});
const cobranca = await r.json();
console.log(cobranca.cashin_id, cobranca.qr_copypaste);
import os, requests

r = requests.post(
    "https://api.luniumpay.com/cashin/charge",
    headers={"X-API-Key": os.environ["LUNIUM_KEY"]},
    json={
        "amount_cents": 25000,
        "asset": "usdt",
        "chain": "polygon",
        "payout_address": "0xSuaCarteira",
        "payer_tax_number": "12345678909",
        "external_id": "pedido-7781",
    },
    timeout=30,
)
cobranca = r.json()
print(cobranca["cashin_id"], cobranca["qr_copypaste"])

Sandbox: test before spending

One call, no prior approval, no card; only your email is required (that is where we send the manual, the flowchart and the integration steps). The lun_test_ key exercises the whole surface without moving any money.

curl -X POST https://api.luniumpay.com/keys/sandbox \
  -H "Content-Type: application/json" \
  -d '{"email":"you@company.com","name":"my-test"}'

What the sandbox does not prove: real settlement. No crypto moves and no PIX is paid — it is there to get the contract right, not to measure the rail.

Assets and networks

Use GET /catalog for incoming crypto and GET /cashin/catalog for delivery after Pix. Asset availability, networks and minimums differ between the two directions and can change.

Send the returned deposit_tag with a deposit when required. Use payout_tag for an outgoing route that requires a memo. See the API manual for the current contract.

Limits and holds

Read account limits and fees from GET /keys/me, payer availability from GET /cashin/limits?payer_tax=... and route minimums from previews. Cash-in, cash-out and payouts have different constraints.

A received payment can be held before settlement. Track status, timeline and signed events until the final outcome; read expiry from expires_at.

Measurements, not promises

Settlement time depends on the payment flow, route availability, network confirmations and any hold. Track the final state and check service status. Sandbox timing does not measure real settlement.

Idempotency

Send external_id on every money-moving call. Repeating the same call returns the same operation, never a second one; the same parameters with a different id create two, and the same id with different parameters answers 409 external_id_divergente instead of guessing.

A timeout is not a refusal. If the call did not answer, look the operation up by external_id before repeating — it may already exist.

Webhooks and reconciliation

Every transition fires an event signed with HMAC SHA-256 plus a timestamp (header X-Lunium-Signature, format t=…,v1=…) — the timestamp is what stops a captured delivery from being replayed. Verify the signature before trusting the body.

The webhook is the primary signal; polling is optional. When you do poll, the status carries a timeline stamped by the server — draw the stage from it, because your polling stopwatch measures your network, not the operation.

Did not answer 2xx? Delivery is retried with backoff. You can resend by hand at POST /webhooks/deliveries/{event_id}/retry and test your URL at POST /webhooks/test.

When it goes wrong

The PIX is paid by a different document than the one declared: the charge is refunded to the payer and the status carries refund_reason. Nothing is delivered to the wrong owner.

The settlement provider goes down: POST /cashin/charge answers 502 provedor_indisponivel with acao: "repetir". No charge is created in that case — repeat the same call with the same external_id.

The customer did not pay in time: the charge becomes expired and nothing happens. Create another.

Security

Open verification is the one that matters in a negotiation: your counterparty checks the PIX themselves, with no key and without taking our word for it.

Time to first integration

From zero to the first QR: one call for the sandbox key and one for the charge. The integrators who arrived through search did it in minutes, without talking to anyone.

For production you need a production key and a webhook URL. The rest of the code does not change: the sandbox uses the same contract, the same fields and the same errors.

Questions integrators ask

Do I need a Brazilian company?

Not to integrate and test. The sandbox key takes one call; a production key goes through review proportional to volume, like on any payment rail.

Why is the payer tax id mandatory?

Because it is what ties each real that comes in to the right customer, applies the per-payer limit and lets us automatically refund a PIX that came from a different document. Without it, money arrives with no owner.

Can I receive assets other than USDT?

Read the catalog for the selected flow and the operation preview. Routes, minimums and timing depend on current availability; sandbox delivery is simulated.

How long does it take?

Within 2 minutes from PIX paid to stablecoin delivered on Polygon (delivered through the exchange). On other networks, the wait is that network's confirmations.

What if the same order is charged twice?

Send external_id. Repeating returns the same charge, with the same QR — never a second one.

Create a sandbox key Talk to integration

Current OpenAPI contract · facts on this page in product-truth.json · status

Take PIX, deliver USDT. Start with a test key.

The test key takes one call, no prior approval and no cost — and your case goes straight to the integration team.

Get a sandbox key and talk to integration 3.3% + R$ 1.00 on cash-in · 1% + provider fee on PIX → PIX · no monthly fee

Other paths

USDT to PIX APISell stablecoin and pay any PIX key.PIX payout APIPay suppliers and users in reais, over the API.BRL ↔ USDT over APIBoth directions, with a quote and a receipt.Brazil on-ramp and off-rampCrypto in and out of Brazil, in one integration.PIX at checkout, stablecoin in treasuryCharge in PIX, hold in digital dollars.PIX and stablecoin for walletsGive your users a way in and out.Infrastructure under your brandThe whole rail running behind your product.

Talk to the people who built it

A sandbox key takes one call, no prior approval — if that's what you're after, start with the docs. This form is for the rest: volume pricing, a case the docs don't cover, or a question before you integrate.

Rather email us? contato@luniumpay.com