Operational Checklist#

Run through this before flipping your integration on for production users.

LNURL-pay endpoint#

  • /.well-known/lnurlp/<user> returns commentAllowed >= 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 LND ValueMsat / CLN amount_msat / Eclair amountMsat, 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 a getreceivedinfo sweep of the recent window).
  • Your subscriber reads TLV 6789998212 and 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=34349334 and accept-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.com is 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 two accept-htlc-tlv-type lines above / LDK on by default / Eclair features.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.