Lightning ↔ PIX

End-to-end flow — issue a Lightning invoice; once paid, settle BRL via PIX in seconds.

What this flow does

A payer anywhere in the world pays a BOLT11 Lightning invoice. Hodle's Spark wallet receives the sats, swaps them into USDB on the Flashnet AMM, then settles BRL via PIX to the recipient. End-to-end median is under 30 seconds.

Use this when:

  • You're building a remittance product paying out to Brazil.
  • A foreign customer pays for a Brazilian-merchant invoice in BTC.
  • A user wants to off-ramp Lightning balance to a bank account.

Sequence

1. POST /api/lightning/invoice           → bolt11 + invoiceId
2. (off-chain) payer pays the invoice
3. Spark wallet receives → Flashnet swap → BRLA → PIX out
4. Webhook lightning.invoice.paid        → invoice settled
5. Webhook payout.completed              → PIX settled

You only own steps 1, 2, and listening for the webhooks. Steps 3 happen inside Hodle.

Step 1 — Issue the invoice

curl -X POST https://api.hodle.com.br/api/lightning/invoice \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "value": 10000,
    "pixKey": "recipient@example.com",
    "pixKeyType": "EMAIL",
    "memo": "invoice #4291"
  }'
const res = await fetch('https://api.hodle.com.br/api/lightning/invoice', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    value: 10000,
    pixKey: 'recipient@example.com',
    pixKeyType: 'EMAIL',
    memo: 'invoice #4291',
  }),
})
const { data } = await res.json()
const { invoice, invoiceId } = data
import os, requests

res = requests.post(
    "https://api.hodle.com.br/api/lightning/invoice",
    headers={
        "Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "value": 10000,
        "pixKey": "recipient@example.com",
        "pixKeyType": "EMAIL",
        "memo": "invoice #4291",
    },
)
data = res.json()["data"]

value is in BRL cents. Hodle quotes the equivalent satoshi amount at the moment of creation.

201 Created
{
  "success": true,
  "data": {
    "invoiceId": "ln_8f3a...",
    "invoice": "lnbc100u1p3...",
    "amountSats": 18420,
    "valueInBrl": "100.00",
    "expiresAt": "2026-05-09T22:30:00.000Z"
  }
}

Show the invoice string as a QR code or "copy to clipboard" — any Lightning wallet can pay it.

Step 2 — Payer pays the invoice

There's nothing to do on your side. The payer scans, pays, and the sats hit Hodle's Spark node within ~1 second.

Step 3 — Internal swap (transparent)

Inside Hodle:

  1. The Spark wallet receives the sats.
  2. The Flashnet AMM swaps them to USDB at the locked rate.
  3. USDB is converted to BRLA.
  4. A PIX-out is fired against the pixKey from step 1.

You don't need to model any of this — it's all a single lightning_pix_out operation in your statement.

Step 4 — Listen for lightning.invoice.paid

Configure a webhook in your Hodle dashboard, then verify each delivery:

{
  "event": "lightning.invoice.paid",
  "data": {
    "invoiceId": "ln_8f3a...",
    "amountSats": 18420,
    "valueInBrl": "100.00",
    "paidAt": "2026-05-09T22:14:32.000Z"
  }
}

This fires the moment the BOLT11 invoice settles, before the PIX has fired. Mark the user-facing transaction as "received" here.

Step 5 — Listen for payout.completed

{
  "event": "payout.completed",
  "data": {
    "invoiceId": "ln_8f3a...",
    "endToEndId": "E12345678202605091114abcdef123456",
    "valueInBrl": "100.00",
    "fee": "1.50",
    "completedAt": "2026-05-09T22:14:50.000Z"
  }
}

This fires when the recipient's bank confirms the PIX. This is the book closed event — surface "Settled" in your UI now.

Failure modes

FailureWhere it surfacesWhat to do
Invoice expires unpaidNo webhook fires.Show "expired" after expiresAt. Re-issue if needed.
AMM swap fails (rare)payout.failed, failureReason: AMM_*Sats are returned to the payer via Lightning refund.
PIX rejected by recipient bankpayout.failed, failureReason: PIX_*The BRL settles back to a Hodle balance. Contact support.

Reconciliation

Operations of type: lightning in /api/account/statement carry both bolt11 (the invoice you issued) and endToEndId (the PIX) — that's the only join you need.

Webhook signatures

Both webhooks are signed. See Webhooks for the verification snippet.