Public Endpoints
No-authentication endpoints for subscriber-facing integrations.
These endpoints are designed for checkout flows. They don't require API keys.
Endpoints
/public/plan/{id}Response:
{
"id": "plan_abc123",
"name": "Pro Plan",
"amount_sats": 10000,
"interval": "monthly",
"description": "Access to premium features",
"payment_methods": ["lightning", "onchain"],
"available_contact_methods": ["email", "telegram"]
}/public/subscribeRequest (JSON body):
{
"plan_id": "plan_abc123",
"payment_method": "lightning",
"email": "user@example.com",
"name": "John Doe",
"receive_confirmations": true,
"receive_reminders": true
}| Field | Type | Required | Description |
|---|---|---|---|
plan_id | string | Yes | Plan to subscribe to |
payment_method | string | Yes | lightning, onchain, card, paypal, or nwc |
email | string | No | Subscriber email address |
name | string | No | Subscriber display name |
telegram | string | No | Telegram username for notifications |
nostr | string | No | Nostr npub or NIP-05 identifier |
receive_confirmations | boolean | No | Opt in to payment confirmations (default: true) |
receive_reminders | boolean | No | Opt in to renewal reminders (default: true) |
Response (Lightning):
{
"payment_id": "pay_123",
"payment_method": "lightning",
"payment_request": "lnbc10000n1...",
"payment_hash": "abc123...",
"qr_codes": { "lightning": "<svg>...</svg>" },
"expires_at": 1704070800
}Response (On-chain):
{
"payment_id": "pay_123",
"payment_method": "onchain",
"onchain_address": "bc1q...",
"bip21_uri": "bitcoin:bc1q...?amount=0.0001",
"payment_request": "lnbc...",
"satspay_charge_id": "charge_abc",
"qr_codes": {
"bip21": "<svg>...</svg>",
"lightning": "<svg>...</svg>",
"onchain": "<svg>...</svg>"
},
"expires_at": 1704070800
}Response (Fiat — Stripe/PayPal):
{
"payment_id": "pay_123",
"payment_method": "card",
"checkout_url": "https://checkout.stripe.com/...",
"expires_at": 1704070800
}Response (Trial):
{
"subscription_id": "sub_xyz789",
"status": "active",
"trial": true,
"trial_days": 7,
"message": "Your 7-day free trial has started!"
}Payment Methods:
| Method | Key Fields Returned |
|---|---|
lightning | payment_request, payment_hash, qr_codes.lightning |
onchain | onchain_address, bip21_uri, qr_codes.* |
card | checkout_url (redirect to Stripe) |
paypal | checkout_url (redirect to PayPal) |
nwc | Use /public/subscribe/nwc instead |
NWC Endpoints
Endpoints for NWC (Nostr Wallet Connect) automatic payment subscriptions.
/public/nwc/validateRequest:
{
"nwc_string": "nostr+walletconnect://pubkey?relay=wss://relay.example.com&secret=..."
}Response (valid):
{
"valid": true,
"error": null
}Response (invalid):
{
"valid": false,
"error": "Connection timed out. Please ensure your wallet is online and try again."
}/public/subscribe/nwcRequest:
{
"plan_id": "plan_abc123",
"email": "user@example.com",
"name": "John Doe",
"nwc_connection_string": "nostr+walletconnect://...",
"contact_preference": "email"
}Response (success):
{
"subscription_id": "sub_xyz789",
"status": "active",
"message": "Subscription created and first payment completed"
}Response (payment failed):
{
"subscription_id": "sub_xyz789",
"status": "pending",
"message": "Your wallet doesn't have enough sats for this payment.",
"error_code": "INSUFFICIENT_BALANCE"
}See Status Codes for all NWC error codes.
/public/payment/{id}/statusResponse:
{
"payment_id": "pay_123",
"status": "paid",
"paid_at": 1704067250,
"subscription_id": "sub_xyz789"
}Statuses:
| Status | Meaning |
|---|---|
pending | Waiting for payment |
paid | Payment received |
expired | Invoice expired |
Subscriber Portal
Self-service endpoints for subscribers. Authenticate with the X-Subscriber-Token header.
/public/manage/subscriptionsHeaders:
| Header | Required | Description |
|---|---|---|
X-Subscriber-Token | Yes | Subscriber access token |
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Items per page (1–200) |
offset | integer | 0 | Items to skip |
status | string | — | Filter by status (active, cancelled, etc.) |
Response: PaginatedResponse of subscriptions.
/public/manage/subscription/{id}/cancelHeaders:
| Header | Required | Description |
|---|---|---|
X-Subscriber-Token | Yes | Subscriber access token |
Response:
{
"success": true,
"message": "Subscription cancelled successfully"
}/public/manage/walletsHeaders:
| Header | Required | Description |
|---|---|---|
X-Subscriber-Token | Yes | Subscriber access token |
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Items per page (1–200) |
offset | integer | 0 | Items to skip |
Response: PaginatedResponse of NWC wallet connections.
/public/manage/wallets/{id}Headers:
| Header | Required | Description |
|---|---|---|
X-Subscriber-Token | Yes | Subscriber access token |
Response:
{
"success": true,
"message": "Wallet disconnected"
}Custom Checkout Flow
Build your own checkout instead of using the default subscribe page:
// 1. Get plan info
const plan = await fetch(`/subscriptions_manager/api/v1/public/plan/${planId}`)
.then(r => r.json())
// 2. Create subscription
const checkout = await fetch('/subscriptions_manager/api/v1/public/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
plan_id: planId,
payment_method: 'lightning',
email: userEmail,
name: userName
})
}).then(r => r.json())
// 3. Show QR code (SVG string ready to inject)
if (checkout.qr_codes) {
document.getElementById('qr').innerHTML = checkout.qr_codes.lightning
}
// 4. Poll for payment (every 3s, stop after 5 errors)
let errors = 0
const interval = setInterval(async () => {
try {
const res = await fetch(
`/subscriptions_manager/api/v1/public/payment/${checkout.payment_id}/status`
)
if (!res.ok) { errors++; if (errors >= 5) clearInterval(interval); return }
errors = 0
const status = await res.json()
if (status.status === 'paid') {
clearInterval(interval)
// Redirect to success page
}
} catch (e) { errors++; if (errors >= 5) clearInterval(interval) }
}, 3000)NWC Checkout Flow
For NWC subscriptions with automatic payments:
// 1. Validate NWC connection
const validation = await fetch('/subscriptions_manager/api/v1/public/nwc/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nwc_string: userNwcString })
}).then(r => r.json())
if (!validation.valid) {
showError(validation.error)
return
}
// 2. Create subscription with NWC
const result = await fetch('/subscriptions_manager/api/v1/public/subscribe/nwc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
plan_id: planId,
email: userEmail,
name: userName,
nwc_connection_string: userNwcString,
contact_preference: 'email'
})
}).then(r => r.json())
// 3. Check result
if (result.status === 'active') {
// Success! First payment completed
redirectToSuccess(result.subscription_id)
} else {
// Payment failed - show error message
showError(result.message)
}