Lunium. docs
📄 Download PDF📝 Markdown version (for agents)🗺 High-resolution flowchart

Lunium API · Integration manual

For B2B integrators · version of 2026-09-02 · base https://api.luniumpay.com · authentication: X-API-Key header · OpenAPI contract 1.12.0

Lunium is the settlement layer between PIX and crypto in Brazil. A single key does all three things: cash-in (the customer pays a PIX and receives crypto in their wallet), cash-out (the customer delivers crypto and a PIX key receives reais) and balance/payouts (BRL custody for the end customer, withdrawable by PIX or as crypto). Every paid operation returns a receipt and the Central Bank's official identifier (E2E), which anyone can verify without a key. Everything in this manual is in production and was measured on the date above.

R$ 200 / day
instant cash-in per CPF/CNPJ, from the very first operation
up to R$ 6,000 / day
cash-in accepted with a 24h provider hold (state delayed)
2 s · 7 s
PIX paid → USDT / USDC in the wallet (median, last 30 days, Polygon)
R$ 6 → R$ 250k
cash-out per transaction · R$ 100k/day per recipient · no daily cap per key
49–65 s
cash-out from accept to PIX paid, Polygon (measured in production)
2.5% + R$ 1.00
default key fee on cash-in · cash-out: the quote is already net

Contents

1. Flowchart
2. First steps: sandbox, production, operations group
3. Cash-in: PIX → crypto
3.1 Limits: instant × 24h hold (how to use it)
3.2 Request, response and states
3.3 Assets, timing and fees
4. Balance deposit (custody) and withdrawals
5. Cash-out: crypto → PIX
5.1 Limits, networks and timing
5.2 States, refunds and receipt
5.3 Sandbox: deterministic triggers
6. Payouts (direct PIX)
7. Webhooks
8. Errors: erro + acao
9. Summary table of limits and fees
10. Go-live checklist · 11. Links and channels

1. Flowchart

High-resolution version: https://docs.luniumpay.com/manual-fluxograma.png (labels in Portuguese).

Lunium API integration flowchart

2. First steps

2.1 Test key (sandbox) — one call

curl -s -X POST https://api.luniumpay.com/keys/sandbox \
  -H 'Content-Type: application/json' -d '{"name":"my-project"}'
→ {"api_key":"lun_test_…","sandbox":true,"como_usar":{…}}

The lun_test_… key runs on the same base URL, with the same shapes, states and error contract as production. Nothing moves. What the sandbox covers today:

SurfaceSandboxNotes
Cash-out (POST /cash-outs → accept → status)completethe order walks the real states in ~15 s; deterministic triggers (section 5.3); the test E2E is verifiable at GET /v1/verificar/{e2e}.
Cash-in (POST /cashin/charge)no QRanswers 400 sandbox_sem_cashin: a test QR would be payable for real. Test cash-in with the production key and small amounts (R$ 1 to R$ 5); GET /cashin/limits, POST /cashin/preview, GET /cashin/catalog and GET /keys/me work in the sandbox.
WebhooksyesPOST /webhooks/test fires a real webhook.test at your endpoint and returns status and latency.
MCP (AI agents)yesa sandbox key works at https://api.luniumpay.com/mcp with nothing special.

2.2 Production key — only the name is required

curl -s -X POST https://api.luniumpay.com/keys \
  -H 'Content-Type: application/json' -d '{"name":"My Company"}'
→ {"api_key":"lun_…", "dashboard_url":"…", "grupos_obrigatorios":{…}, "limits":{…}}

The key is born immediately with cash-in and cash-out enabled, in the Início tier (R$ 5,000/day on cash-in + payouts; it rises to R$ 25,000 at R$ 50k settled and to R$ 100,000 at R$ 500k). The response carries the raw key once only (only the hash is stored); keep it in a vault. Limit of 5 production keys per IP per day.

Never paste the lun_… key in the Telegram group. If you need to recover it, create another (it is free) and swap it in your code; we can deactivate the old one.

2.3 Operations group on Telegram — strongly recommended

  1. Create a group named Lunium <> YourProject with @LuniumNotifyBot (the agent) and @luniumB2B (the human on the other side). The API works without a group, but operating without one is operating blind: it is where you learn about a settlement that failed at 3 a.m.
  2. In the bot's private chat, create or look up the key: https://t.me/LuniumNotifyBot/chave shows the lk… code.
  3. A group admin sends /vincular lk…. Without it the group receives no notification at all.
  4. From then on the bot announces every PIX that comes in, every crypto that goes out, attaches the receipt PDF of every paid sale and answers questions when mentioned. Useful commands: /configurar (checklist), /carteira 0x…, /status, /extrato, /limites.

2.4 Configure the key

curl -s -X PATCH https://api.luniumpay.com/keys/me -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' -d '{
    "settlement_address": "0xYOUR_POLYGON_WALLET",
    "webhook_url": "https://api.yourdomain.com/lunium/webhook"
  }'
→ returns webhook_secret (whk_…) ONCE; rotate_webhook_secret:true generates another

2.5 Test and reference links

WhatWhere
Runnable quickstart (zero to an order with an E2E, in the sandbox)https://api.luniumpay.com/quickstart
Documentation for humans (curl, Node, Python, networks × timing, webhooks)https://docs.luniumpay.com/en/ · walkthrough: /en/comecar
OpenAPI 3.1 contract (importable into Postman/Insomnia)https://api.luniumpay.com/openapi.json
Summary for AI agents / LLMshttps://api.luniumpay.com/llms.txt (EN) · /llms.pt.txt (PT)
Live status (real calls every 5 min)https://docs.luniumpay.com/status · machine: /status.json
MCP server (Streamable HTTP, 10 tools)https://api.luniumpay.com/mcp
Examples that run (Node/Python) and skills for coding agentsgithub.com/guilhermezanqueta-collab/lunium-examples · …/lunium-agent-skills
App to try the whole flow without writing codehttps://app.luniumpay.com
Partner channel (incidents and contract changes before they go live)https://t.me/+FerOZMRRP_g5MDk5

3. Cash-in: the customer pays a PIX → receives crypto

Flow: check the payer's limit → create the charge (QR + copy-and-paste) → the customer pays → the provider confirms → Lunium sends the crypto to payout_address → webhook cashin.settled with the transaction hash. The payer's CPF/CNPJ is required (a Central Bank requirement — the provider does not issue a QR without it) and it is what feeds the limit ladder.

3.1 Limits: instant × 24h hold — and how to use the hold

There is an anti-fraud step per payer (CPF/CNPJ), independent of the key. It has two levels and applies from the document's very first operation — no history and no 24h wait needed:

Amount in the day for that CPF/CNPJWhat happensHow it shows in the API
up to R$ 200.00Instant. PIX paid → crypto sent in seconds.QR issued with held:false; status pending → paid; settlement_status: sent.
from R$ 200.00 to R$ 6,000.00Accepted with a 24h hold at the provider. The PIX is paid normally; the amount is held and released on its own at the end of the window. Not a failure and not a refusal.QR issued with held:true, held_hours:24 (before payment). Once paid it becomes delayed with delay_until; on release it becomes paid and the crypto goes out. Webhooks cashin.delayedcashin.paidcashin.settled.
above R$ 6,000.00/dayRefused at creation.403 limite_do_pagador, acao: corrigir, with payer_limits saying how much fits.

Per charge: minimum R$ 1.00 (with the default fee the effective minimum is R$ 1.02), maximum R$ 6,000.00. The ladder counts what was already paid and what is in flight (pending charges still valid); the clock only starts at the first confirmed payment — creating a QR does not mature the document.

How to use the hold in practice (what your app needs to do)
  1. Ask before charging: GET /cashin/limits?payer_tax=CPF. Read instant_available_cents (what settles instantly) and max_amount_cents (the largest amount the POST accepts right now, hold included). Above the instant amount and up to the maximum, the charge is issued with a hold.
  2. Show the customer before they pay: the POST /cashin/charge response carries held and held_hours. If held:true, say "the amount is held for 24h and releases on its own".
  3. Do not treat delayed as a failure and do not cancel: the PIX was paid. Keep delay_until and show "paid, releases at …". The order becomes paid by itself.
  4. Listen to the webhook cashin.delayed (paid, held), then cashin.paid and cashin.settled (crypto sent, settlement_tx_hash). If you poll, call GET /cashin/{id}/status every 30–60 s while delayed.
  5. The hold is the window for a dispute (MED) before the crypto leaves — it is what makes it possible to accept R$ 6,000 from a document that has never paid before.
GET /cashin/limits?payer_tax=52998224725   (real response, payer with no history)
{
  "stage": "first_transaction",
  "instant_available_cents": 20000,          ← settles instantly
  "max_amount_cents": 600000,                 ← largest amount accepted now (with hold)
  "held_qr": { "max_amount_cents": 600000, "used_cents": 0, "available_cents": 600000, "hold_hours": 24 },
  "ladder": { "first_transaction_cents": 20000, "first_24h_cents": 20000,
              "mature_day_cents": 600000, "held_max_cents": 600000, "hold_hours": 24 },
  "next_stage": { "stage": "first_24h", "limit_cents": 20000, "quando": "após o primeiro pagamento confirmado" },
  "explicacao": "Primeira operação deste CPF/CNPJ: até R$ 200.00 entra na hora; acima disso, até R$ 6000.00 o QR sai com retenção de 24h do provedor…"
}
Two provider refusals that are not outages (do not repeat the call):

3.2 Request, response and states

POST /cashin/charge     (X-API-Key: lun_…)
{
  "amount_cents": 15000,                         // R$ 150.00 — ALWAYS in cents, integer
  "payer_tax_number": "12345678901",             // CPF (11) or CNPJ (14), digits only — required
  "payer_name": "Payer name",                    // optional
  "asset": "usdt", "chain": "polygon",           // pair from GET /cashin/catalog; omitted = usdt/polygon
  "payout_address": "0x51e3d4…bc75e0",           // YOUR customer's wallet; omitted = the key's settlement_address
  "external_id": "dep-0001"                      // idempotent: repeating returns the same charge (200)
}
→ 200
{
  "cashin_id": "ci_3ca9f23b…", "status": "pending",
  "amount_cents": 15000, "asset": "usdt", "chain": "polygon", "payout_address": "0x51e3…",
  "qr_copypaste": "00020126…", "qr_image_url": "https://…png",
  "expires_at": "2026-09-02T23:25:00.000Z",     // the QR is valid for 15 minutes
  "held": false, "held_hours": 0,                // true/24 when the amount requires a hold
  "delay_until": null, "liberar_em": null, "hold_hours": 0,
  "external_id": "dep-0001", "created_at": "…"
}
GET /cashin/{cashin_id}/status
{
  "cashin_id": "ci_…", "status": "paid",                     // pending | delayed | under_review | paid | expired | refunded | failed
  "settlement_status": "sent",                              // pending | sending | sent | incerto | failed
  "amount_cents": 15000, "depix_received_cents": 14625,     // net after fee
  "usdt_amount": "28.620000", "settlement_tx_hash": "0x…", "settlement_tx_url": "https://polygonscan.com/tx/0x…",
  "e2e": "E18236120202609021420…", "payer_name": "PAYER NAME", "payer_tax_number": "123.***.***-01",
  "held": false, "held_hours": 0, "delay_until": null, "liberar_em": null,
  "destino": "cripto", "customer_ref": null,
  "paid_at": "…", "settled_at": "…", "expires_at": "…", "created_at": "…"
}
StateMeaningWebhook
pendingQR issued, nobody has paid.
delayedPIX paid, the provider is holding the release (24h hold). delay_until says when it releases. Not a failure.cashin.delayed
under_reviewPIX received, settlement in transit.
paidCredited. The crypto goes out next (settlement_status pending → sending → sent).cashin.paidcashin.settled (with settlement_tx_hash) · cashin.settlement_failed if the send fails (retried)
expiredNobody paid before expires_at.cashin.expired
refundedReturned to the payer (e.g. a dispute executed by the provider).cashin.refunded
failedThe provider cancelled or errored before payment.cashin.failed
The QR must be paid for the exact amount. A fixed-amount charge does not reconcile with any difference, not even upwards. Tell the customer not to edit the amount in the banking app.

3.3 Assets, timing and fees

GET /cashin/catalog is the source of truth and changes on its own. Real shape: {updatedAt, instant[], convert[], dropped[], totals}.

RailAssetsMeasured time (PIX paid → crypto in the wallet, 30 days)
instant (our own settlement)USDT and USDC on PolygonUSDT: median 2 s (p90 2 s) · USDC: median 7 s (p90 8 s)
convert (via exchange)1,300+ assets and networks (BTC, ETH, SOL, TON, BNB…), each with eta, minimum and withdrawal feeminutes; depends on the network — e.g. BTC on-chain ~37 min. Read etaSeconds on each item.
liquidchain: "liquid" → the payment is delivered as DePix (BRL stablecoin on Liquid) straight to the Liquid wallet given, with no key fee deductedseconds after the provider confirms

Default key fee: admin_fee_bps: 250 (2.5%) + admin_fee_fixed_cents: 100 (R$ 1.00) per charge, deducted from the amount paid before conversion. POST /cashin/preview shows the maths without creating anything:

POST /cashin/preview {"amount_cents":10000,"asset":"usdt","chain":"polygon"}
→ {"amount_cents":10000,"admin_fee_bps":250,"admin_fee_fixed_cents":100,"fee_total_cents":348,"net_cents":9652,"brl_per_usdt":"5.11","usdt_amount":"18.874787"}

Different commercial terms (fee, per-payer ceilings, holding period) are configured per key by Lunium and show up in GET /keys/me.

4. Balance deposit (custody) and withdrawals

For wallet apps that need a "deposit and withdraw" tab in reais. The key needs custody enabled by Lunium (custodia_ativa: true in GET /saldo) — ask in the group. No crypto leaves: the paid PIX becomes a BRL balance in your customer's sub-account (customer_ref).

StepCallRule
DepositPOST /cashin/charge with "destino":"saldo" and "customer_ref":"your-customer-id"Deposit fee 0%. Same payer ladder (200 / 6,000 per day per CPF).
Hold above R$ 60automaticUp to R$ 60.00: credited immediately, available on D+1 (disponivel_em). Above R$ 60.00 per deposit: the QR is issued held for 24h at the provider (held:true); once paid, the balance shows as frozen (delayed) and, when the provider releases it, it becomes available immediately — the hold counts as the holding period. It is the window for a dispute (MED) before the money leaves.
QueryGET /saldo?customer_ref=… · GET /saldo/extratodisponivel_cents, bloqueado_cents, proximas_liberacoes, carencia_ate, fees (deposito_fee_bps, saque_fee_bps, retencao_acima_cents). Append-only ledger: balance = sum.
Withdraw by PIXPOST /payouts with the same customer_ref, amount_cents, pix_key, tax_numberDebits amount + 1.8% (house fee) + provider fee (~R$ 1.00) before the PIX leaves; 402 saldo_insuficiente if short. The PIX key must belong to the tax_number (bank rule). The PIX lands within 24h, usually seconds.
Withdraw as cryptoPOST /saldo/sacar-cripto {amount_cents, chain, asset, payout_address, tax_number, customer_ref}Reais leave the balance, crypto goes to the customer's wallet through the same rail as cash-in; the response is a charge already paid — follow settlement_status.
1st depositThe first deposit of a sub-account blocks any withdrawal for 24h: 423 carencia_primeiro_deposito with libera_em.

Nothing is advanced and no fee is absorbed: what the provider charges is what the balance pays. Charges carry fonte (pix | saldo) and destino (cripto | saldo) so a withdrawal is never confused with a purchase.

5. Cash-out: the customer delivers crypto → a PIX key receives reais

Three steps, in this order. The quote is already net: brl_amount is exactly what lands on the PIX key.

1) POST /cash-outs                       (quote — commits nothing)
{
  "asset": "usdt", "network": "polygon",
  "brl_amount": "250.00",                 // OR "amount": "50.77" (crypto) — exactly one of the two
  "pix_key": "customer@example.com",      // CPF · CNPJ · +55AREANUMBER · e-mail · UUID; 11 bare digits require pix_key_type
  "refund_address": "0x51e3d4…bc75e0",    // the customer's RETURN wallet, same network — always send it
  "external_id": "sale-001"
}
→ 201 (real sandbox response)
{ "cashout_id": "sbx_141ce3e9…", "state": "QUOTE_CREATED", "amount": "50.774309", "brl_amount": "250.00",
  "pix_key_type": "email", "deposit_address": null, "expires_at": "2026-09-03T02:33:31Z", … }

2) POST /cash-outs/{cashout_id}/accept    (locks the quote — point of no return)
→ { "state": "AWAITING_DEPOSIT", "deposit_address": "0x2952552e…", "deposit_tag": null, … }
   The customer sends EXACTLY "amount" to deposit_address, on the quoted network.

3) GET /cash-outs/{cashout_id}            (follow it; or wait for the webhook)
→ { "state": "COMPLETED", "pix_e2e": "E0000000020260903…", "pix_paid_at": "…",
    "receipt_url": "https://app.luniumpay.com/comprovante/…", "receipt_pdf_url": "….pdf",
    "verify_url": "https://api.luniumpay.com/v1/verificar/E…", "refund_tx_hash": null, "deposit_from": "0x…" }

5.1 Limits, networks and timing

LimitValueIn the API
Per transactionR$ 6.00 to R$ 250,000.00a refusal returns limits.min_amount / max_amount already converted into the order's crypto
Per recipient per dayR$ 100,000.00 per CPF/CNPJ (or per key, when the key is not a document). Resets at midnight, Brasília time.limits.max_brl_cents = the effective ceiling (the lower of the two); limits.max_brl_cents_por_transacao = nominal. Exceeded: limite_diario_recebedor, acao: esperar.
Per key per daydoes not exist on cash-outthe tier (R$ 5k/25k/100k) applies only to cash-in and payouts — daily_limit_applies_to in GET /keys/me
Minimums per assete.g. USDT 10, USDC 20 on the convert railGET /catalogmin/max per item
NetworkTime from confirmed deposit to PIX paid
Polygon (USDT/USDC) — fastour own settlement: seconds; measured 49–65 s from accept to PIX paid (the deposit itself takes ~10–20 s to confirm)
Other networks — convert (1,300+ assets via exchange)the network's confirmation count: TON/Aptos ~1 min · Avalanche/NEAR ~2 min · BSC ~3 min · Base ~5 min · Ethereum ~20 min · Arbitrum ~21 min · Optimism ~1 h · Celo ~3 h. Never promise "instant" outside Polygon.
Solanatemporarily out of cash-out — the quote refuses (rede_indisponivel)

5.2 States, refunds and receipt

QUOTE_CREATED → accept → AWAITING_DEPOSITDEPOSIT_DETECTEDPROCESSINGCOMPLETED  ·  detours: MANUAL_REVIEWREFUNDINGREFUNDED · EXPIRED · FAILED. Webhooks: cashout.awaiting_deposit, deposit_detected, processing, under_review, refunding, completed, refunded, expired, failed.

SituationWhat happens
The provider refuses the key after the deposit (invalid/non-existent)MANUAL_REVIEW → the crypto is automatically returned to refund_addressREFUNDED with refund_tx_hash. The net amount the provider returned is refunded.
The send fails before leaving (settlement provider down, key rejected on submission)retried every 5 min for up to 45 min; if it still does not settle, the full amount (fee included) returns to refund_address. While retrying the state is PROCESSING, not a failure.
PIX reversed after being paid (rare)same path: REFUNDED + hash.
Deposit different from the quotethe amount actually received is paid; a different token goes to MANUAL_REVIEW.
Deposit after expires_atthe order is revived when the deposit shows up (up to 7 days); after that, MANUAL_REVIEW.
No refund_addressthe refund goes to the on-chain origin of the deposit — wrong when it came from an exchange (that is the exchange's hot wallet). Hence: always send it.
Refund amountautomatic up to 5,000 USDT per order; above that a person from operations executes it and you receive the same REFUNDED + hash. Always on the deposit's network and token; the gas is ours.

Receipt — three forms of the same fact, in the same response and the same webhook (never build URLs by hand): pix_e2e (Central Bank identifier), receipt_url (page), receipt_pdf_url (PDF with the recipient's name, document and institution, amount, date, E2E), verify_url. And anyone, with no key, can confirm the payment:

GET https://api.luniumpay.com/v1/verificar/{e2e}
→ { "verificado": true, "pago": true, "valor_brl": "250.00", "pago_em": "2026-09-03T02:18:47Z",
    "recebedor_iniciais": "T. S. T.", "instituicao": "…", "liquidado_por": "Lunium", "comprovante_url": "…" }
  (does not expose the PIX key, full name or document)

5.3 Sandbox: deterministic triggers

The first two decimals of amount (in crypto) choose the outcome — you can assert on it in CI:

amount ends inWhat happensWhat it proves
.01goes to delayed and completes on its ownyour code does not call a hold a failure
.02failsyour error path runs
.03the quote expires in 5 syou re-quote instead of insisting
.04refused on limits with limits filledyou read limits.min_amount/max_amount
.05completes in ~2 minyour polling is patient
.06compliance refusal at settlement → FAILED with failure_reason: pagador_recusado_pelo_provedoryou show the reason and do not retry
.071st accept → 503 provedor_indisponivel; the 2nd accept of the same order worksyour retry re-accepts instead of re-quoting
.08refused at creation by limite_do_pagadoryou read payer_limits

Reusing an external_id with a different destination returns 409, same as production. Sandbox orders vanish after 2 h. Never send crypto to a sandbox deposit_address: it has no owner.

6. Payouts (direct PIX, no crypto leg)

POST /payouts sends a PIX to a key. It requires payout_enabled on the key (born disabled; ask in the group). With custody, the amount leaves the sub-account's balance (customer_ref); without custody, it leaves Lunium's treasury as agreed commercially. Limits per operation: R$ 1.00 to R$ 6,000.00; counts towards the tier's daily quota.

POST /payouts { "amount_cents": 5000, "pix_key": "+5548999990000", "tax_number": "12345678901",
                "external_id": "withdrawal-001", "customer_ref": "customer-42" }
→ { "payout_id": "po_…", "status": "processing",   // processing | sent | failed | refunded
    "amount_cents": 5000, "fee_cents": 100, "pix_key": "+5548*****0000", "sent_at": null, … }
Webhooks: payout.sent · payout.failed · payout.refunded

7. Webhooks

One envelope for every event. data is exactly the object the status GET returns for that resource.

POST {webhook_url}
X-Lunium-Event-Id: evt_9f2c…          // id of the EVENT — stable across retries: deduplicate by it
X-Lunium-Delivery-Id: dlv_…           // id of the ATTEMPT — quote it to support
X-Lunium-Signature: t=1756850000,v1=<hex HMAC-SHA256(secret, "t.body")>
{ "event": "cashin.settled", "event_id": "evt_9f2c…", "created_at": "2026-09-02T23:10:11.000Z",
  "data": { "cashin_id": "ci_…", "status": "paid", "settlement_status": "sent", "settlement_tx_hash": "0x…", … } }
Cash-inCash-outPayout / other
cashin.held (QR issued with a hold) · cashin.delayed · cashin.paid · cashin.settled · cashin.settlement_failed · cashin.expired · cashin.refunded · cashin.failed cashout.awaiting_deposit · cashout.deposit_detected · cashout.processing · cashout.under_review · cashout.refunding · cashout.completed · cashout.refunded · cashout.expired · cashout.failed payout.sent · payout.failed · payout.refunded · webhook.test
// Node — verifying the v2 signature
const [t, v1] = req.get('X-Lunium-Signature').split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', SECRET).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now()/1000 - t) < 300;

8. Errors: the field that tells you what to do

Every error carries erro (a stable code), acao, detail, request_id and agent_guidance. Branch on acao, not on the message:

acaoMeaningExamples of erro
corrigirthe request is wrong; repeating it unchanged never passescampos_obrigatorios · amount_invalido (decimal as a string) · pix_key_invalida · tipo_ambiguo (11 digits: send pix_key_type) · valor_abaixo_do_minimo / valor_acima_do_maximo (read limits) · limite_do_pagador · rede_indisponivel · nao_encontrado · campos_conflitantes
repetirtransient failure on our sideprovedor_indisponivel
esperara quota renews or a service comes backlimite_diario (tier, cash-in/payouts only) · limite_diario_recebedor (sale, R$ 100k/day) · liquidante_indisponivel (retry_after_seconds) · br_code_indisponivel · cashout_indisponivel
parardo not insist; show it to the user / talk to uschave_ausente · chave_incorreta (key cut when copying) · chave_desconhecida · pagador_recusado_pelo_provedor · sandbox_sem_cashin

Every timestamp is ISO 8601 with an explicit offset/timezone (2026-08-01T22:35:49-03:00 or …Z). Never assume local time or UTC.

9. Summary table of limits and fees (production, 2026-09-02)

ProductPer operationPer personPer keyDefault feeMeasured time
Cash-in (PIX → crypto)R$ 1.00 to R$ 6,000.00payer (CPF/CNPJ): up to R$ 200/day instant · R$ 200 to R$ 6,000/day with a 24h hold · from the first operationtier: R$ 5,000 → 25,000 → 100,000 per day (cash-in + payouts)2.5% + R$ 1.00USDT 2 s · USDC 7 s after the PIX lands (Polygon)
Balance depositsame as cash-insame; above R$ 60 per deposit it is held 24h (frozen) and then released as available; up to R$ 60 available on D+1same0% on deposit · withdrawal 1.8% + ~R$ 1.00PIX withdrawal: seconds, up to 24h
Cash-out (crypto → PIX)R$ 6.00 to R$ 250,000.00recipient: R$ 100,000/day (CPF/CNPJ or key)no daily capalready inside the quote (brl_amount is net)Polygon: 49–65 s from accept to PIX paid; other networks: the network's time
Payout (direct PIX)R$ 1.00 to R$ 6,000.00tierprovider ~R$ 1.00 (+1.8% when it leaves the balance)seconds, up to 24h
Requests60 per minute per key (configurable) · a cash-in QR is valid for 15 min · a cash-out quote is valid for 15 min on Polygon

Every number here can change; GET /keys/me returns the ones that apply to your key. Specific commercial terms (fee, per-payer ceilings, holding period) are agreed with Lunium and reflected on the key.

10. Go-live checklist

  1. Production key created (POST /keys) and stored in a vault; never in chat.
  2. Operations group with @LuniumNotifyBot + @luniumB2B, /vincular done by an admin; /configurar shows everything ✅.
  3. settlement_address (Polygon) and webhook_url configured via PATCH /keys/me; POST /webhooks/test answered 2xx.
  4. Webhook verifies X-Lunium-Signature v2, deduplicates by event_id, ignores unknown events, answers in < 15 s.
  5. Cash-in: the front end queries GET /cashin/limits before charging, shows held/held_hours, treats delayed as "paid, releases at …" and credits only on cashin.settled (or paid with settlement_status: sent). Tested with a real PIX of R$ 1 to R$ 5.
  6. Cash-out: quote → show the holder (/pix/keys/lookup) and the amount → accept → show the exact deposit_address and amount; always sends refund_address and external_id; polls every 10–15 s or uses the webhook; handles 503 liquidante_indisponivel by re-accepting the same order. Triggers .01.08 passed in the sandbox.
  7. Errors branched by acao; limits read from the response instead of hard-coded; expires_at read instead of a fixed window.
  8. Joined the partner channel (https://t.me/+FerOZMRRP_g5MDk5) and follows docs.luniumpay.com/status.

11. Links and channels

APIhttps://api.luniumpay.com · OpenAPI /openapi.json · /quickstart · /llms.txt · MCP /mcp · health /ping
Docshttps://docs.luniumpay.com/en/ · /en/comecar · /status
Codegithub.com/guilhermezanqueta-collab/lunium-examples · github.com/guilhermezanqueta-collab/lunium-agent-skills
Operationsyour project's group (Lunium <> YourProject) with @LuniumNotifyBot · human: @luniumB2B · partner channel t.me/+FerOZMRRP_g5MDk5 · e-mail contato@luniumpay.com
Lunium API · manual generated on 2026-09-02 from the production service (OpenAPI 1.12.0, GET /keys/me, live catalogs and measurements from the last 30 days). Limits and verification proportional to volume, as per Lunium's policy.