# Lunium quickstart — zero to a verified PIX payment

Crypto in, Brazilian reais out. This page takes you from nothing to a completed
order with a Central Bank receipt, without spending a cent and without talking
to anyone.

Every command below is runnable as-is.

---

## 1. Get a test key (one call, no signup)

```bash
curl -s -X POST https://api.luniumpay.com/keys/sandbox \
  -H 'Content-Type: application/json' -d '{"name":"my first test"}'
```

You get back `api_key` starting with `lun_test_`. Export it:

```bash
export LUNIUM_KEY="lun_test_..."
```

Nothing this key touches moves money. Same base URL, same shapes, same states as
production — code written here works in production unchanged.

---

## 2. See what settles right now

```bash
curl -s https://api.luniumpay.com/catalog -H "X-API-Key: $LUNIUM_KEY"
```

This is the **real** catalog, not a simulation: assets appear and networks get
suspended without notice, so read it instead of hard-coding a list. Polygon
(USDT/USDC) settles in seconds and is the default.

---

## 3. Quote a sale

`amount` is a decimal **string**, never a JSON number — floats lose precision in
transit. **The PIX key alone is enough** — the type is inferred from it. `pix_key_type` is only
needed when the key is 11 bare digits, where a CPF and a phone number are the same length. `external_id` is yours: it makes
the call idempotent, so a retry returns the same order instead of creating a
second one.

```bash
curl -s -X POST https://api.luniumpay.com/cash-outs \
  -H "X-API-Key: $LUNIUM_KEY" -H 'Content-Type: application/json' -d '{
    "asset": "USDT",
    "network": "polygon",
    "amount": "100",
    "pix_key": "someone@example.com",
    "external_id": "my-first-order-001"
  }'
```

Read `brl_amount` (what the recipient receives) and `expires_at` (read it, do
not assume a window). Keep `cashout_id`.

---

## 4. Accept — in production this is the point of no return

```bash
export ID="the cashout_id from step 3"
curl -s -X POST "https://api.luniumpay.com/cash-outs/$ID/accept" \
  -H "X-API-Key: $LUNIUM_KEY"
```

You get `deposit_address`. In production, crypto sent there is converted and
paid out to the PIX key from the quote — there is no cancel and no reversal, so
show your user the BRL amount and the destination **before** this call.

In the sandbox, `deposit_address` has no owner. Never send real crypto to it.

---

## 5. Follow it

```bash
watch -n 3 "curl -s https://api.luniumpay.com/cash-outs/$ID -H 'X-API-Key: $LUNIUM_KEY'"
```

It walks the real states — `AWAITING_DEPOSIT` → `DEPOSIT_DETECTED` →
`PAYING_OUT` → `COMPLETED` — over about 15 seconds. It does not complete
instantly on purpose: you need this polling loop (or a webhook) in production
anyway, so you write it now.

Poll every 10–15 seconds, not every second: one call per second consumes the
60-per-minute budget by itself.

When it completes you get four things at once, in the same response and the same
webhook — never build these URLs by hand:

| field | what it is |
|---|---|
| `pix_e2e` | Central Bank end-to-end identifier |
| `receipt_url` | shareable page for the end customer |
| `receipt_pdf_url` | PDF file, for tickets and accounting |
| `verify_url` | link a counterparty can check themselves |

---

## 6. Verify it — the part that has no equivalent

```bash
curl -s https://api.luniumpay.com/v1/verificar/<the pix_e2e>
```

**No API key. No account.** Anyone can confirm the payment happened, including a
counterparty who has no reason to trust you. It returns the amount, the
timestamp, the recipient's initials and institution — and never the PIX key, the
full name or the tax number.

This is why Lunium exists: "A says it paid, how does B check without trusting A"
normally needs escrow. With the E2E it needs one open GET.

---

## 7. Rehearse the bad days

Testing only the happy path is how integrations break on day one. In the
sandbox, the **first two decimals of `amount`** choose the outcome — no
randomness, so you can assert on it in CI:

| `amount` ends in | what happens | what it proves |
|---|---|---|
| `.01` | goes to `delayed`, completes on its own | your code does not call a held payment a failure |
| `.02` | fails | your error path runs |
| `.03` | quote expires in 5s | you re-quote instead of insisting |
| `.04` | refused on limits, `limits` filled | you read `limits.min_amount` instead of guessing |
| `.05` | completes in ~2 minutes | your polling is patient |

Also worth rehearsing: reuse an `external_id` with a **different destination**
and you get `409`. That is the case that confuses every integrator — better to
meet it here.

---

## 8. Errors tell you what to do

Every failure carries `erro` (a stable code that does not change when the
wording does) and **`acao`**:

- `corrigir` — your request is wrong. Repeating it unchanged will never work.
- `repetir` — transient on our side. Retry once.
- `esperar` — a quota renews. Back off.
- `parar` — do not insist; surface the message to your user.

Branch on `acao`, not on the message.

---

## 9. Go to production

```bash
curl -s -X POST https://api.luniumpay.com/keys \
  -H 'Content-Type: application/json' -d '{"name":"your business name"}'
```

Only `name` is required. The response carries your production key, your live
read-only dashboard link, and a Telegram link — **join it**. Contract changes
and incidents are announced there before they reach you, and every partner who
skipped it found out about an incident from their own customer instead.

Swap the key in your code. Nothing else changes.

---

## Using an AI agent instead

Add the MCP server as a remote connector:

```
https://api.luniumpay.com/mcp
```

Streamable HTTP, revision 2025-06-18. Connect with no credentials and payment
verification already works. Pass your key as the `X-API-Key` header for the
rest. The two tools that move real money are marked `destructiveHint: true`, and
confirming a sale requires a token bound to the exact amount, network and
destination that were quoted.

Machine-readable contract: https://api.luniumpay.com/llms.txt ·
https://api.luniumpay.com/openapi.json

Stuck? contato@luniumpay.com
