Subscriptions
Manage recurring billing with trials, discounts, and lifecycle events
Subscriptions automate recurring billing by charging customers at regular intervals. RevKeen handles billing dates, invoicing, payment collection, and dunning automatically. This page covers the complete subscription lifecycle.
A subscription that collects by Direct Debit still needs an Active mandate and advance notice before each collection. If the mandate is missing at renewal, RevKeen sends a setup invitation — that email authorises the mandate; it does not take the renewal by itself. See Direct Debit.
Subscription Lifecycle
┌─────────┐ ┌──────────┐ ┌────────┐
│ PENDING │────>│ TRIALING │────>│ ACTIVE │
└─────────┘ └──────────┘ └───┬────┘
│
┌────────────────┼────────────────┐
│ │ │
v v v
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PAST_DUE │ │ PAUSED │ │ CANCELED │
└────┬─────┘ └──────────┘ └──────────┘
│
v
┌──────────┐
│ UNPAID │
└────┬─────┘
│
v
┌──────────┐
│ EXPIRED │
└──────────┘Subscription Statuses
| Status | Description | Billing |
|---|---|---|
pending | Subscription created, awaiting first payment | Not billing |
trialing | In free trial period | Not billing |
active | Active and billing normally | Billing |
past_due | Payment failed, retrying | Attempting |
paused | Temporarily paused by customer or merchant | Not billing |
canceled | Subscription ended | Not billing |
unpaid | All payment retries exhausted | Stopped |
expired | Subscription term ended without renewal | Stopped |
Creating Subscriptions
Via Checkout
The most common way to create subscriptions is through checkout links:
- Create a checkout link with a recurring price
- Customer completes checkout
- Subscription is automatically created
- First invoice is generated and charged
Via API
import { RevKeenClient } from "@revkeen/sdk";
const client = new RevKeenClient({ apiKey: process.env.REVKEEN_API_KEY! });
const { data: subscription } = await client.subscriptions.create({
customerId: "11111111-1111-4111-8111-111111111111",
priceId: "55555555-5555-4555-8555-555555555555", // a recurring GBP price
currency: "gbp",
quantity: 1,
// Optional: end a free trial on a set date
trialEnd: "2026-11-01T00:00:00Z",
});
console.log(subscription.status); // "trialing" or "active"If the price has a trial period (
trial_period_days), it is applied automatically. Sendtrial_period_daysto use a different length, or0to start without the trial. The billing interval (for example, "Billed monthly") is shown on invoices, receipts, and the checkout success page.
See Create a subscription for every field, including Direct Debit (paymentRail: "direct_debit" with a mandateId).
Quantity
A subscription created through the API bills one price, multiplied by its quantity. For example, a £10.00 per-seat price with quantity: 5 bills £50.00 each period. Each price appears as its own line on the invoice.
await client.subscriptions.update("sub_xxxxxxxx", { quantity: 5 });Trial Periods
Trials allow customers to try your product before paying:
- Subscription status is
trialing - Customer has full access during the trial
- No payment is collected
- At trial end, first invoice is created and charged
- If payment fails, subscription enters
past_due
Trial periods can be set on the Price or overridden when creating the subscription, with
trial_period_daysor an explicittrialEnd(not both).
Billing Cycle
Each subscription has a billing cycle that determines when invoices are generated:
| Field | Description |
|---|---|
interval | Billing frequency: day, week, month, quarter, half_year, year |
interval_count | Number of intervals between charges (e.g., 2 with month = every 2 months) |
current_period_start | Start of current billing period |
current_period_end | End of current billing period |
billing_cycle_anchor | Day of month/year for billing (e.g., 1st of each month) |
The billing interval is automatically shown on invoices, receipts, and the checkout success page so customers always know when their next charge will occur.
Where the schedule comes from
The product catalogue holds the Billing Schedule template. Checkout links inherit that template. An invoice can use the product standard or, when Customize for this invoice is on, store sale-time terms that win over the product.
After the sale, the subscription is the only runtime truth. Changing the product schedule later does not rewrite live subscriptions. To change an existing customer’s cadence, update that subscription (or cancel and resell) — do not expect a catalogue edit to flow through.
Changing Plans
A plan or quantity change takes effect straight away. The price difference for the rest of the current period is billed on the next renewal invoice, on card and Direct Debit alike:
- Upgrade — the difference is added to the next renewal invoice as its own line.
- Downgrade — the unused time becomes a credit on the next renewal invoice. It is never refunded. If the credit is larger than the renewal, that renewal is £0 and the rest carries to the one after.
- Several changes in one period — their differences are added together into one line on the next invoice.
- No proration — the new price simply starts from the next renewal.
Card subscriptions: Charge difference now. On an active card subscription, an upgrade can be charged to the customer's card at the moment you change the plan, from the dashboard. You confirm the amount, including tax, and prove it is you with a passkey or authenticator app. The plan changes only if the charge succeeds; a declined card changes nothing. Owners and admins are emailed each time. A downgrade credit is never refunded; it stays on the next invoice. Direct Debit subscriptions are always billed on the next invoice. This option is being rolled out and may not be on your account yet.
Change a subscription's plan from its page in the dashboard, where you can review the proration before you confirm, or through the API:
POST /v2/subscriptions/{id}/preview-change-planshows what the change would do: the new renewal amount, the proration for this change, and what the next renewal invoice would add or credit (pending_proration_net_amount_minor). It changes nothing. It needs thesubscriptions:readscope and thesubscription:readpermission.POST /v2/subscriptions/{id}/change-planmakes the change with the same calculation, so it stores the figures the preview showed (as of the moment you call it). Nothing is charged when the plan changes. It needs thesubscriptions:writescope and thesubscription:updatepermission, and sendssubscription.plan_changed.
Both take newPlanId (a price, or a product, which resolves to its default price) and optionally prorationBehavior (next_invoice or none) and effectiveDate. prorationBehavior: invoice_immediately is applied as next_invoice and never charges, on every API version up to 2026-05-01; the response carries a Deprecation header and a Link to the versioning guide. From RevKeen-Version 2026-11-02 it means "charge the card now", which is a dashboard action: a card subscription gets 403 charge_now_requires_interactive_session and any other gets 400 charge_now_card_only, and nothing is changed. The new price must bill on the subscription's own interval and in its currency, and must have an amount above zero; otherwise the call is refused with 400 (interval_mismatch, currency_mismatch, price_amount_missing) and nothing changes.
A Direct Debit subscription cannot change plan through the API: a new amount needs advance notice to the payer, so the call is refused with 409 direct_debit_plan_change_unsupported. Change it from the dashboard.
Cancelling Subscriptions
Active, paused, and past-due subscriptions can be cancelled immediately. Cancellation stops future renewals and revokes access, while preserving existing invoices and payment history. It does not settle or write off unpaid invoices. Cancellation at the end of the billing period is available for active subscriptions.
// Cancel at end of billing period (recommended)
await client.subscriptions.cancel("sub_xxxxxxxx", {
mode: "period_end",
reason: "Customer requested",
});
// Cancel immediately (stop access now)
await client.subscriptions.cancel("sub_xxxxxxxx", {
mode: "immediately",
});Warning: Immediate cancellation stops access right away. Use
mode: "period_end"to let customers use their remaining time.
Pausing and Resuming Subscriptions
Pause an active or trialing subscription immediately, and resume it later:
await client.subscriptions.pause("sub_xxxxxxxx");
// Later
await client.subscriptions.resume("sub_xxxxxxxx");Neither call takes any parameters. They need the subscriptions:write scope and a role that holds the subscription:pause and subscription:resume permissions. Send an Idempotency-Key header to make retries safe.
While paused the subscription has status paused and no renewal invoices are generated or collected. A pause never ends by itself: resume it when you are ready. Nothing already issued is voided or recalled, and invoices that were open when you paused stay open. A Direct Debit subscription also stops collecting, and the pause response lists those open invoices in dd_collection_stopped so you can decide how to collect them.
On resume the subscription returns to active. The periods that elapsed while it was paused are not billed: it keeps its billing day, and the next charge is the next billing date after you resume. A Direct Debit subscription resumes only if its mandate can collect now. Otherwise the call is refused with 409 mandate_not_usable (or 409 direct_debit_not_configured), the subscription stays paused, and details.mandate_usability_code says why.
A subscription in any other status is refused with 400 invalid_status, and nothing changes.
To pause a subscription once its current paid period ends instead, set pauseAtPeriodEnd:
await client.subscriptions.update("sub_xxxxxxxx", { pauseAtPeriodEnd: true });This lets customers use their remaining paid time before the pause.
Billing Anchor Day
Set the day of the month the subscription bills on, when you create it or later:
// Bill on the 15th of each month
await client.subscriptions.update("sub_xxxxxxxx", {
billingAnchorDay: 15, // 1-31
});
// For days 29-31, billing falls back to the last day of shorter monthsSmart Payment
RevKeen automatically retries failed payments, distinguishing between temporary and permanent failures.
| Failure Type | Examples | Retry Behavior |
|---|---|---|
| Soft Decline | Insufficient funds, expired card | Retry on days 1, 3, 7 |
| Hard Decline | Lost/stolen card, fraud | No retry, request new card |
See Smart Payment for the collection strategy, and Dunning for the full recovery schedule, including Direct Debit.
Order Creation on Renewal
When a subscription renews and the invoice is paid, RevKeen checks whether the subscription contains products with a fulfillment_type of physical or digital. If so, an Order is automatically created to track fulfillment for that billing period.
This means a subscription for a monthly physical product (e.g., a supplement box) will create a new order every month when the renewal invoice is paid. Products with
fulfillment_type: none(e.g., SaaS access) do not create orders.
Dunning (Payment Recovery)
When a subscription payment fails, RevKeen automatically enters the dunning process:
- Subscription status changes to
past_due - Payment is retried according to your retry schedule
- Customer receives reminder emails
- If all retries fail, subscription becomes
unpaidorcanceled
Configure dunning behavior at Settings > Dunning including retry intervals, email templates, and end behavior. See the Dunning page for details.
Subscription Events
These public webhook events cover the subscription lifecycle. The full list is in the event catalogue.
| Event | When Triggered |
|---|---|
subscription.created | Subscription is created (before activation) |
subscription.activated | Subscription becomes active after a successful payment |
subscription.renewed | Billing period renews successfully; a new invoice has been created and paid |
subscription.canceled | Subscription is cancelled |
subscription.paused | Subscription is paused immediately, through the API or the dashboard, or by the customer in the portal |
subscription.resumed | A paused subscription is resumed, through the API or the dashboard |
subscription.plan_changed | A subscription moves to another price, through the API or the dashboard |
invoice.created | A renewal invoice is created |
invoice.paid | A renewal invoice is paid |
payment.failed | A renewal payment attempt failed |
collection.failed | A Direct Debit renewal collection failed |
There are no public events for trial endings or dunning transitions yet. A resume the customer makes in the portal, and a pause scheduled for the end of the period taking effect, are not announced by subscription.resumed or subscription.paused. Read the subscription with client.subscriptions.get to see its current status.
Related
- Products and Pricing -- Define subscription products with pricing
- Invoices and Orders -- How subscriptions generate invoices
- Dunning -- Configure payment recovery settings
API reference for this workflow
Owner: david · Reviewed 10 Oct 2026 · Next review 30 Oct 2026