<!-- Manual de integração Lunium API · versão Markdown para agentes e LLMs · fonte: https://docs.luniumpay.com/manual · EN: https://docs.luniumpay.com/en/manual.md · PDF: https://docs.luniumpay.com/manual.pdf · contrato: https://api.luniumpay.com/openapi.json · resumo: https://api.luniumpay.com/llms.pt.txt -->

# Lunium API · Manual de integração

**Para integradores B2B** · versão de **02/09/2026** · base `https://api.luniumpay.com` · autenticação: header `X-API-Key` · contrato OpenAPI **1.12.0**

A Lunium é a camada de liquidação entre PIX e criptomoedas no Brasil. Uma única chave faz as três coisas: **cash-in** (cliente paga PIX e recebe cripto na carteira), **cash-out** (cliente entrega cripto e uma chave PIX recebe reais) e **saldo/payouts** (custódia em reais para o cliente final, com saque por PIX ou em cripto). Toda operação paga sai com comprovante e com o identificador oficial do Banco Central (E2E), verificável por qualquer pessoa sem chave. Tudo que está neste manual está **em produção e foi medido** na data acima.

- **R$ 200 / dia** — cash-in instantâneo por CPF/CNPJ, já na 1ª operação
- **até R$ 6.000 / dia** — cash-in aceito com retenção de 24h no provedor (estado `delayed`)
- **2 s · 7 s** — PIX pago → USDT / USDC na carteira (mediana, últimos 30 dias, Polygon)
- **R$ 6 → R$ 250 mil** — cash-out por transação · R$ 100 mil/dia por recebedor · sem teto diário por chave
- **49–65 s** — cash-out do aceite ao PIX pago, Polygon (medido em produção)
- **2,5% + R$ 1,00** — taxa padrão da chave no cash-in · cash-out: cotação já líquida

### Sumário

- 1. Fluxograma
- 2. Primeiros passos: sandbox, produção, grupo de operação
- 3. Cash-in: PIX → cripto
- 3.1 Limites: instantâneo × retenção de 24h (como usar)
- 3.2 Requisição, resposta e estados
- 3.3 Ativos, prazos e taxas
- 4. Depósito em saldo (custódia) e saques
- 5. Cash-out: cripto → PIX
- 5.1 Limites, redes e prazos
- 5.2 Estados, devoluções e comprovante
- 5.3 Sandbox: gatilhos determinísticos
- 6. Payouts (PIX direto)
- 7. Webhooks
- 8. Erros: `erro` + `acao`
- 9. Tabela-resumo de limites e taxas
- 10. Checklist de go-live · 11. Links e canais

## 1. Fluxograma

Versão em alta resolução: `https://docs.luniumpay.com/manual-fluxograma.png`.

![Fluxograma de integração Lunium API](https://docs.luniumpay.com/manual-fluxograma.png)

## 2. Primeiros passos

### 2.1 Chave de teste (sandbox) — uma chamada

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

A chave `lun_test_…` roda na **mesma URL base**, com as mesmas formas, estados e contrato de erro da produção. Nada se move. O que o sandbox cobre hoje:

| Superfície | Sandbox | Observação |
|---|---|---|
| Cash-out (`POST /cash-outs` → accept → status) | `completo` | ordem caminha pelos estados reais em ~15 s; gatilhos determinísticos (seção 5.3); E2E de teste verificável em `GET /v1/verificar/{e2e}`. |
| Cash-in (`POST /cashin/charge`) | `não gera QR` | responde `400 sandbox_sem_cashin`: um QR de teste seria pagável de verdade. **Teste o cash-in com a chave de produção e valores pequenos (R$ 1 a R$ 5)**; `GET /cashin/limits`, `POST /cashin/preview`, `GET /cashin/catalog` e `GET /keys/me` funcionam no sandbox. |
| Webhooks | `sim` | `POST /webhooks/test` dispara um `webhook.test` real contra o seu endpoint e devolve status e latência. |
| MCP (agentes de IA) | `sim` | chave de sandbox funciona no `https://api.luniumpay.com/mcp` sem nada especial. |

### 2.2 Chave de produção — só o nome é obrigatório

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

A chave nasce na hora com cash-in e cash-out habilitados, no tier **Início** (R$ 5.000/dia em cash-in + payouts; sobe para R$ 25.000 com R$ 50 mil liquidados e R$ 100.000 com R$ 500 mil). A resposta traz a chave crua **uma única vez** (só o hash fica no banco); guarde em cofre. Limite de 5 chaves de produção por IP por dia.

**Nunca cole a chave `lun_…` no grupo do Telegram.** Se precisar recuperar, crie outra (é grátis) e troque no seu código; a antiga pode ser desativada por nós.

### 2.3 Grupo de operação no Telegram — fortemente recomendado

1. Crie um grupo chamado **Lunium <> SeuProjeto** com **@LuniumNotifyBot** (o agente) e **@luniumB2B** (o humano do outro lado). A API funciona sem grupo, mas operar sem ele é operar às cegas: é por ali que você fica sabendo de uma liquidação que falhou às 3h da manhã.

2. No privado do bot, crie ou consulte a chave: `https://t.me/LuniumNotifyBot` → `/chave` mostra o código `lk…`.

3. Um **admin do grupo** manda `/vincular lk…`. Sem isso o grupo não recebe aviso nenhum.

4. A partir daí o bot avisa cada PIX que entra, cada cripto que sai, anexa o **PDF do comprovante** de cada venda paga e responde dúvidas quando mencionado. Comandos úteis: `/configurar` (checklist), `/carteira 0x…`, `/status`, `/extrato`, `/limites`.

### 2.4 Configurar a chave

```
curl -s -X PATCH https://api.luniumpay.com/keys/me -H "X-API-Key: $KEY" \
  -H 'Content-Type: application/json' -d '{
    "settlement_address": "0xSUA_CARTEIRA_POLYGON",
    "webhook_url": "https://api.seudominio.com/lunium/webhook"
  }'
→ devolve webhook_secret (whk_…) UMA vez; rotate_webhook_secret:true gera outro
```

- `settlement_address`: carteira padrão que recebe a cripto do cash-in quando a cobrança não traz `payout_address`.

- `webhook_url` + `webhook_secret`: assinatura HMAC dos eventos (seção 7).

- `GET /keys/me` devolve **todos os limites vigentes para a sua chave** (tier, por operação, escada do pagador, teto do recebedor). É o número que nunca envelhece: leia-o em vez de fixar as tabelas deste manual no código.

### 2.5 Links de teste e referência

| O quê | Onde |
|---|---|
| Quickstart executável (do zero a uma ordem com E2E, em sandbox) | `https://api.luniumpay.com/quickstart` |
| Documentação para humanos (curl, Node, Python, redes × prazos, webhooks) | `https://docs.luniumpay.com` · passo a passo: `/comecar` |
| Contrato OpenAPI 3.1 (importável no Postman/Insomnia) | `https://api.luniumpay.com/openapi.json` |
| Resumo para agentes de IA / LLMs | `https://api.luniumpay.com/llms.pt.txt` (PT) · `/llms.txt` (EN) |
| Status em tempo real (chamadas reais a cada 5 min) | `https://docs.luniumpay.com/status` · máquina: `/status.json` |
| Servidor MCP (Streamable HTTP, 10 ferramentas) | `https://api.luniumpay.com/mcp` |
| Exemplos que rodam (Node/Python) e skills para agentes de código | `github.com/guilhermezanqueta-collab/lunium-examples` · `…/lunium-agent-skills` |
| App para testar o fluxo sem escrever código | `https://app.luniumpay.com` |
| Canal de parceiros (incidentes e mudanças de contrato antes de irem ao ar) | `https://t.me/+FerOZMRRP_g5MDk5` |

## 3. Cash-in: cliente paga PIX → recebe cripto

**Fluxo:** consultar o limite do pagador → criar a cobrança (QR + copia-e-cola) → cliente paga → provedor confirma → Lunium envia a cripto ao `payout_address` → webhook `cashin.settled` com a hash da transação. O CPF/CNPJ do pagador é **obrigatório** (exigência do Banco Central, o provedor não gera QR sem ele) e é o que alimenta a escada de limites.

### 3.1 Limites: instantâneo × retenção de 24h — e como usar a retenção

Existe um degrau antifraude **por pagador** (CPF/CNPJ), independente da chave. Ele tem dois patamares e vale **já na primeira operação** do documento — não é preciso histórico nem esperar 24h:

| Valor no dia daquele CPF/CNPJ | O que acontece | Como aparece na API |
|---|---|---|
| **até R$ 200,00** | **Instantâneo.** PIX pago → cripto enviada em segundos. | QR sai com `held:false`; status `pending → paid`; `settlement_status: sent`. |
| **de R$ 200,00 a R$ 6.000,00** | **Aceito com retenção de 24h no provedor.** O PIX é pago normalmente; o valor fica retido e libera sozinho ao fim da janela. Não é falha e não é recusa. | QR sai com `held:true, held_hours:24` (antes de pagar). Pago, vira `delayed` com `delay_until`; ao liberar vira `paid` e a cripto sai. Webhooks `cashin.delayed` → `cashin.paid` → `cashin.settled`. |
| **acima de R$ 6.000,00/dia** | Recusado na criação. | `403 limite_do_pagador`, `acao: corrigir`, com `payer_limits` dizendo quanto cabe. |

Por cobrança: mínimo **R$ 1,00** (com a taxa padrão o mínimo efetivo é R$ 1,02), máximo **R$ 6.000,00**. A escada conta o que já foi pago **e o que está em voo** (cobranças pendentes ainda válidas); o relógio só começa no primeiro pagamento confirmado — criar QR não amadurece o documento.

> **Como usar a retenção na prática (o que o seu app precisa fazer)**

> 1. **Pergunte antes de cobrar:** `GET /cashin/limits?payer_tax=CPF`. Leia `instant_available_cents` (o que entra na hora) e `max_amount_cents` (o maior valor que o `POST` aceita agora, retenção incluída). Acima do instantâneo e até o máximo, a cobrança sai retida.

> 2. **Mostre ao cliente antes de ele pagar:** a resposta do `POST /cashin/charge` traz `held` e `held_hours`. Se `held:true`, diga "o valor fica retido por 24h e libera sozinho".

> 3. **Não trate `delayed` como falha e não cancele:** o PIX foi pago. Guarde `delay_until` e mostre "pago, libera em …". A ordem vira `paid` sozinha.

> 4. **Ouça o webhook** `cashin.delayed` (pagou, retido), depois `cashin.paid` e `cashin.settled` (cripto enviada, `settlement_tx_hash`). Se fizer polling, use `GET /cashin/{id}/status` a cada 30–60 s enquanto `delayed`.

> 5. A retenção é a janela para contestação (MED) antes de a cripto sair — é o que permite aceitar R$ 6.000 de um documento que nunca pagou antes.

```
GET /cashin/limits?payer_tax=52998224725   (resposta real, pagador sem histórico)
{
  "stage": "first_transaction",
  "instant_available_cents": 20000,          ← entra na hora
  "max_amount_cents": 600000,                 ← maior valor aceito agora (com retenção)
  "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…"
}
```

> **Duas recusas do provedor que não são queda (não repita a chamada):**

> - `403 pagador_recusado_pelo_provedor`, `acao: parar`, com `provider_reference`: o provedor de PIX recusou aquele CPF/CNPJ por decisão de compliance própria. Só outro documento ou revisão junto ao provedor citando a referência resolve.

> - `403 limite_do_pagador` com `stage: provider_first_qr`: o provedor limita o primeiro QR de um documento que ele ainda não identificou a R$ 200; a partir do próximo QR o documento pode usar a escada inteira.

> - `502 provedor_indisponivel`, `acao: repetir`: aí sim é instabilidade; tente de novo em instantes.

### 3.2 Requisição, resposta e estados

```
POST /cashin/charge     (X-API-Key: lun_…)
{
  "amount_cents": 15000,                         // R$ 150,00 — SEMPRE em centavos, inteiro
  "payer_tax_number": "12345678901",             // CPF (11) ou CNPJ (14), só dígitos — obrigatório
  "payer_name": "Nome do pagador",               // opcional
  "asset": "usdt", "chain": "polygon",           // par do GET /cashin/catalog; omitido = usdt/polygon
  "payout_address": "0x51e3d4…bc75e0",           // carteira do SEU cliente; omitido = settlement_address da chave
  "external_id": "dep-0001"                 // idempotente: repetir devolve a mesma cobrança (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",     // QR vale 15 minutos
  "held": false, "held_hours": 0,                // true/24 quando o valor exige retenção
  "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,     // líquido após taxa
  "usdt_amount": "28.620000", "settlement_tx_hash": "0x…", "settlement_tx_url": "https://polygonscan.com/tx/0x…",
  "e2e": "E18236120202609021420…", "payer_name": "NOME DO PAGADOR", "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": "…"
}
```

| Estado | Significado | Webhook |
|---|---|---|
| `pending` | QR emitido, ninguém pagou. | — |
| `delayed` | **PIX pago**, provedor segurando a liberação (retenção de 24h). `delay_until` diz quando libera. Não é falha. | `cashin.delayed` |
| `under_review` | PIX recebido, liquidação em trânsito. | — |
| `paid` | Creditado. A cripto sai em seguida (`settlement_status` pending → sending → **sent**). | `cashin.paid` → `cashin.settled` (com `settlement_tx_hash`) · `cashin.settlement_failed` se o envio falhar (retentado) |
| `expired` | Ninguém pagou até `expires_at`. | `cashin.expired` |
| `refunded` | Devolvido ao pagador (ex.: contestação executada pelo provedor). | `cashin.refunded` |
| `failed` | Provedor cancelou/errou antes do pagamento. | `cashin.failed` |

**O QR tem que ser pago no valor exato.** Cobrança de valor fixo não concilia com nenhuma diferença, nem para mais. Instrua o cliente a não editar o valor no app do banco.

### 3.3 Ativos, prazos e taxas

`GET /cashin/catalog` é a fonte da verdade e muda sozinho. Formato real: `{updatedAt, instant[], convert[], dropped[], totals}`.

| Trilho | Ativos | Prazo medido (PIX pago → cripto na carteira, 30 dias) |
|---|---|---|
| **instant** (liquidação nossa) | USDT e USDC na **Polygon** | USDT: mediana **2 s** (p90 2 s) · USDC: mediana **7 s** (p90 8 s) |
| **convert** (via exchange) | 1.300+ ativos e redes (BTC, ETH, SOL, TON, BNB…), cada um com `eta`, mínimo e taxa de saque | minutos; depende da rede — ex.: BTC on-chain ~37 min. Leia `etaSeconds` de cada item. |
| **liquid** | `chain: "liquid"` → o pagamento é entregue em **DePix** (stablecoin BRL na Liquid) direto na carteira Liquid informada, sem desconto de taxa da chave | segundos após a confirmação do provedor |

**Taxa padrão da chave:** `admin_fee_bps: 250` (2,5%) + `admin_fee_fixed_cents: 100` (R$ 1,00) por cobrança, descontados do valor pago antes da conversão. `POST /cashin/preview` mostra a conta sem criar nada:

```
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"}
```

Condições comerciais diferentes (taxa, tetos por pagador, carência) são configuradas por chave pela Lunium e aparecem em `GET /keys/me`.

## 4. Depósito em saldo (custódia) e saques

Para apps de carteira que precisam de uma aba "depositar e sacar" em reais. A chave precisa da custódia habilitada pela Lunium (`custodia_ativa: true` em `GET /saldo`) — pedimos no grupo. Nenhuma cripto sai: o PIX pago vira saldo em reais na sub-conta do seu cliente (`customer_ref`).

| Passo | Chamada | Regra |
|---|---|---|
| Depositar | `POST /cashin/charge` com `"destino":"saldo"` e `"customer_ref":"id-do-seu-cliente"` | Depósito **0%**. Mesma escada do pagador (200 / 6.000 por dia por CPF). |
| Retenção acima de R$ 60 | automática | **Até R$ 60,00:** credita na hora, disponível em **D+1** (`disponivel_em`). **Acima de R$ 60,00 por depósito:** o QR sai **retido 24h no provedor** (`held:true`); pago, o saldo aparece **congelado** (`delayed`) e, quando o provedor libera, fica **disponível na hora** — a retenção conta como a carência. É a janela para contestação (MED) antes de o dinheiro sair. |
| Consultar | `GET /saldo?customer_ref=…` · `GET /saldo/extrato` | `disponivel_cents`, `bloqueado_cents`, `proximas_liberacoes`, `carencia_ate`, taxas (`deposito_fee_bps`, `saque_fee_bps`, `retencao_acima_cents`). Livro-razão append-only: saldo = soma. |
| Sacar por PIX | `POST /payouts` com o mesmo `customer_ref`, `amount_cents`, `pix_key`, `tax_number` | Debita valor + **1,8%** (taxa da casa) + taxa do provedor (~R$ 1,00) antes de o PIX sair; `402 saldo_insuficiente` se faltar. A chave PIX tem que pertencer ao `tax_number` (regra do banco). PIX cai em até 24h, em geral segundos. |
| Sacar em cripto | `POST /saldo/sacar-cripto` {amount_cents, chain, asset, payout_address, tax_number, customer_ref} | Reais saem do saldo, cripto vai para a carteira do cliente pelo mesmo trilho do cash-in; a resposta é uma cobrança já `paid` — acompanhe `settlement_status`. |
| 1º depósito | — | O primeiro depósito de uma sub-conta trava qualquer saque por 24h: `423 carencia_primeiro_deposito` com `libera_em`. |

Nada é adiantado e nenhuma taxa é absorvida: o que o provedor cobra é o que o saldo paga. As cobranças trazem `fonte` (`pix` | `saldo`) e `destino` (`cripto` | `saldo`) para um saque nunca ser confundido com uma compra.

## 5. Cash-out: cliente entrega cripto → chave PIX recebe reais

Três passos, nesta ordem. A cotação já é **líquida**: `brl_amount` é exatamente o que cai na chave PIX.

```
1) POST /cash-outs                       (cotar — não compromete nada)
{
  "asset": "usdt", "network": "polygon",
  "brl_amount": "250.00",                 // OU "amount": "50.77" (cripto) — exatamente um dos dois
  "pix_key": "cliente@exemplo.com",       // CPF · CNPJ · +55DDDNÚMERO · e-mail · UUID; 11 dígitos puros exige pix_key_type
  "refund_address": "0x51e3d4…bc75e0",    // carteira de RETORNO do cliente, mesma rede — mande sempre
  "external_id": "venda-001"
}
→ 201 (resposta real de sandbox)
{ "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    (trava a cotação — ponto sem volta)
→ { "state": "AWAITING_DEPOSIT", "deposit_address": "0x2952552e…", "deposit_tag": null, … }
   O cliente envia EXATAMENTE "amount" para deposit_address, na rede cotada.

3) GET /cash-outs/{cashout_id}            (acompanhar; ou espere o 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…" }
```

- **Antes de aceitar, mostre quem vai receber:** `GET /pix/keys/lookup?key=…` devolve nome, documento mascarado e instituição do titular (DICT). 5 consultas/min por conta; chave inexistente → `404 chave_nao_encontrada`.

- **Nunca peça CPF de quem vende.** A chave PIX já identifica o recebedor; o campo não existe na venda.

- `expires_at`: 15 min na Polygon, até 300 min nas outras redes. Leia o campo; não fixe prazo.

- Se o aceite responder `503 liquidante_indisponivel` (`acao: esperar`, `retry_after_seconds: 120`): o liquidante de PIX está fora naquele instante; espere e **re-aceite a mesma ordem**. Nenhum endereço é emitido enquanto isso, de propósito — cripto nunca entra numa esteira parada.

- Pagar QR/copia-e-cola (`br_code`) em vez de chave: **desligado em produção hoje** (`503 br_code_indisponivel`). A venda paga **chaves** PIX.

### 5.1 Limites, redes e prazos

| Limite | Valor | Na API |
|---|---|---|
| Por transação | **R$ 6,00 a R$ 250.000,00** | recusa devolve `limits.min_amount` / `max_amount` já convertidos na cripto da ordem |
| Por recebedor por dia | **R$ 100.000,00** por CPF/CNPJ (ou por chave, quando ela não é documento). Renova à meia-noite de Brasília. | `limits.max_brl_cents` = teto **efetivo** (o menor dos dois); `limits.max_brl_cents_por_transacao` = nominal. Estouro: `limite_diario_recebedor`, `acao: esperar`. |
| Por chave por dia | **não existe** no cash-out | o tier (R$ 5k/25k/100k) vale só para cash-in e payouts — `daily_limit_applies_to` em `GET /keys/me` |
| Mínimos por ativo | ex.: USDT 10, USDC 20 no trilho convert | `GET /catalog` → `min`/`max` por item |

| Rede | Prazo do depósito confirmado ao PIX pago |
|---|---|
| **Polygon** (USDT/USDC) — `fast` | liquidação nossa: **segundos**; medido 49–65 s do aceite ao PIX pago (o depósito em si leva ~10–20 s para confirmar) |
| Demais redes — `convert` (1.300+ ativos via exchange) | o número de confirmações da rede: 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. **Nunca prometa "instantâneo" fora da Polygon.** |
| Solana | temporariamente **fora** do cash-out — a cotação recusa (`rede_indisponivel`) |

### 5.2 Estados, devoluções e comprovante

`QUOTE_CREATED` → accept → `AWAITING_DEPOSIT` → `DEPOSIT_DETECTED` → `PROCESSING` → `COMPLETED`  ·  desvios: `MANUAL_REVIEW` → `REFUNDING` → `REFUNDED` · `EXPIRED` · `FAILED`. Webhooks: `cashout.awaiting_deposit`, `deposit_detected`, `processing`, `under_review`, `refunding`, `completed`, `refunded`, `expired`, `failed`.

| Situação | O que acontece |
|---|---|
| Provedor recusa a chave depois do depósito (inválida/inexistente) | `MANUAL_REVIEW` → cripto volta automaticamente para `refund_address` → `REFUNDED` com `refund_tx_hash`. Devolve-se o líquido que o provedor devolveu. |
| Envio falha antes de sair (liquidante fora, chave rejeitada na submissão) | retentado a cada 5 min por até 45 min; se não liquidar, o valor **cheio** (taxa inclusive) volta para `refund_address`. O status enquanto retenta é `PROCESSING`, não falha. |
| PIX revertido depois de pago (raro) | mesmo caminho: `REFUNDED` + hash. |
| Depósito diferente do cotado | paga-se o valor realmente recebido; token diferente fica em `MANUAL_REVIEW`. |
| Depósito depois de `expires_at` | a ordem revive quando o depósito aparece (até 7 dias); depois disso, `MANUAL_REVIEW`. |
| Sem `refund_address` | a devolução vai para a origem on-chain do depósito — **errado** quando veio de exchange (é a hot wallet dela). Por isso: mande sempre. |
| Valor da devolução | automática até 5.000 USDT por ordem; acima disso uma pessoa da operação executa e você recebe o mesmo `REFUNDED` + hash. Sempre na rede e no token do depósito; o gas é nosso. |

**Comprovante — três formas do mesmo fato,** na mesma resposta e no mesmo webhook (nunca monte URL na mão): `pix_e2e` (identificador do Banco Central), `receipt_url` (página), `receipt_pdf_url` (PDF com nome, documento e instituição do recebedor, valor, data, E2E), `verify_url`. E **qualquer pessoa**, sem chave, confirma o pagamento:

```
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": "…" }
  (não expõe chave PIX, nome completo nem documento)
```

### 5.3 Sandbox: gatilhos determinísticos

As **duas primeiras casas decimais do `amount`** (em cripto) escolhem o desfecho — dá para afirmar em CI:

| `amount` termina em | O que acontece | O que prova |
|---|---|---|
| `.01` | vai para `delayed` e conclui sozinha | seu código não chama retenção de falha |
| `.02` | falha | seu caminho de erro roda |
| `.03` | cotação expira em 5 s | você recota em vez de insistir |
| `.04` | recusa por limite com `limits` preenchido | você lê `limits.min_amount`/`max_amount` |
| `.05` | conclui em ~2 min | seu polling tem paciência |
| `.06` | recusa de compliance na liquidação → `FAILED` com `failure_reason: pagador_recusado_pelo_provedor` | você mostra o motivo e não repete |
| `.07` | 1º aceite → `503 provedor_indisponivel`; 2º aceite da **mesma** ordem funciona | seu retry re-aceita em vez de recotar |
| `.08` | recusa na criação por `limite_do_pagador` | você lê `payer_limits` |

Reusar um `external_id` com destino diferente devolve **409**, igual à produção. Ordens de sandbox somem em 2 h. Nunca envie cripto a um `deposit_address` de sandbox: ele não tem dono.

## 6. Payouts (PIX direto, sem perna cripto)

`POST /payouts` envia um PIX para uma chave. Precisa de `payout_enabled` na chave (nasce desligado; pedimos no grupo). Com custódia, o valor sai do saldo da sub-conta (`customer_ref`); sem custódia, sai da tesouraria da Lunium conforme acordo comercial. Limites por operação: R$ 1,00 a R$ 6.000,00; conta na cota diária do tier.

```
POST /payouts { "amount_cents": 5000, "pix_key": "+5548999990000", "tax_number": "12345678901",
                "external_id": "saque-001", "customer_ref": "cliente-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

Envelope único para todos os eventos. `data` é exatamente o objeto que o `GET` de status devolve para aquele recurso.

```
POST {webhook_url}
X-Lunium-Event-Id: evt_9f2c…          // id do EVENTO — estável entre retentativas: deduplique por ele
X-Lunium-Delivery-Id: dlv_…           // id da TENTATIVA — cite no suporte
X-Lunium-Signature: t=1756850000,v1=<hex HMAC-SHA256(secret, "t.corpo")>
{ "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…", … } }
```

- **Verifique a assinatura v2** e recuse o que tiver mais de ~5 minutos. O header antigo `X-Webhook-Signature` (só o corpo) deixa de ser enviado em **09/11/2026**; chaves criadas depois de 08/08/2026 já não o recebem.

- Responda **2xx em até 15 s**. Sem 2xx: 12 tentativas com backoff (~7 h). A que esgota aparece como `failed` em `GET /webhooks/deliveries` e é recuperável com `POST /webhooks/deliveries/{event_id}/retry`. Nada se perde em silêncio: o bot também avisa no grupo.

- Trate evento desconhecido como no-op — a lista cresce sem aviso.

- `POST /webhooks/test` dispara `webhook.test` pelo mesmo transporte e devolve o que o SEU endpoint respondeu. Rode antes da primeira cobrança real.

- Prefira webhook a polling: 1 consulta/s consome sozinha os 60 req/min da chave (`429` é proteção, não falha).

| Cash-in | Cash-out | Payout / outros |
|---|---|---|
| `cashin.held` (QR emitido com retenção) · `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 — verificação da assinatura v2
const [t, v1] = req.get('X-Lunium-Signature').split(',').map(p => p.split('=')[1]);
const esperado = crypto.createHmac('sha256', SECRET).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(v1)) && Math.abs(Date.now()/1000 - t) < 300;
```

## 8. Erros: o campo que diz o que fazer

Todo erro traz `erro` (código estável), `acao`, `detail`, `request_id` e `agent_guidance`. **Ramifique pela `acao`**, não pela mensagem:

| `acao` | Significado | Exemplos de `erro` |
|---|---|---|
| **corrigir** | o pedido está errado; repetir igual nunca passa | `campos_obrigatorios` · `amount_invalido` (decimal em **string**) · `pix_key_invalida` · `tipo_ambiguo` (11 dígitos: informe `pix_key_type`) · `valor_abaixo_do_minimo` / `valor_acima_do_maximo` (leia `limits`) · `limite_do_pagador` · `rede_indisponivel` · `nao_encontrado` · `campos_conflitantes` |
| **repetir** | falha transitória nossa | `provedor_indisponivel` |
| **esperar** | uma cota renova ou um serviço volta | `limite_diario` (tier, só cash-in/payouts) · `limite_diario_recebedor` (venda, R$ 100 mil/dia) · `liquidante_indisponivel` (`retry_after_seconds`) · `br_code_indisponivel` · `cashout_indisponivel` |
| **parar** | não insista; mostre ao usuário / fale com a gente | `chave_ausente` · `chave_incorreta` (chave cortada ao copiar) · `chave_desconhecida` · `pagador_recusado_pelo_provedor` · `sandbox_sem_cashin` |

Todo carimbo de tempo é ISO 8601 com fuso explícito (`2026-08-01T22:35:49-03:00` ou `…Z`). Nunca suponha local ou UTC.

## 9. Tabela-resumo de limites e taxas (produção, 02/09/2026)

| Produto | Por operação | Por pessoa | Por chave | Taxa padrão | Tempo medido |
|---|---|---|---|---|---|
| **Cash-in** (PIX → cripto) | R$ 1,00 a R$ 6.000,00 | pagador (CPF/CNPJ): até R$ 200/dia na hora · R$ 200 a R$ 6.000/dia com retenção de 24h · já na 1ª operação | tier: R$ 5.000 → 25.000 → 100.000 por dia (cash-in + payouts) | 2,5% + R$ 1,00 | USDT 2 s · USDC 7 s após o PIX cair (Polygon) |
| **Depósito em saldo** | idem cash-in | idem; **acima de R$ 60 por depósito sai retido 24h** (congelado) e libera disponível; até R$ 60 disponível D+1 | idem | 0% no depósito · saque 1,8% + ~R$ 1,00 | saque PIX: segundos, até 24h |
| **Cash-out** (cripto → PIX) | R$ 6,00 a R$ 250.000,00 | recebedor: R$ 100.000/dia (CPF/CNPJ ou chave) | **sem teto diário** | já dentro da cotação (`brl_amount` é líquido) | Polygon: 49–65 s do aceite ao PIX pago; outras redes: prazo da rede |
| **Payout** (PIX direto) | R$ 1,00 a R$ 6.000,00 | — | tier | provedor ~R$ 1,00 (+1,8% se sair do saldo) | segundos, até 24h |
| Requisições | 60 por minuto por chave (configurável) · QR de cash-in vale 15 min · cotação de cash-out vale 15 min na Polygon |  |  |  |  |

Todo número daqui pode mudar; `GET /keys/me` devolve os que valem para a sua chave. Condições comerciais específicas (taxa, tetos por pagador, carência) são acordadas com a Lunium e refletidas na chave.

## 10. Checklist de go-live

1. Chave de produção criada (`POST /keys`) e guardada em cofre; nunca no chat.

2. Grupo de operação com @LuniumNotifyBot + @luniumB2B, `/vincular` feito por um admin; `/configurar` mostra tudo ✅.

3. `settlement_address` (Polygon) e `webhook_url` configurados via `PATCH /keys/me`; `POST /webhooks/test` respondeu 2xx.

4. Webhook verifica `X-Lunium-Signature` v2, deduplica por `event_id`, ignora eventos desconhecidos, responde em < 15 s.

5. Cash-in: front consulta `GET /cashin/limits` antes de cobrar, mostra `held`/`held_hours`, trata `delayed` como "pago, libera em …" e credita só em `cashin.settled` (ou `paid` com `settlement_status: sent`). Testado com PIX real de R$ 1 a R$ 5.

6. Cash-out: fluxo cotar → mostrar titular (`/pix/keys/lookup`) e valor → aceitar → mostrar `deposit_address` e `amount` exatos; sempre manda `refund_address` e `external_id`; polling a cada 10–15 s ou webhook; trata `503 liquidante_indisponivel` re-aceitando a mesma ordem. Gatilhos `.01`–`.08` passaram em sandbox.

7. Erros ramificados por `acao`; `limits` lidos da resposta em vez de fixados; `expires_at` lido em vez de prazo fixo.

8. Entrou no canal de parceiros (`https://t.me/+FerOZMRRP_g5MDk5`) e acompanha `docs.luniumpay.com/status`.

## 11. Links e canais

|  |  |
|---|---|
| API | `https://api.luniumpay.com` · OpenAPI `/openapi.json` · `/quickstart` · `/llms.pt.txt` · MCP `/mcp` · saúde `/ping` |
| Docs | `https://docs.luniumpay.com` · `/comecar` · `/status` |
| Código | `github.com/guilhermezanqueta-collab/lunium-examples` · `github.com/guilhermezanqueta-collab/lunium-agent-skills` |
| Operação | grupo do seu projeto (Lunium <> SeuProjeto) com @LuniumNotifyBot · humano: @luniumB2B · canal de parceiros `t.me/+FerOZMRRP_g5MDk5` · e-mail `contato@luniumpay.com` |

---

Lunium API · manual gerado em 02/09/2026 a partir do serviço em produção (OpenAPI 1.12.0, `GET /keys/me`, catálogos vivos e medições dos últimos 30 dias). Linguagem de limites e verificação proporcional ao volume conforme política da Lunium.
