Skip to main content

LNURL Integration

This protocol implements the LUD-06 and LUD-16 specifications to bridge traditional Lightning Network clients with the Nostr identity ecosystem.

LUD-16: Internet Identifiers

LUD-16 allows Lightning wallets to resolve human-readable identifiers formatted like email addresses (e.g., user@example.com). Any compliant service can expose its identity graph to standard Lightning wallets through this mechanism.

Identifier Resolution

The username portion of the LUD-16 address can be one of three things, resolved in priority order:

PriorityTypeExampleHow it resolves
1NIP-05 handlealice@example.comLooked up in the identity store by name
2ConnectionKey hasha1b2c3...@example.com (64-char hex)Looked up in the connection store by hash
3Raw pubkey<hex_pubkey>@example.com (64-char hex)Used directly as the recipient pubkey

For social LIDP connections (Discord, GitHub, etc.), the identifier is the connectionKey hashSHA256(lidp_name:lidp_id). Clients never expose the raw LIDP username in the LNURL path.

GET https://example.com/.well-known/lnurlp/<connectionKey>

The Callback Response

The service resolves the identifier via the priority table above, then responds with a standard LNURL payload:

{
"callback": "https://example.com/api/lnurl/cb/<connectionKey>",
"maxSendable": 100000000,
"minSendable": 1000,
"metadata": "[[\"text/plain\",\"Zap <connectionKey>@example.com\"],[\"text/identifier\",\"<connectionKey>@example.com\"],[\"chain/flokicoin\",\"loki\"]]",
"tag": "payRequest",
"commentAllowed": 205,
"allowsNostr": true,
"nostrPubkey": "<recipient_or_server_pubkey>"
}

Nostr Extensions

  • allowsNostr: true signals to NIP-57 capable clients that they can submit a Kind 5520 Zap Request to the callback URL.
  • The nostrPubkey is the pubkey that will sign the resulting Kind 5521 Zap Receipt. This is typically the recipient's pubkey, or the server's pubkey for unclaimed/anonymous users.

Invoice Generation (Callback)

When the client accesses the callback URL (e.g., GET https://example.com/api/lnurl/cb/...&amount=10000&nostr=<url_encoded_kind_5520>), the server executes the following logic:

  1. Validation: Validate the Kind 5520 against the requested amount.
  2. Target Resolution: Is the target a registered user with a connected NWC wallet?
    • Yes (Direct): The server acts as a passthrough, retrieving an invoice directly from the user's NWC node.
    • No (Custodial): The server generates an invoice from its own Master Node (the Fallback Address flow).
  3. Invoice Response: The server returns the generated pr (payment request) to the client.
{
"pr": "lnbc10n1...",
"routes": []
}

Multi-Chain Support

The standard LUD-06/LUD-16 metadata array is extended with chain tags to signal which Lightning Network chains and denominations the service accepts. This is a backward-compatible extension — existing Bitcoin-only wallets simply ignore unknown metadata entries and continue to function normally.

Chain Metadata Tags

Chain support is declared via optional entries in the metadata JSON array using the format chain/<chain_name>:

TagDenominationDescription
chain/bitcoinsats or btcStandard Bitcoin Lightning Network
chain/flokicoinlokiFlokicoin Lightning Network

If no chain tags are present, clients default to chain/bitcoin. If at least one chain tag is present, clients should assume only the listed chains are supported.

Example: Flokicoin Service

A service operating on the Flokicoin Lightning Network includes the chain/flokicoin metadata tag with the loki denomination:

["chain/flokicoin", "loki"]

Amounts in minSendable and maxSendable are denominated in the smallest sub-unit of the declared chain, and the returned pr invoice conforms to the respective chain's Lightning invoice format.

ChainUnit NameExample
chain/bitcoinmillisatoshis (msat)1000 = 1 sat
chain/flokicoinmilliloki (mloki)1000 = 1 loki

Bitcoin Wallet Compatibility

Standard Bitcoin Lightning wallets that do not understand chain/flokicoin will silently ignore the tag and attempt to pay the returned invoice. This will fail at the protocol level because the invoice belongs to a different chain — no funds are lost, no cross-chain confusion is possible. The wallet simply rejects the invoice as unpayable.

When a service supports multiple chains, it includes multiple chain tags (e.g., both chain/bitcoin and chain/flokicoin), and the callback negotiates the correct chain based on the client's preference.