Create Salable Only Subscriptions
When you need to grant access and bypass Stripe checkout, create a Salable Only Subscription. They are real Subscriptions with Entitlements and Groups, active immediately, but with no invoice, no payment method, and no Stripe involvement, so to your Entitlement checks it's indistinguishable from one created through checkout. Reach for it to provision access on an enterprise deal settled by purchase order, letting customers trial Plans from higher tiers, or for simply providing access to your app without having to collect payment. For the full lifecycle options and the metered and token nuances, see Subscriptions & Billing.
Creating a Salable Only Subscription
Call POST /api/subscriptions with the Plans you want to grant access to. Because there's no Stripe billing cycle to inherit, you set the lifecycle yourself. The example below renews monthly.
| Field | Description |
|---|---|
owner | The Owner is the tenant the Subscription belongs to, like a team or organisation ID. If your application is single-user, use the user ID. |
plans[].planId | The ID of the Plan to grant. At least one plan is required. |
plans[].grantee | Optional. The ID of the entity in your system, like a user ID or a workspace ID. Use a Salable Group ID (prefixed grp_) when the Plan has a per-seat Line Item. When providing an entity ID that isn't a Salable Group ID, a new Group containing that ID is created for the Owner. If no grantee is provided at all, a new empty Group is created for the Owner. |
plans[].metadata | Optional. Per-Line-Item quantity overrides keyed by Line Item slug, for non-metered items whose maximum quantity is above 1. |
interval | The renewal interval: day, week, month, or year. Required unless the Subscription is perpetual or sets an expiryDate. |
intervalCount | How many intervals per cycle (3 with month renews quarterly). Required whenever interval is set, and vice versa. |
expiryDate | A fixed date for the Subscription period. Set cancelAtPeriodEnd: true to end access on this date. Cannot be combined with isPerpetual. |
isPerpetual | When true, access never expires. Cannot be combined with expiryDate or cancelAtPeriodEnd. |
cancelAtPeriodEnd | When true, the Subscription ends at the end of the current period instead of renewing. Cannot be combined with isPerpetual. |
ignoreTierTagRestrictions | When true, skips Tier Tag conflict checks between the requested Plans and the Owner's active Subscriptions. |
import { Salable } from '@salable/sdk';
const salable = new Salable(process.env.SALABLE_SECRET_KEY);
await salable.api.subscriptions.post({
owner: 'org_acme',
plans: [
{
planId: 'plan_01HXACMEPRO',
grantee: 'user_charlie'
}
],
interval: 'month',
intervalCount: 1
});A successful call returns 201 with the newly created active Subscription and emits a subscription.created webhook event. The Grantees attached to the Subscription get instant access to the Entitlements from the Plans.
Important A Plan with a metered Line Item always needs
intervalandintervalCount, even when you'd otherwise rely on an expiry date or perpetual access, because usage aggregates over a billing period.
Note To grant a Salable Only Subscription alongside any of the Owner's existing Subscriptions where Plans share the same Tier Tag, set
ignoreTierTagRestrictions: true. A Plan that shares a Tier Tag with one of the Owner's active Subscriptions is otherwise rejected.
Subscription end behaviour
The lifecycle is set by the fields you send in the request. Configure it in one of three ways:
- Renew: Set
intervalandintervalCount. The Subscription renews each period with no payment collected and keeps Entitlements valid across periods; addcancelAtPeriodEnd: trueto stop it renewing at the end of the current period. - Fixed expiry: Set
expiryDateandcancelAtPeriodEnd: true, and omitinterval. Access ends automatically on that date, which suits trials, beta windows, and compensation periods. - Perpetual: Set
isPerpetual: trueand omitintervalandexpiryDate. The Subscription stays active until you explicitly cancel it, creating a Perpetual Subscription.
The following is an example of creating a Salable Only Subscription with a fixed expiry:
import { Salable } from '@salable/sdk';
const salable = new Salable(process.env.SALABLE_SECRET_KEY);
await salable.api.subscriptions.post({
owner: 'org_acme',
plans: [
{
planId: 'plan_01HXACMEPRO',
grantee: 'user_charlie'
}
],
cancelAtPeriodEnd: true,
expiryDate: new Date('2026-12-31')
});Note To move a customer onto paid billing, for example when a trial converts, cancel the Salable Only Subscription and send them through the standard checkout flow to create a paid one.
Related
- Subscriptions & Billing The full lifecycle options and the metered and token behaviour in depth.
- Check a Grantee's Entitlements Confirm the access this Subscription grants.
- Build a Cart Checkout Flow Create a paid Subscription through checkout when a Salable Only grant converts to billing.