Refunds
Lightning Enable supports refunding payments back to customers via Lightning Network or on-chain Bitcoin.
Overview
Refunds work differently than traditional payment processors — Lightning payments are irreversible, so a refund is a new outgoing payment:
- On-chain refunds (default) — the customer provides a Bitcoin address, funds are sent on-chain
- Lightning refunds — the customer provides a BOLT11 Lightning invoice for the refund amount, and it gets paid
Refunds are processed by your payment provider and debited from your provider balance. For Strike merchants, refunds are implemented as outgoing payments via payment-quotes (Strike has no native refund API). For OpenNode merchants, refunds are processed as withdrawals through OpenNode.
Only payments in paid, underpaid, or processing status can be refunded.
Creating a Refund
The request always takes invoiceId + refundAddress. The refundType field tells Lightning Enable how to interpret the address: "chain" for a Bitcoin address (the default) or "ln" for a Lightning invoice. Amounts are specified in satoshis via amountSatoshis — required for all Lightning refunds and for every refund on Strike (the platform default). It's optional only for OpenNode on-chain refunds, where omitting it means a full refund.
On-chain (Bitcoin address)
curl -X POST https://api.lightningenable.com/api/refunds \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "1042",
"refundAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"amountSatoshis": 62500,
"reason": "Customer requested refund"
}'
Lightning (BOLT11 invoice)
Ask the customer to generate an invoice for the refund amount in their wallet, then:
curl -X POST https://api.lightningenable.com/api/refunds \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "1042",
"refundAddress": "lnbc250000n1p...",
"refundType": "ln",
"amountSatoshis": 25000,
"reason": "Customer requested refund"
}'
amountSatoshis is always required for Lightning refunds — it must match the amount the customer's invoice was generated for.
Partial refund
curl -X POST https://api.lightningenable.com/api/refunds \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "1042",
"refundAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"amountSatoshis": 5000,
"reason": "Partial return"
}'
Partial refunds may not be supported by every provider/payment type — if unsupported, the API returns 400 with an explanatory error message.
Response
{
"refundId": "17",
"providerRefundId": "wd_9f8e7d6c",
"openNodeRefundId": "wd_9f8e7d6c",
"invoiceId": "1042",
"status": "pending",
"amountSatoshis": 62500,
"feeSatoshis": 154,
"refundAddress": "bc1qxy2...",
"refundType": "chain",
"createdAt": "2026-07-03T14:00:00Z",
"transactionHash": null
}
See the Refunds API reference for the full field-by-field schema, list endpoints, and error contract.
Tracking a Refund
Refund status changes are not delivered via merchant webhooks. Poll for status:
# Current status (pending → processing → confirmed | failed)
curl https://api.lightningenable.com/api/refunds/17 \
-H "X-API-Key: YOUR_API_KEY"
# Force an immediate re-check against your payment provider
curl -X POST https://api.lightningenable.com/api/refunds/17/sync \
-H "X-API-Key: YOUR_API_KEY"
| Status | Meaning |
|---|---|
pending | Created, not yet processed by the provider |
processing | Provider is processing the payout |
confirmed | Settled — on-chain refunds also carry a transactionHash |
failed | Failed — retry with a fresh request or contact support |
You can list all refunds with GET /api/refunds (optional status, skip, take query parameters — returns a bare JSON array), or fetch every refund for one payment with GET /api/refunds/invoice/{invoiceId}.
Collecting Refund Details from Customers
A minimal refund-request form needs just two inputs:
<form id="refund-form">
<label>
Where should we send your refund?
<input name="refundAddress"
placeholder="Bitcoin address or Lightning invoice" required />
</label>
<label>
Reason (optional)
<input name="reason" maxlength="500" />
</label>
<button type="submit">Request refund</button>
</form>
Detect the type server-side: BOLT11 invoices start with lnbc (mainnet) — anything else, treat as an on-chain address and let validation catch mistakes:
app.post('/request-refund', async (req, res) => {
const { refundAddress, reason, orderId } = req.body;
const order = await db.orders.findOne({ orderId });
const response = await fetch('https://api.lightningenable.com/api/refunds', {
method: 'POST',
headers: {
'X-API-Key': process.env.LIGHTNING_ENABLE_API_KEY, // server-side only
'Content-Type': 'application/json'
},
body: JSON.stringify({
invoiceId: order.lightningInvoiceId,
refundAddress,
refundType: refundAddress.toLowerCase().startsWith('lnbc') ? 'ln' : 'chain',
// Required for Lightning refunds and for all refunds on Strike —
// derive from your order record (sats), never from client input
amountSatoshis: order.amountSats,
reason
})
});
const refund = await response.json();
if (!response.ok) {
return res.status(400).json({ error: refund.error });
}
await db.orders.updateOne({ orderId }, { $set: { refundId: refund.refundId } });
res.json({ refundId: refund.refundId, status: refund.status });
});
BOLT11 invoices typically expire within minutes to hours. Process Lightning refunds promptly after the customer submits their invoice, and ask for a fresh one if the refund fails with an expiry error.
Best Practices
- Prefer on-chain for slow workflows — Bitcoin addresses don't expire; Lightning invoices do.
- Verify the payment first — the API rejects refunds for invoices that aren't
paid/underpaid/processing, with the reason inerror. - Poll until terminal status —
confirmedorfailed; don't assume success at creation (pending). - Use
X-Idempotency-Keyon the create call so retries can't double-refund. - Keep your provider balance funded — refunds are outgoing payments and fail if the balance can't cover amount + fee.
Next Steps
- Refunds API reference — full schemas and endpoints
- Webhooks — payment notifications
- Integration Guide — the full payment flow