NIP-XX: Recurring Subscriptions & Treasury Events
NIP-XX: Recurring Subscriptions & Treasury Events
draft optional
This NIP defines event kinds for managing recurring subscriptions over Lightning and automated infrastructure payments within the Nostr ecosystem.
Motivation
Creators and app operators need a protocol-native way to:
- Offer recurring subscriptions paid via Lightning (NWC/NIP-47)
- Verify subscription status from relay data without proprietary APIs
- Manage infrastructure payments (relays, media hosts, compute) under auditable rules
Design Goals
- Non-custodial: No event kind implies custody of funds. Only wallet connections (NWC URIs) and rules are stored.
- Composable: Builds on NIP-47 (Wallet Connect), NIP-57 (Zaps), NIP-17 (Private DMs), NIP-44 (Encrypted Payloads).
- Minimal: Five event kinds cover the full lifecycle.
- Verifiable: Any client can verify an active subscription by checking receipts signed by the plan publisher.
Event Kinds
kind 31039 — Subscription Plan (Addressable)
Published by the provider (creator/app). Defines a subscription tier.
Tags:
d— unique plan identifier (e.g."supporter")title— human-readable plan namedescription— plan descriptionamount— price in millisatoshis per periodperiod— ISO-8601 duration (P7D,P1M,P1Y)grace— grace period as ISO-8601 duration (e.g.P8D)perk— (repeatable) one perk per tagalt— human-readable description for NIP-31 compatibilitycurrency— (optional) fiat reference currency code
{
"kind": 31039,
"pubkey": "<provider-pubkey>",
"tags": [
["d", "supporter"],
["title", "Supporter Tier"],
["description", "Full access plus exclusive content"],
["amount", "21000000"],
["period", "P1M"],
["grace", "P8D"],
["perk", "All articles"],
["perk", "Monthly Q&A"],
["alt", "Subscription plan: Supporter Tier — 21,000 sats/month"]
],
"content": ""
}kind 1359 — Subscription Agreement
Published by the subscriber, referencing the plan. This is the signed, timestamped "contract".
Tags:
a— plan coordinate (31039:<provider-pubkey>:<plan-d-tag>)p— provider pubkeyalt— human-readable description
Content: May contain a NIP-44 encrypted payload with subscriber contact preferences (email hash, notification channel).
{
"kind": 1359,
"pubkey": "<subscriber-pubkey>",
"tags": [
["a", "31039:<provider-pubkey>:supporter"],
["p", "<provider-pubkey>"],
["alt", "Subscription agreement for Supporter Tier"]
],
"content": "<optional NIP-44 encrypted contact preferences>"
}kind 6301 — Payment Receipt
Published by the provider (or its delegated billing key) after each successful payment cycle. Receipts are the public, verifiable proof of active subscription.
Tags:
a— plan coordinatep— subscriber pubkeyperiod_start— unix timestamp of period startperiod_end— unix timestamp of period endamount— amount paid in millisatoshisbolt11— (optional) the BOLT11 invoice referencepreimage— (optional) payment preimage as proofalt— human-readable description
{
"kind": 6301,
"pubkey": "<provider-pubkey>",
"tags": [
["a", "31039:<provider-pubkey>:supporter"],
["p", "<subscriber-pubkey>"],
["period_start", "1719792000"],
["period_end", "1722470400"],
["amount", "21000000"],
["alt", "Subscription receipt: Supporter Tier — Jul 2026"]
],
"content": ""
}kind 4739 — Cancellation / Lapse Notice
Published by either party to record termination or lapse.
Tags:
a— plan coordinatep— the other party's pubkeyreason—"canceled","lapsed", or"expired"alt— human-readable description
{
"kind": 4739,
"pubkey": "<subscriber-or-provider-pubkey>",
"tags": [
["a", "31039:<provider-pubkey>:supporter"],
["p", "<other-party-pubkey>"],
["reason", "canceled"],
["alt", "Subscription canceled: Supporter Tier"]
],
"content": ""
}kind 39729 — Treasury Rule (Addressable, Private)
Published by the creator to define automated payment rules for infrastructure. Content SHOULD be NIP-44 encrypted so configuration is portable but private.
Tags:
d— unique rule identifieralt— human-readable description
Encrypted content (JSON):
{
"payee": "<lightning-address-or-npub>",
"maxAmount": 15000,
"interval": "P1M",
"autoPay": true,
"category": "relay"
}Verification Rules
A reader validates an active subscription for subscriber S on plan P as:
- Find the latest
kind 6301event wherepubkey= plan provider AND#pcontains S AND#acontains P's coordinate. - Parse
period_endtag. - If
period_end>now, the subscription is active. - Additionally check no
kind 4739event exists from either party more recent than the receipt.
Privacy Considerations
- Email addresses MUST NOT appear in plaintext on relays. Use NIP-44 encryption in
kind 1359content or SHA-256 hashes. - Treasury rules (
kind 39729) SHOULD encrypt their content with NIP-44. - NWC connection strings are NEVER published as events. They are stored locally and encrypted at rest.
Interaction with NIP-47
NWC connections used for subscription pull payments SHOULD:
- Have explicit budget caps matching
amount × 1per period - Include expiry dates
- Be revocable by the subscriber at any time
The billing engine MUST be idempotent: for a given (agreement, period) pair, at most one pull payment attempt succeeds. Retries use the same period identifier to prevent double-charging.
kind 7012 — Multi-Rail Payment Receipt (Module E Extension)
Published by the provider after a payment settles via any rail (Stripe, NWC, Zaprite). Extends kind 6301 with rail identification and split allocation transparency.
Tags:
a— plan coordinate (if applicable)p— subscriber pubkey (if known)amount— gross amount in claim currencycurrency— ISO 4217 currency code (CHF, EUR, USD, or SAT for native Lightning)rail— settlement rail identifier:stripe,nwc, orzapritesettled_at— unix timestamp of confirmed settlementexternal_ref— external system reference (Stripe invoice ID, BOLT11, Zaprite order ID)purpose—subscription,share,ticket,crowdfund,auction-clear,one-timeperiod— (optional) billing period identifier for recurring paymentssplit— (repeatable) allocation tag:["split", "<label>", "<amount>", "<percentage>", "<pubkey?>"]fee— estimated processing fee amountalt— human-readable description
{
"kind": 7012,
"pubkey": "<provider-pubkey>",
"tags": [
["a", "31039:<provider-pubkey>:supporter"],
["p", "<subscriber-pubkey>"],
["amount", "19.00"],
["currency", "CHF"],
["rail", "stripe"],
["settled_at", "1720454400"],
["external_ref", "in_live_abc123"],
["purpose", "subscription"],
["period", "1720454400-1723132800"],
["split", "Autorin", "14.44", "80"],
["split", "Gemeinsame Kasse", "2.71", "15"],
["split", "Abwicklung", "0.90", "5"],
["fee", "0.95"],
["alt", "Payment receipt: 19.00 CHF via stripe for subscription"]
],
"content": ""
}Design Notes:
kind 7012complementskind 6301(which remains the Lightning-native receipt). Applications SHOULD query both kinds for complete payment history.- The
railtag enables clients to filter/display receipts by payment method without inspecting rail-specific metadata. splittags make the payout breakdown verifiable by any third party — this transparency is a feature, not a leak (Bajour logic for community-funded media).- The
feetag shows the displayed estimate. The platform may absorb higher actual fees (for small card payments) to give authors a predictable rate.
Prior Art
This NIP was designed with awareness of earlier NIP-88 (recurring subscription) proposals. Key divergences:
- Uses generated, conflict-free kind numbers rather than assumed ranges
- Separates agreement (subscriber-signed) from receipt (provider-signed) for bilateral auditability
- Adds treasury management events for infrastructure spend
- Specifies verification rules for third-party status checks (enabling Module C embeddable primitives)
NIP-XX — Stablezaps: Asset-Denominated Zaps & Recurring Subscriptions
draft optional
Status: Draft v0.1 (skeleton for review) · Author: Pareto / Athanor Studio · License: Public domain Depends on: NIP-01, NIP-57 (Lightning Zaps), NIP-47 (Nostr Wallet Connect) · Relates to: BOLT11, BOLT12, Taproot Assets (Lightning Labs), RGB Kind numbers are provisional pending the NIP review process.
Abstract
This NIP extends NIP-57 zaps with asset denomination (e.g., USDT) and defines a minimal, ledger-derived subscription scheme on top of them. It allows a payment request to be denominated in a stable unit, settled over Lightning either natively (Taproot Assets / RGB asset invoices) or in BTC at a disclosed rate, and it allows recurring creator revenue ("$5/month") whose entire state — active, lapsed, cancelled — is derivable from public zap receipts alone. No custodian, no subscription database, no platform.
Motivation
Zaps made value transfer a native gesture of the social web, but they are denominated in a volatile unit. Tips work in sats; income does not: the proven revenue model of independent publishing is the fixed-price monthly subscription, and it cannot be priced in an asset that moves 10% in a week. Since USDT became transactable over Lightning via Taproot Assets (March 2026), stable-denominated settlement on open rails is possible — what is missing is the Nostr convention that lets any client display, request, verify, and renew such payments interoperably. This NIP is that convention.
Design goals, in order: (1) auditability — every claim about who paid whom, how much, in what unit, must be verifiable from public events and the embedded invoice, never from a self-declared tag alone; (2) backwards compatibility — a NIP-57-only client must degrade gracefully; (3) settlement neutrality — BTC remains the universal settlement asset; asset-denominated requests MUST remain payable in BTC; (4) issuer honesty — clients implementing this NIP MUST NOT present issuer-backed assets as trustless (see Security Considerations).
Terminology
- Stablezap — a zap whose requested amount is denominated in a non-BTC unit.
- Asset invoice — a BOLT11/BOLT12 invoice carrying a Taproot Assets or RGB asset transfer.
- Minor units — integer amounts in the smallest customary unit of the denomination (cents for USD-pegged assets).
- Offer — a creator-published subscription product.
- Cycle — one billing period of an offer.
1. Asset-denominated zap requests (extends kind 9734)
A stablezap request is a standard NIP-57 zap request with the following additional tags:
{
"kind": 9734,
"tags": [
["relays", "..."],
["p", "<recipient-pubkey>"],
["e", "<zapped-event-id>"],
["amount", "<msats-equivalent>"],
["unit", "USDT"],
["uamount", "500", "2"],
["asset", "<asset-id>", "taproot"],
["settle", "asset", "btc"]
],
"content": ""
}Rules:
unit+uamounttogether define the denominated amount.uamountMUST be an integer string; the second element is the decimal count. Clients MUST NOT use floats anywhere.assetidentifies the concrete on-Lightning asset when native settlement is desired. If absent, the request is denomination-only and settlement is expected in BTC at a disclosed rate.settlelists acceptable settlement paths.["settle", "btc"]alone makes this a display-denominated zap ("$5, paid in sats").["settle", "asset"]alone demands native asset settlement; wallets that cannot comply MUST fail rather than silently settle in BTC.- If
amount(msats) is present alongsideuamount, it is a courtesy fallback for NIP-57-only wallets; the denominated amount is authoritative for stablezap-aware clients.
1.1 LNURL / lud16 extension
The recipient's LNURL-pay endpoint advertises stablezap capability by adding to its response:
{
"allowsNostr": true,
"nostrPubkey": "...",
"stablezaps": {
"units": ["USDT"],
"assets": [{"id": "<asset-id>", "proto": "taproot", "unit": "USDT"}],
"settlement": ["asset", "btc"]
}
}When the payer requests an invoice for a stablezap, the endpoint MUST return either (a) an asset invoice matching asset/uamount exactly, or (b) a BTC invoice whose msat amount reflects the denominated amount at the endpoint's current rate, in which case the endpoint MUST also return "rate": "<msats-per-minor-unit>" so the payer can verify the conversion before paying. Payers SHOULD reject rates deviating more than a client-configured tolerance from an independent reference.
2. Stablezap receipts (extends kind 9735)
The zap receipt mirrors NIP-57 and adds:
{
"kind": 9735,
"tags": [
["p", "..."], ["e", "..."],
["bolt11", "<invoice>"],
["description", "<zap-request-json>"],
["preimage", "..."],
["unit", "USDT"],
["uamount", "500", "2"],
["asset", "<asset-id>", "taproot"],
["rate", "<msats-per-minor-unit>"]
]
}2.1 Verification rules (normative)
A receipt claims a stable amount only if it survives all of the following; clients computing totals, leaderboards, or subscription state MUST apply them:
- The embedded zap request (in
description) parses, is validly signed, and itsp/e/atargets match the receipt's tags. - Native settlement: the decoded invoice is an asset invoice whose asset id equals the
assettag and whose asset amount equalsuamount. The self-declared tags are display hints; the invoice is the truth. - BTC settlement: the decoded invoice's msat amount equals
uamount × ratewithin rounding of one minor unit. Clients SHOULD flag receipts whoseratedeviates grossly from historical reference rates for the receipt's timestamp. - The receipt is published by the recipient's LNURL provider pubkey as declared in their profile metadata, where verifiable (as in NIP-57).
- Deduplication by receipt id; one receipt counts once.
Receipts failing 1–4 MUST be treated as unverified and MUST NOT contribute to any displayed stable-denominated total.
3. Subscription offers (addressable kind 37555, provisional)
A creator publishes an offer:
{
"kind": 37555,
"tags": [
["d", "<offer-id>"],
["title", "Freischwebende Intelligenz — Supporter"],
["unit", "USDT"],
["uamount", "500", "2"],
["period", "2592000"],
["grace", "259200"],
["asset", "<asset-id>", "taproot"],
["settle", "asset", "btc"],
["benefit", "Full archive access"],
["visibility", "public"]
],
"content": "<markdown description>"
}4. Subscription state — derived from the ledger, not declared
There is deliberately no "subscription status" event signed by anyone. State is computed:
- A cycle payment is a verified stablezap receipt (per §2.1) whose embedded request contains an
atag referencing the offer (37555:<pubkey>:<offer-id>) and acycletag:["cycle", "<unix-start-of-cycle>"]. - A subscriber is active for cycle N iff a verified cycle payment exists for N, or for N−1 within the offer's
gracewindow. - A subscriber lapses by simply not paying. Cancellation requires no permission and no event — though a client MAY publish kind
7556(["a", <offer-address>], empty content) as a courtesy signal to stop renewal reminders. - Creators verify entitlement by querying receipts against their own offer address — one relay query, no database. Any third party can audit a creator's subscriber revenue the same way. The engagement report and the payment ledger are the same document.
4.1 Renewal execution
Non-custodial auto-renewal is delegated to NIP-47 budgets: the subscriber grants their NWC-connected wallet a standing permission scoped to (offer-address, uamount, unit, period); the client (or the wallet itself) executes the cycle payment when due. Where no NWC budget exists, clients fall back to reminder-based renewal (a local or DM notification with a one-tap pay action). Implementations MUST NOT build custodial pull-payment schemes to work around this; the inconvenience is the cost of self-custody and is accepted by design.
5. Privacy modes
visibility: public(default): receipts are ordinary public events. Maximal auditability; the subscriber list is effectively public. Appropriate for patronage.visibility: private: cycle payments are wrapped per NIP-59 (gift wrap) to the creator; the creator's entitlement checks work as in §4 over unwrapped receipts, but third-party auditability is lost and totals become claims, not proofs. Clients MUST label privately-settled totals as unaudited.
The trade-off is stated rather than hidden: this NIP considers public-by-default the honest configuration for creator revenue and private mode the concession to subscriber privacy.
6. Backwards compatibility
- NIP-57-only clients see stablezap receipts as ordinary zaps (and, where the
amountfallback was set, display a sats value). Nothing breaks. - NIP-57-only wallets can pay denomination-only requests via the BTC fallback path (§1.1 b).
- Offers (
37555) are invisible to legacy clients; entitlement checks are the publisher's concern, so degradation is graceful.
7. Security & trust considerations
- Issuer risk is not abstracted away. USDT and comparable assets are centrally issued and freezable. Clients implementing this NIP MUST NOT describe asset-settled balances or streams as censorship-resistant, and SHOULD display an issuer-risk notice at the point where a user first opts into asset settlement. Sats remain the base layer; this NIP standardizes the pragmatic layer above it — the distinction is part of the spec, not marketing.
- Rate honesty. BTC-settled denominated zaps are the main manipulation surface (inflated
rate→ inflated displayed dollar totals). Hence §2.1(3): the invoice arithmetic, not the tag, is authoritative, and gross rate outliers are flagged. - Fake-asset invoices. Wallets MUST verify asset ids against the offer/request before paying; a matching ticker string is not a matching asset.
- Replay/duplication. Receipt-id dedupe plus cycle-tag uniqueness (one counted payment per subscriber per cycle; extras are tips).
- Regulatory surface. A subscription is a payment for published content; this NIP defines no custody, no pooling, no exchange function. Implementers adding fiat on/off-ramps or custodial wallets import their own regulatory obligations, outside this spec's scope.
8. Reference flows (informative)
A. One-off stablezap, native settlement: client builds 9734 with unit/uamount/asset → LNURL endpoint returns taproot asset invoice → wallet (asset-capable) pays → provider publishes 9735 with asset tag → verifiers apply §2.1(2).
B. One-off stablezap, BTC settlement: as above, endpoint returns BTC invoice + rate → any NIP-57 wallet pays → 9735 carries rate → verifiers apply §2.1(3).
C. Subscription lifecycle: creator publishes 37555 → subscriber's first cycle payment (receipt with a + cycle) → NWC budget authorized → wallet auto-pays each cycle → subscriber stops payment or publishes 7556 → state lapses after grace. No party ever asked permission of a platform.
9. Open questions for review
- Kind numbers (
37555,7556) — collision review against deployed kinds required before merge. - BOLT12 offers as the renewal primitive (recurrence field) once wallet support matures — could replace NWC budgets as the preferred path.
- Multi-asset offers (same price, several acceptable assets) — repeatable
assettags vs. separate offers. - Proration and price changes mid-subscription — current position: a price change is a new offer; old offers are honored until lapse.
- Whether
rateshould additionally cite a signed oracle attestation rather than relying on outlier detection alone.
Skeleton v0.1 — circulated for comment. Reference implementation planned in Conciergstr (billing engine) and the shared zap-validation module used across Pareto/Athanor clients.