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 settledYou 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 } = dataimport 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.
{
"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:
- The Spark wallet receives the sats.
- The Flashnet AMM swaps them to USDB at the locked rate.
- USDB is converted to BRLA.
- A PIX-out is fired against the
pixKeyfrom 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
| Failure | Where it surfaces | What to do |
|---|---|---|
| Invoice expires unpaid | No 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 bank | payout.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.