Operational Checklist
Run through this before flipping your integration on for production users.
LNURL-pay endpoint
-
/.well-known/lnurlp/<user>returnscommentAllowed >= 512,minSendable <= 1000, and a callback URL. - Callback handler returns an invoice whose msat amount exactly matches the
?amount=parameter (use msat-precision fields like LNDValueMsat/ CLNamount_msat/ EclairamountMsat, not the rounded-sat variants). - Description hash on the invoice matches sha256 of the metadata string from the lnurlp endpoint (LUD-06 compliance, most LNURL-pay parsers verify this).
- Your endpoint is not rate-limiting the Satogram operator’s IPs aggressively. The service can hit
/.well-known/lnurlp/<user>in tight succession during a campaign. Skipped users get no payment.
Invoice subscriber
- Your invoice subscriber persists a resumable cursor and resumes on restart (LND:
add_index; CLN:lastpay_index; LDK: persisted event handler; Eclair: WebSocket reconnect plus agetreceivedinfosweep of the recent window). - Your subscriber reads TLV
6789998212and splits on,so it handles both single-address (Path A) and batch (Path B) payments. - Your subscriber tolerates unknown recipients in a CSV (a typo, deleted user, etc.) without throwing.
- Credit writes are idempotent on
(payment_hash, recipient)so a subscription replay after restart doesn’t double-credit. - On CLN (v23.08+), your config has
accept-htlc-tlv-type=34349334andaccept-htlc-tlv-type=6789998212. Both Satogram TLVs are even. Without these lines, CLN rejects Path A payments and strips the TLVs from batch keysends.
User experience
- You have a path to display the message (TLV
34349334) to the user in your app: notification copy, transaction history, in-app inbox. - The message field is treated as untrusted UTF-8: escape for HTML rendering, cap displayed length, consider stripping unsafe Unicode (RTL overrides, zero-width chars). There is no verified sender, text inside the message that reads
from alice@example.comis just text the sender typed. - Your accounting layer is happy with credits as small as 1 sat (Path A) and splits batch credits in msat (Path B), or holds sub-floor credits until they cross your floor.
Testing
- You have run the end-to-end regtest recipe in Testing and seen both Path A and Path B credit your test user.
- You have run the negative tests (no TLVs, unknown user, mixed batch) and verified your code’s behavior matches your written policy.
- You have a test recipient in production that you can pay end-to-end to verify the live flow. From an LND sender:
lncli sendpayment --keysend --dest <your-node> --data 34349334=<hex>,6789998212=<hex> --amt 5. From a CLN sender:lightning-cli keysend -k destination=<your-node> amount_msat=5000 extratlvs='{"34349334":"<hex>","6789998212":"<hex>"}'.
Batch keysend (only if opting in)
- Your node accepts keysend (LND
accept-keysend=true/ CLN keysend plugin loaded plus the twoaccept-htlc-tlv-typelines above / LDK on by default / Eclairfeatures.keysend = optional). - Your code applies the delivery fee rate set during onboarding to batch keysends only.
- You have a written unknown-recipient policy (refund / drop / operator-bucket) and your code reflects it.
- You have sent us your domain, node pubkey, and confirmations from Batch keysend opt-in.