Project Subscriptions
Recurring billing for your project — plans, hosted checkout, subscriber portal, dunning, and renewals that settle to your settlement wallet.
Project subscriptions bring Stripe-Billing-style recurring revenue to your project: you create plans (price + interval), share a hosted checkout link, and ZapAPI handles renewals, trials, failed-payment dunning and the subscriber portal. Renewal payments settle to the project's settlement wallet — including your own external wallet via NWC.
Lightning has no pull payments
Cards can be charged silently; Lightning cannot. Renewals work three ways: manual (the subscriber gets an invoice email each cycle), balance (auto-deducted from the subscriber's ZapAPI wallet), or NWC (the subscriber's own wallet auto-pays via a Nostr Wallet Connect budget they approve). The subscriber chooses — you never hold their keys.
1. Create a plan
From Dashboard → your project → Plans, or via the API:
curl -X POST https://api.zapapi.com/api/v1/plans \
-H "Authorization: Bearer zap_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Pro",
"amountSats": 21000,
"interval": "month",
"trialDays": 7,
"perks": ["Priority support", "Early access"]
}'With the SDK (projectId is optional for project-scoped keys):
const plan = await zap.plans.create({
name: 'Pro',
amountSats: 21000,
interval: 'month', // 'week' | 'month' | 'year'
trialDays: 7, // 0–90 days free before the first charge
maxSubscribers: 100, // optional scarcity cap
});2. Share the checkout link
Every plan gets a hosted checkout page:
https://zapapi.app/subscribe/<plan.id>The subscriber enters an email or nostr npub, pays the first invoice, and is
in. No account needed. Trials activate immediately and charge at trial end;
non-trial plans stay pending (no access) until the first payment settles.
3. The subscriber portal
Subscribers receive a magic-link portal (30-day token, refreshed with every renewal receipt) where they can:
- View their plan, period and payment history
- Pause / resume renewals
- Enable NWC auto-pay (their wallet pays renewals automatically)
- Cancel
4. The lifecycle (and dunning)
subscribe → pending → (first payment) → active → renewals…
↘ trialDays > 0 → trialing → (trial ends, paid) → active
renewal fails → 3-day grace (still active)
grace expires → past_due + warning email (subscription.past_due webhook)
7 more days unpaid → canceled (subscription.canceled webhook)Renewals run hourly with bounded parallelism. A paid renewal extends the period exactly once (compare-and-swap settlement — duplicate webhook deliveries can never double-extend).
5. Track revenue and subscribers
From Dashboard → your project → Subscriptions (list, filters, cancel) or the API:
const { subscriptions, total } = await zap.subscriptions.list({
status: 'active',
});
const { activeSubscribers, mrrSats, arrSats } =
await zap.subscriptions.revenue();
await zap.subscriptions.cancel(subscriptionId);Webhook events for your endpoint: subscription.created,
subscription.activated, subscription.renewed, subscription.past_due,
subscription.canceled.
6. Where the money goes
Renewal invoices are minted on the project's settlement wallet (Project → Settlement Wallet): your default ZapAPI wallet, a dedicated project wallet, an account wallet, or your own external wallet via NWC — fully non-custodial when NWC is linked on both sides (your NWC wallet receives; the subscriber's NWC wallet pays).
API reference
| Endpoint | Scope |
|---|---|
POST /v1/plans | plans:write |
GET /v1/plans?projectId= | plans:read |
GET/PATCH/DELETE /v1/plans/:id | plans:read / plans:write |
GET /v1/plans/:id/subscriptions | subscriptions:read |
GET /v1/subscriptions?projectId=&status= | subscriptions:read |
GET /v1/subscriptions/revenue?projectId= | subscriptions:read |
POST /v1/subscriptions/:id/cancel | subscriptions:write |
Public (rate-limited, no auth): GET /v1/subscribe/plans/:id,
POST /v1/subscribe/plans/:id/subscribe, portal endpoints under
/v1/subscribe/portal/:token/*.