Subscriptions
Settlx subscriptions let you charge customers on a recurring schedule — weekly, monthly, annually, or any custom interval — using crypto. You create a plan once, enroll your customers, and Settlx handles the billing cycle automatically.
How it works
Plan ─── defines interval, amount, currency
│
└─► Subscriber ─── a customer enrolled into a plan
│
└─► Invoice ─── created automatically each billing cycle
│
└─► Customer pays ─► subscriber advances to next period
- Create a plan in the dashboard. Plans define the price, interval, and grace period.
- Enroll a subscriber via API from your backend whenever a customer signs up.
- Settlx creates the first invoice immediately and fires a
subscriber.enrolled webhook.
- When the customer pays, Settlx fires
subscriber.activated and advances their subscription period.
- At the start of each new billing cycle, Settlx creates a new invoice automatically and fires
subscriber.past_due.
- If payment is not received before the grace period ends, the subscriber is expired and
subscriber.expired fires.
Subscriber statuses
| Status | Meaning |
|---|
pending | Enrolled, first invoice created, awaiting first payment |
trialing | In free-trial period — no invoice created until trial ends |
active | Paid up — subscription is in good standing |
past_due | New billing cycle started, invoice created, payment not yet received |
paused | Manually paused by the merchant — no new invoices created |
cancelled | Cancelled by merchant — subscription will not renew |
expired | Grace period elapsed without payment |
Subscribers start as pending and only become active after their first payment is confirmed on-chain. Do not provision access until you receive the subscriber.activated webhook.
Plans
Plans are created and managed from the Subscriptions → Plans section of your dashboard. The following fields are set at creation and cannot be changed after subscribers have enrolled:
| Field | Notes |
|---|
amount | Billing amount per cycle |
currency | Fiat currency (e.g. USD) |
interval | day, week, month, or year |
intervalCount | Number of intervals per cycle (e.g. 3 months = quarterly) |
trialPeriodDays | Days of free trial before first charge |
These fields can be updated at any time:
| Field | Notes |
|---|
name | Display name |
description | Description shown to subscribers |
gracePeriodDays | Days a subscriber has to pay before expiring (minimum 1) |
Grace period
Every subscriber has a grace period — the window they have to pay each invoice before their subscription is expired. The grace period starts from the moment an invoice is created.
- Minimum grace period is 1 day
- If a subscriber does not pay within the grace period, their status transitions to
expired and subscriber.expired fires
- To reinstate an expired subscriber, you must re-enroll them
Trial periods
If a plan has trialPeriodDays > 0, enrolled subscribers start as trialing. No invoice is created during the trial. When the trial ends, the first invoice is generated automatically and billing begins.
Enrolling subscribers
Enroll subscribers from your backend server using your API key:
curl -X POST https://api.settlx.io/api/v1/subscriptions/subscribers \
-H "Authorization: Bearer pk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"planId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "customer@example.com",
"externalId": "usr_789",
"metadata": { "userId": "usr_789" }
}'
See Enroll Subscriber for the full API reference.
Never enroll subscribers from client-side code. Your API key must be kept server-side only.
Receiving subscription webhooks
Set your Webhook URL in Settings → Developer. Settlx will POST all subscription lifecycle events to that URL — signed with the same HMAC-SHA256 secret as invoice webhooks.
Subscription events use a flat payload (no data wrapper):
{
"event": "subscriber.activated",
"subscriberId": "9f1e2d3c-4b5a-6789-abcd-ef0123456789",
"merchantId": "f9e8d7c6-b5a4-3210-9876-543210fedcba",
"planId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "customer@example.com",
"status": "active",
"currentPeriodEnd": "2026-05-19T06:00:00.000Z",
"timestamp": "2026-04-19T06:00:00.000Z"
}
The signature is in the X-Webhook-Signature header — same as invoice webhooks. Verify it the same way — see Webhook Integration.
Handling all event types together
app.post('/webhooks/settlx', express.raw({ type: 'application/json' }), async (req, res) => {
verifySignature(req); // same verification as invoice webhooks
const event = JSON.parse(req.body);
if (event.event.startsWith('invoice.')) {
// Invoice payment events
if (event.event === 'invoice.settled') {
await fulfillOneTimeOrder(event.data.invoice.metadata.orderId);
}
}
if (event.event.startsWith('subscriber.')) {
// Subscription lifecycle events
switch (event.event) {
case 'subscriber.activated':
await activateSubscription(event.email, event.planId);
break;
case 'subscriber.expired':
case 'subscriber.cancelled':
await revokeAccess(event.email);
break;
case 'subscriber.paused':
await pauseAccess(event.email);
break;
case 'subscriber.resumed':
await restoreAccess(event.email);
break;
}
}
res.status(200).json({ received: true });
});
Subscription lifecycle reference
| Event | Status transition | What to do |
|---|
subscriber.enrolled | → pending | Send a welcome email; show payment instructions |
subscriber.activated | → active | Provision access to your product |
subscriber.past_due | → past_due | Send payment reminder; restrict non-critical features |
subscriber.expired | → expired | Revoke access; offer resubscribe flow |
subscriber.cancelled | → cancelled | Schedule access revocation at period end |
subscriber.paused | → paused | Suspend access for the pause duration |
subscriber.resumed | → active | Restore access |
Manual actions
From the dashboard (Subscriptions → Subscribers → [subscriber]) you can:
| Action | When to use |
|---|
| Send Invoice | Manually trigger a new invoice for a pending or past_due subscriber |
| Pause | Temporarily halt billing without cancelling |
| Resume | Restart a paused subscription |
| Cancel | End a subscription immediately or at period end |
Re-billing
If a subscriber is stuck in pending or past_due and you want to prompt them to pay again, use the Send Invoice button on the subscriber detail page (or POST .../rebill via the merchant API). This is fully idempotent — if a pending invoice already exists, no duplicate is created.