What is LNURL?
LNURL is a set of open standards built on top of the Lightning Network. Rather than exposing raw Lightning invoices or node pubkeys directly in your app, LNURL defines human-friendly URL-based protocols that wallets know how to handle. When a wallet scans a LNURL QR code, it decodes a URL, fetches a JSON response from your server, and then takes the appropriate action (signing a challenge, fetching a pay request, etc.) — all invisibly to the user.
l402-kit ships with two LNURL features out of the box:
| Feature | Use case |
|---|
| LNURL-auth | Passwordless login — wallet signs a challenge, no email or password |
| LNURL-pay | Lightning Address (you@domain.com) — anyone can pay you with a human-readable address |
Both protocols work across all major Lightning wallets (Phoenix, Blink, Zeus, Breez, Alby) and require no additional dependencies — your server only needs to serve a JSON endpoint and optionally verify a secp256k1 signature.
LNURL-auth — passwordless login
l402-kit.com uses LNURL-auth for the payment dashboard. You can use the same flow in your own app.
How it works
1. Your server generates a challenge (k1) — a random 32-byte hex string
2. You encode it as a LNURL and display as QR
3. User scans with any LNURL-auth wallet (Phoenix, Blink, Zeus, Breez...)
4. Wallet signs k1 with its Lightning node key → sends signature + pubkey to your callback
5. You verify the signature — if valid, the user is authenticated
The user’s identity is their Lightning node pubkey — stable, global, and self-sovereign.
Endpoint
Returns a LNURL challenge. Encode as a QR and display in your login flow.
{
"lnurl": "LNURL1DP68GURN8GHJ7...",
"k1": "a1b2c3..."
}
After the wallet callback, verify via:
GET /api/lnurl-auth?k1=<challenge>&sig=<signature>&key=<pubkey>
TypeScript — verify LNURL-auth signature
import { createHash } from "crypto";
import * as secp256k1 from "@noble/curves/secp256k1";
function verifyLnurlAuth(k1: string, sig: string, key: string): boolean {
try {
const msgHash = createHash("sha256").update(Buffer.from(k1, "hex")).digest();
return secp256k1.secp256k1.verify(sig, msgHash, key);
} catch {
return false;
}
}
LNURL-auth requires no email, no password, no OAuth. The user’s identity is their Lightning pubkey — portable across wallets, uncensorable, globally unique.
LNURL-pay — Lightning Address
A Lightning Address (you@domain.com) is a human-readable alias that resolves to a LNURL-pay endpoint. l402-kit exposes one at:
GET /.well-known/lnurlp/{username}
This is used internally by the split mechanism — when you set ownerAddress: "you@blink.sv", the Worker resolves blink.sv/.well-known/lnurlp/you to get a BOLT11 invoice.
How to set up your own Lightning Address
- Deploy a LNURL-pay endpoint at
https://yourdomain.com/.well-known/lnurlp/{username}
- Return the standard LNURL-pay metadata response:
{
"callback": "https://yourdomain.com/lnurlp/pay",
"maxSendable": 100000000,
"minSendable": 1000,
"metadata": "[[\"text/plain\",\"Pay you@yourdomain.com\"]]",
"tag": "payRequest"
}
- The
callback endpoint receives ?amount=<msats> and returns:
{
"pr": "lnbc10n1p...",
"routes": []
}
Self-hosted with BTCPay Server
BTCPay Server ships with LNURL-pay built in — just enable it in your store settings. Your Lightning Address becomes you@yourbtcpay.com.
import { BTCPayProvider } from "l402-kit";
const lightning = new BTCPayProvider(
process.env.BTCPAY_URL!,
process.env.BTCPAY_API_KEY!,
process.env.BTCPAY_STORE_ID!,
);
// Lightning Address: you@yourbtcpay.com — zero intermediary, 0% fee
Ownership verification for the API directory
When you register your API at POST /api/register, l402-kit automatically checks for a /.well-known/l402.txt file at your API’s domain. If found and it contains your Lightning Address, your listing gets a verified badge.
Create the file:
# https://api.yourdomain.com/.well-known/l402.txt
you@blink.sv
Then register:
ManagedProvider.fromAddress("you@blink.sv", {
registerDirectory: {
url: "https://api.yourdomain.com/v1/data",
name: "My Data API",
priceSats: 10,
},
});
// Response: { ok: true, id: "...", verified: true }
Verified APIs rank higher in the directory and display a ✓ badge.
Compatible wallets
| Wallet | LNURL-auth | LNURL-pay | Self-custody |
|---|
| Phoenix | ✅ | ✅ | ✅ |
| Blink | ✅ | ✅ | ❌ custodial |
| Zeus | ✅ | ✅ | ✅ |
| Breez | ✅ | ✅ | ✅ |
| Alby | ✅ | ✅ | ✅ Hub |
| Mutiny | ✅ | ✅ | ✅ |