Read more about Tier Tags
Tier tags restrict Owners from purchasing multiple Plans that share the same tag. Plans sharing a tier tag belong to the same tier set, and an Owner can only subscribe to one Plan in a tier set at a time — making those Plans mutually exclusive.
An Owner cannot add multiple Plans from the same tier set to a cart, nor add a Plan from a tier set they are already subscribed to. They can, however, replace an existing Plan with another from the same tier set — they will still only hold one Plan in the set.
View Tier Tags in Core Concepts →
## 2. Generate a Stripe checkout link
In order to accept payment from a user, you will need to generate a checkout link.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data } = await salable.api.checkout.post({
planId: 'plan_01KHNZHBA28YMY720VVD8KVKF5',
owner: 'user_123',
grantee: 'user_123',
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
});
// Redirect user to data.url
```
```js
const response = await fetch('https://salable.app/api/checkout', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
planId: 'YOUR_PLAN_ID',
owner: 'user_123',
grantee: 'user_123',
interval: 'YOUR_INTERVAL', // 'month' or 'year'
intervalCount: 1,
currency: 'YOUR_PLANS_CURRENCY', // USD, GBP, EUR, etc
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
});
const { data } = await response.json();
// Redirect user to data.url
```
## 3. Add entitlement checks to your application
In your application, you will only want to allow the grantee to perform certain actions if they have an active subscription.
We can check for a grantee's active entitlements as follows:
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data } = await salable.api.entitlements.check.get({
queryParameters: {
granteeId: 'user_123'
}
});
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'ad_free_listening');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
```js
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=YOUR_GRANTEE_ID', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data } = await response.json();
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'YOUR_ENTITLEMENT_NAME');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
> **Tip** Setting up Entitlements allows you to easily create **tailored plans** for your customers' requirements. Copy any existing Plan, adjust its Entitlements and cost based on the agreed terms with the customer, and they immediately get the exact feature set you agreed without any code change. Now everything flows through Entitlements, sales can be instant. For a deeper dive, see [Understanding Entitlements](/docs/understanding-entitlements).
## 4. Next steps
Now that you have payment processing and entitlement checking set up in your application, there are some further things you should
set up to enable full subscription handling in your application:
- **[Cancel a Subscription](/docs/subscriptions-and-billing#cancellation-management)** Cancel an active subscription and stop future billing.
- **[Customer Billing Portal](/docs/openapi/scalar#tag/owners/POST/api/owners/{id}/portal)** Let customers manage their subscriptions through a Stripe-hosted portal scoped to the owner's Stripe customer.
- **[Webhooks](/docs/webhooks)** Handle subscription lifecycle events like renewals, payment failures, and cancellations in your application.
---
### Per-Seat Billing Quick Start
Source: https://salable.app/docs/per-seat-quick-start
# Per-Seat Billing Quick Start
Per-seat billing charges customers based on how many users have access to your product, but Stripe alone has no concept of who those users are. Salable handles the entire Stripe webhook lifecycle for you, team onboarding, seat management and keeping entitlements in sync as users join or leave.
## Introduction
Per-seat billing means charging based on the number of grantees. **£10 per user per month**, **£99 per seat per year**, and **£5 per seat per week** are all examples of this billing model.
## 1. Create and configure your product
Before you can start implementing Salable into your codebase, you need to sign up for an account and create a product in the Salable dashboard.
You will need to create a product, the plans that you want the users to be able to subscribe to, and any entitlements your users should be able to access depending on their subscription.
As an example, if your application was a project management tool, you may offer "Team" and "Enterprise" plans, with `advanced_reporting`, `custom_fields`, and `api_access` as your entitlements.
To charge for seats on your "Team" plan, set up a Line Item with a Pricing Type of "Per Seat".
## 2. Generate a Stripe checkout link
To onboard an entire team through a checkout, pass a [Group ID](/docs/grantee-groups#groups) as the `grantee` value. If quantity is not provided, the quantity is set from the number of users in the group. Every user gets the plan's entitlements as soon as the subscription is active.
If you don't pass a group, Salable creates one automatically when the subscription is created. Useful when a single user is buying for themselves who may add teammates later.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data } = await salable.api.checkout.post({
planId: 'plan_01KJWF3VM5HNQ3YRS27K06J64T',
owner: 'company_acme',
grantee: 'test-user-123',
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
});
// Redirect user to data.url
```
### Creating a group upfront (optional)
If you need to create a group with specific team members before checkout, you can do so:
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data: group } = await salable.api.groups.post({
owner: 'company_acme',
name: 'Development Team',
grantees: [
{ granteeId: 'user_alice', name: 'Alice Smith' },
{ granteeId: 'user_bob', name: 'Bob Johnson' }
]
});
const { data } = await salable.api.checkout.post({
planId: 'plan_01KJWF3VM5HNQ3YRS27K06J64T',
owner: 'company_acme',
grantee: group.id,
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
});
```
```js
const response = await fetch('https://salable.app/api/groups', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
owner: 'company_acme',
name: 'Development Team',
grantees: [
{ granteeId: 'user_alice', name: 'Alice Smith' },
{ granteeId: 'user_bob', name: 'Bob Johnson' }
]
})
});
const { data: group } = await response.json();
// Then use the group ID in checkout
const checkoutResponse = await fetch('https://salable.app/api/checkout', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
planId: 'YOUR_PLAN_ID',
owner: 'company_acme',
grantee: group.id, // Specify the pre-created group
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
});
const { data } = await checkoutResponse.json();
```
## 3. Add entitlement checks to your application
In your application, you'll want to check if each grantee has access through their group membership.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data } = await salable.api.entitlements.check.get({
queryParameters: {
granteeId: 'user_alice'
}
});
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'ai_assistant');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
```js
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=user_alice', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data } = await response.json();
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'YOUR_ENTITLEMENT_NAME');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
> **Tip** Setting up Entitlements allows you to easily create **tailored plans** for your customers requirements. Copy any existing Plan, adjust its Entitlements and cost based on the agreed terms with the customer, and they immediately get the exact feature set you agreed without any code change. Now everything flows through Entitlements, sales can be instant.
## 4. Managing team members
As your customers' teams grow, you can add or remove grantees from the group.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
// Update seat quantity
await salable.api.subscriptionPlanLineItems.byId(subscriptionPlanLineItemId).put({
quantity: 3,
proration: 'always_invoice' // only required for Stripe subscriptions
});
// Add a new team member
await salable.api.groups.byId(group.id).grantees.post([
{
type: 'add',
granteeId: 'user_charlie',
name: 'Charlie Brown'
}
]);
```
```js
// Update seat quantity
await fetch(`https://salable.app/api/subscription-plan-line-items/${subscriptionPlanLineItemId}`, {
method: 'PUT',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
quantity: 3, // New seat count
proration: 'always_invoice'
})
});
// Add a new team member
await fetch(`https://salable.app/api/groups/${group.id}/grantees`, {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
type: 'add',
granteeId: 'user_charlie',
name: 'Charlie Brown'
}
])
});
```
## 5. Next steps
Now that you have per-seat payment processing and entitlement checking set up in your application, there are some further things you should set up to enable full subscription handling:
- **[Cancel Subscription](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/cancel)** Cancel an active subscription and stop future billing.
- **[Customer Billing Portal](/docs/openapi/scalar#tag/owners/POST/api/owners/{id}/portal)** Let customers manage their subscriptions through a Stripe-hosted portal.
- **[Groups and Seat Management](/docs/grantee-groups)** Manage team members and seats within a subscription.
---
### Usage-Based Billing Quick Start
Source: https://salable.app/docs/usage-quick-start
# Usage-Based Billing Quick Start
Usage-based billing ties cost to consumption, charging customers only for what they actually use. Salable handles the Stripe webhook lifecycle for you, scopes usage to the right owner across multiple organisations, and prevents accidental duplicate subscriptions to the same meter. Salable also lets you combine metered and per-seat line items on a single subscription, meaning every member in the team contributes to the same meter.
## Introduction
Usage-based billing means charging based on consumption. **£0.01 per API call**, **£0.10 per GB of storage**, and **£0.05 per message sent** are all examples of this billing model.
## 1. Create and configure your product
Before you can start implementing Salable into your codebase, you need to sign up for an account and create a product in the Salable dashboard.
You will need to create a product, the plans that you want the users to be able to subscribe to, and any entitlements your users should be able to access depending on their subscription.
As an example, if your application was an AI image generation tool, you may offer "Starter" and "Pro" plans, with `high_resolution_exports`, `commercial_rights`, and `priority_processing` as your entitlements.
To charge for usage on your "Pro" plan, set up a Line Item with a Pricing Type of "Metered". Create a Meter (like `image_generations`) and set your per-unit price.
## 2. Generate a checkout link
To accept payment from a user, you will need to generate a checkout link.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('secret-api-key');
const { data } = await salable.api.checkout.post({
planId: 'plan_01KJWF3VM5HNQ3YRS27K06J64T',
owner: 'user_123',
grantee: 'user_123',
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
});
// Redirect user to data.url
```
```js
const response = await fetch('https://salable.app/api/checkout', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
planId: 'YOUR_PLAN_ID',
owner: 'user_123',
grantee: 'user_123',
interval: 'YOUR_INTERVAL', // 'month' or 'year'
intervalCount: 1,
currency: 'YOUR_PLANS_CURRENCY', // USD, GBP, EUR, etc
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
});
const { data } = await response.json();
// Redirect user to data.url
```
Once the customer completes checkout, Salable automatically creates a Usage Record to track their consumption.
## 3. Record usage in your application
As users consume your service, record their usage to the appropriate meter. Usage recording returns immediately and processes in the background.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
await salable.api.usage.record.post({
owner: 'user_123',
meterSlug: 'image_generations',
increment: 1
});
```
```js
await fetch('https://salable.app/api/usage/record', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
owner: 'user_123',
meterSlug: 'image_generations',
increment: 1
})
});
```
> **Note** Passing [owner](/docs/grantee-groups#owners) scopes consumption to a specific tenant. All users belonging to that owner contribute to the same meter, this is useful for plans that combine metered with per-seat line items. It also handles users who belong to multiple tenants: pass the right owner, and Salable applies the usage to the right meter.
## 4. Add entitlement checks to your application
In your application, you will only want to allow the grantee to perform certain actions if they have an active subscription.
We can check for a grantee's active entitlements as follows:
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data } = await salable.api.entitlements.check.get({
queryParameters: {
granteeId: 'user_123'
}
});
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'image_generations');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
```js
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=YOUR_GRANTEE_ID', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data } = await response.json();
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'YOUR_ENTITLEMENT_NAME');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
> **Tip** Setting up Entitlements allows you to easily create **tailored plans** for your customers requirements. Copy any existing Plan, adjust its Entitlements and cost based on the agreed terms with the customer, and they immediately get the exact feature set you agreed without any code change. Now everything flows through Entitlements, sales can be instant.
## 5. Display usage to customers
You can retrieve current usage data to display in your application's dashboard.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data } = await salable.api.usageRecords.get({
queryParameters: {
owner: 'user_123',
meterSlug: 'image_generations',
status: ['current']
}
});
const currentUsage = data[0]?.count || 0;
```
```js
const response = await fetch('https://salable.app/api/usage-records?owner=user_123&meterSlug=image_generations&status=current', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data } = await response.json();
const currentUsage = data[0]?.count || 0;
```
## 6. Next steps
Now that you have usage-based payment processing and entitlement checking set up in your application, there are some further things you should set up to enable full subscription handling:
- **[Cancel Subscription](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/cancel)** Cancel an active subscription and stop future billing.
- **[Customer Billing Portal](/docs/openapi/scalar#tag/owners/POST/api/owners/{id}/portal)** Let customers manage their subscriptions through a Stripe-hosted portal.
- **[Metered Usage and Billing Cycles](/docs/metered-usage)** Learn how usage records are aggregated and billed at the end of each period.
---
### Hybrid Pricing Quick Start
Source: https://salable.app/docs/hybrid-pricing-quick-start
# Hybrid Pricing Quick Start
Hybrid pricing combines flat-rate, per-seat, and metered line items in any combination on a single subscription. This guide walks through setting it up with Salable, where Stripe webhooks are handled out of the box and entitlements stay in sync across every plan on the subscription.
## Introduction
Hybrid pricing means combining multiple billing models in a single plan. **£50 base fee + £10 per user per month + £0.01 per API call** is an example of this billing model.
## 1. Create and configure your product
Before you can start implementing Salable into your codebase, you need to sign up for an account and create a product in the Salable dashboard.
Create a product, the plans that you want your users to be able to subscribe to, and any entitlements they should be able to access depending on their subscription.
As an example, if your application was a team collaboration platform, you may offer a "Professional" plan with `advanced_analytics`, `custom_integrations`, and `priority_support` as your entitlements.
To create hybrid pricing on your "Professional" plan, you'll add multiple Line Items:
1. A "Platform Fee" Line Item with a Pricing Type of "Flat Rate" (e.g., £50/month)
2. A "Team Members" Line Item with a Pricing Type of "Per Seat" (e.g., £10/user/month)
3. An "API Usage" Line Item with a Pricing Type of "Metered" using a meter like `api_calls` (e.g., £0.01/call)
## 2. Create a group with team members
For the per-seat line item of your hybrid pricing, you'll need to create a group that represents a team.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data: group } = await salable.api.groups.post({
owner: 'company_acme',
name: 'Development Team',
grantees: [
{ granteeId: 'user_alice', name: 'Alice Smith' },
{ granteeId: 'user_bob', name: 'Bob Johnson' },
{ granteeId: 'user_charlie', name: 'Charlie Brown' }
]
});
```
```js
const response = await fetch('https://salable.app/api/groups', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
owner: 'company_acme',
name: 'Development Team',
grantees: [
{ granteeId: 'user_alice', name: 'Alice Smith' },
{ granteeId: 'user_bob', name: 'Bob Johnson' },
{ granteeId: 'user_charlie', name: 'Charlie Brown' }
]
})
});
const { data: group } = await response.json();
```
## 3. Generate a checkout link
When creating a checkout for a hybrid pricing plan, specify the [Group ID](/docs/grantee-groups#groups) as the `grantee` value. As soon as the subscription is active, every user in the group inherits the plan's entitlements. If an explicit quantity isn't provided, we calculate it based on the group's size.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data } = await salable.api.checkout.post({
planId: 'plan_01KJWF3VM5HNQ3YRS27K06J64T',
owner: 'company_acme',
grantee: group.id,
interval: 'month',
intervalCount: 1,
currency: 'USD',
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
});
// Redirect user to data.url
```
```js
const response = await fetch('https://salable.app/api/checkout', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
planId: 'YOUR_PLAN_ID',
owner: 'company_acme',
grantee: group.id,
interval: 'YOUR_INTERVAL', // 'month' or 'year'
intervalCount: 1,
currency: 'YOUR_PLANS_CURRENCY', // USD, GBP, EUR, etc
successUrl: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
});
const { data } = await response.json();
// Redirect user to data.url
```
Once the customer completes checkout, they'll be charged for all Line Items: the base platform fee, the per-seat charges, and Salable will begin tracking usage for metered billing.
## 4. Record usage in your application
As users consume your metered services, record their usage to the appropriate meter. Usage recording returns immediately and processes in the background.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
await salable.api.usage.record.post({
owner: 'company_acme',
meterSlug: 'api_calls',
increment: 1
});
```
```js
await fetch('https://salable.app/api/usage/record', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
owner: 'company_acme',
meterSlug: 'api_calls',
increment: 1
})
});
```
> **Note** Passing [owner](/docs/grantee-groups#owners) scopes consumption to a specific tenant. All users belonging to that owner contribute to the same meter, this is useful for plans that combine metered with per-seat line items. It also handles users who belong to multiple tenants: pass the right owner, and Salable applies the usage to the right meter.
## 5. Add entitlement checks to your application
In your application, you'll want to check if each individual grantee has access through their group membership.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data } = await salable.api.entitlements.check.get({
queryParameters: {
granteeId: 'user_alice'
}
});
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'advanced_analytics');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
```js
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=user_alice', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data } = await response.json();
const granteeHasEntitlement = data.entitlements.find(e => e.value === 'YOUR_ENTITLEMENT_NAME');
if (granteeHasEntitlement) {
// Allow the user to perform the action in your system.
}
```
> **Tip** Setting up Entitlements allows you to easily create **tailored plans** for your customers requirements. Copy any existing Plan, adjust its Entitlements and cost based on the agreed terms with the customer, and they immediately get the exact feature set you agreed without any code change. Now everything flows through Entitlements, sales can be instant.
## 6. Managing team members and usage
As your team grows, you can add or remove grantees and update seat quantities for your per-seat Line Items. You can also retrieve usage data for your metered Line Items to display in your dashboard.
```js
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
// Add a new team member
await salable.api.groups.byId(group.id).grantees.post([
{
type: 'add',
granteeId: 'user_diana',
name: 'Diana Prince'
}
]);
// Update seat quantity (only for per-seat Line Items)
await salable.api.subscriptionPlanLineItems.byId('spli_01KHRQ6CRK2HW7CHXMYFGXFSNX').put({
quantity: 4 // New seat count
});
// Retrieve current usage (only for metered Line Items)
const { data: usageData } = await salable.api.usageRecords.get({
queryParameters: {
owner: 'company_acme',
meterSlug: 'api_calls',
status: ['current']
}
});
const currentUsage = usageData[0]?.count || 0;
```
```js
// Add a new team member
await fetch(`https://salable.app/api/groups/${group.id}/grantees`, {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify([
{
type: 'add',
granteeId: 'user_diana',
name: 'Diana Prince'
}
])
});
// Update seat quantity (only for per-seat Line Items)
await fetch(`https://salable.app/api/subscription-plan-line-items/${perSeatSubscriptionPlanLineItemId}`, {
method: 'PUT',
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
quantity: 4 // New seat count
})
});
// Retrieve current usage (only for metered Line Items)
const usageResponse = await fetch('https://salable.app/api/usage-records?owner=company_acme&meterSlug=api_calls&status=current', {
headers: {
Authorization: 'Bearer YOUR_SECRET_KEY'
}
});
const { data: usageData } = await usageResponse.json();
const currentUsage = usageData[0]?.count || 0;
```
## 7. Next steps
Now that you have hybrid payment processing with multiple line items set up in your application, there are some further things you should set up to enable full subscription handling:
- **[Cancel Subscription](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/cancel)** Cancel an active subscription and stop future billing.
- **[Customer Billing Portal](/docs/openapi/scalar#tag/owners/POST/api/owners/{id}/portal)** Let customers manage their subscriptions through a Stripe-hosted portal.
- **[Combining Multiple Line Items](/docs/products-and-pricing#combining-multiple-line-items)** Learn how to mix flat-rate, per-seat, and usage-based charges on a single plan.
- **[Groups and Seat Management](/docs/grantee-groups)** Manage team members and seats within a subscription.
- **[Metered Usage and Billing Cycles](/docs/metered-usage)** Learn how usage records are aggregated and billed at the end of each period.
---
### Getting Started with Salable
Source: https://salable.app/docs/getting-started-guide
# Getting Started with Salable
This guide will walk you through everything you need to know to get your subscription billing up and running. In less than 30 minutes, you'll have a complete pricing structure ready to accept payments
## What You'll Accomplish
By the end of this guide, you'll have a [Subscription](/docs/core-concepts#subscription) and [Entitlement](/docs/core-concepts#entitlement) management system running in Salable. You'll create a [Product](/docs/core-concepts#product) with multiple [Plans](/docs/core-concepts#plan) and [Line Items](/docs/core-concepts#line-item) with recurring or one-time charges. You'll add items to your [Cart](/docs/core-concepts#cart), create your first test Subscription, configure Entitlements that control feature access, and verify that your subscribed customer has the correct permissions. You'll also configure multi-currency support, working checkout links, and API keys for integrating with your application.
## 1. Create Your First Product
**[Products](/docs/core-concepts#product)** represent what you're selling. They contain your Plans and pricing configuration.
### 1.1 Navigate to Products
In your sidebar, click Products to view your Products list.
### 1.2 Create a New Product
There are two ways to create a Product.
**Option A: Simple Form (Recommended)**
Enter the name for your Product in the Product Name field (_eg_ "My SaaS Platform") and click Create Product. Your new Product should appear in the table below.
**Option B: YAML Import (Advanced)**
Download the Product template by clicking Download Template, edit the YAML file with your Product configuration, and upload it using the Import button.
### 1.3 Configure Product Settings
Find your Product in the table and click the Edit button (pencil icon) to open configuration.
### 1.4 Configure Checkout Settings
Click the Settings accordion to expand configuration options.
Set up the checkout experience by configuring the Success URL (where customers go after a successful purchase, like `https://yourapp.com/welcome`) and the Cancel URL (where customers go if they abandon checkout, like `https://yourapp.com/pricing`).
### 1.5 Additional Options
Configure optional settings based on your needs:
- **Allow Promo Codes at Checkout**: Enable this to let customers apply discount codes during checkout. When enabled, a promo code field appears on the Stripe checkout page, allowing customers to enter promotional codes you've configured in your Stripe dashboard.
- **Collect Tax Automatically**: Enable this to let Stripe calculate and collect taxes based on customer location. Stripe Tax determines the correct tax rate and applies it to the invoice. Requires collecting the customer's billing address for tax jurisdiction.
- **Collect Billing Address**: Enable this to display billing address fields during checkout. Required if you're using automatic tax calculation, since Stripe needs the customer's location for tax rates. The billing address also appears on invoices and receipts.
- **Collect Shipping Address**: For physical Products that require shipping, enable this option. It adds shipping address fields to the checkout flow. This address is separate from the billing address.
- **Return Entitlements While Past Due**: Controls feature access when a customer's payment fails but their Subscription hasn't been cancelled. When enabled, customers keep their Entitlements during the grace period while Stripe retries payment. When disabled, access is revoked immediately on payment failure.
- **Card Pre-fill Preference**: Controls how saved payment methods are handled at checkout. Choose None for an empty payment form, Choice to let customers select from saved cards or add a new one, or Always to use the customer's default payment method.
> **Important** Enabling **Collect Tax Automatically** requires opting in to Stripe Tax in your Stripe Dashboard. Enable and configure Stripe Tax in your Stripe account settings before using this option.
### 1.6 Save Product Settings
Click Save to persist your changes.
## 2. Create Your First Plan
**[Plans](/docs/core-concepts#plan)** define a payment model and the [Entitlements](/docs/core-concepts#entitlement) available to subscribers (_eg_ Basic, Standard, Pro tiers).
### 2.1 Create a Plan
Scroll to the Plans section and enter a name for your Plan in the Plan Name field (_eg_ "Starter Plan"). Click Create Plan and a Plan configuration form will appear.
### 2.2 Configure Plan Settings
Verify or update your Plan name and optionally enter a number of days for the Trial Period to offer a free trial.
### 2.3 Add a Tier Tag
Tier Tags prevent [Owners](/docs/core-concepts#owner) from purchasing multiple Plans that share the same tag, making Plans mutually exclusive. Enter a name in the Tier Tag field to create one.
### 2.4 Add Entitlements
**[Entitlements](/docs/core-concepts#entitlement)** determine which features customers can access based on their Plan. Attach Entitlements to Plans and check them in your application instead of managing feature flags manually.
In the Entitlements field, use the typeahead to search for existing Entitlements or create new ones. Enter a name (_eg_ `premium_features` or `api_access`) and click Create to add it to your Plan.
## 3. Add Line Items and Pricing
**[Line Items](/docs/core-concepts#line-item)** are the pricing components that make up your Plan's pricing model. A Plan can combine multiple Line Items, for example a recurring monthly fee plus a one-time setup charge.
### 3.1 Understanding Line Item Types
Salable supports four pricing types that you can mix and match within a single Plan:
#### Flat Rate
A fixed price charged per billing cycle, regardless of usage or quantity. The most common pricing model for SaaS applications, like a \$29/month base Plan.
#### Per-Seat
The price is multiplied by the number of seats or users in the Subscription. For example, \$10 per user per month means a team of 5 users pays \$50/month.
#### Metered
Charges based on actual consumption during the billing period. Examples: \$0.01 per API call, \$0.50 per GB of storage, or \$5 per 1,000 emails sent.
#### One-Off
A single charge that doesn't recur. Commonly used for setup fees, onboarding charges, or one-off purchases. For example, a \$99 implementation fee charged once when a customer first subscribes.
### 3.2 Add Your First Line Item
Click Add Line Item to begin. Enter a name for the Line Item in the Line Item Name field (_eg_ "Monthly Subscription").
> **Important** Line item names appear on Stripe invoices, so use clear, customer-facing language. Avoid internal codes or technical jargon.
The **Slug** is auto-generated from the Line Item's name and must be unique in your organisation, but you can update it to another value if you prefer. The Slug is used as a pretty identifier for managing quantities in the cart and checkout.
You can optionally add a **Nickname** for internal reference, which won't be shown to customers.
You can optionally enable Allow Changing Quantities to let customers adjust quantities in the checkout.
Select Recurring for the Interval Type if charges should repeat each billing cycle (most common), or One-off for single charges, such as setup fees.
### 3.3 Choose Your Pricing Type
Select the pricing type that matches your billing model:
#### Flat Rate
Select Flat Rate as your pricing type. Optionally configure **Min Quantity** and **Max Quantity** to allow customers to purchase multiple units.
#### Per-Seat
Select Per-Seat as your pricing type, then choose a Billing Scheme:
- **Per Unit**: Multiplies your price by the seat count (_eg_ \$10 × 5 users = \$50/month)
- **Flat Rate**: Charges a fixed total regardless of seat count
- **Tiered**: Applies volume discounts or graduated pricing based on seat count
Configure **Min Quantity** and **Max Quantity** to set the allowed seat range. Your Basic Plan might cap at 10 seats, while Pro requires a minimum of 11.
> **Important** Each Plan can only have one Per-Seat Line Item.
#### Metered
Select Metered as your pricing type, then choose a Billing Scheme:
- **Per Unit**: Multiplies your rate by actual usage (_eg_ \$0.01 × 1,000 API calls = \$10)
- **Tiered**: Applies volume or graduated pricing based on usage totals
Use the Select Meter typeahead to choose an existing meter or create a new one. Meters track usage and prevent double-billing.
#### Tiered Pricing (Per-Seat and Metered)
If you selected Tiered billing, choose a Tier Mode:
- **Volume**: All units charged at the rate of whichever tier the total falls into. Example: 0–10 units cost \$10 each, 11+ cost \$8 each. At 15 units, all 15 are charged at \$8 = \$120.
- **Graduated**: Different rates apply to each tier separately. Example: first 10 units at \$10 each, next 10 at \$8 each. At 15 units: (10 × \$10) + (5 × \$8) = \$140.
### 3.4 Configure Prices and Currencies
Configure prices for each billing interval (monthly, yearly) and currency (USD, GBP, EUR) you support.
#### Add a Price Interval
In your Line Item, click Add Price and select a Billing Interval from Day, Week, Month (most common), or Year.
> **Pro Tip** You can add multiple intervals. For example, offer both monthly (\$29) and yearly (\$290 – 17% discount) options.
#### Add Currency Options
For each interval, you can support multiple currencies. Click Add Currency and select a Currency from the dropdown (_eg_ USD, GBP, EUR etc). Enter the pricing based on your billing scheme.
#### For Per-Unit or Flat-Rate:
Enter the Unit Amount as the price (for example, 29 or 29.00 for \$29.00).
> **Note** Some currencies do not support decimal values and entering a decimal value may cause an error. For zero-decimal currencies such as JPY or KRW, only whole numbers are allowed (_eg_ 1000 for 1000 JPY).
#### For Tiered Pricing:
Configure each tier by setting the First Unit (auto-calculated from the previous Tier), entering the Last Unit as the upper limit (or "inf" for the final tier), and specifying the Unit Amount as the price per unit in this Tier. You can add a Flat Amount as a base fee for reaching this Tier. Click Add Tier to add more Tiers.
**Example Tiered Pricing:**
```
Tier 1: Units 1–10 → $10/unit + $0 flat
Tier 2: Units 11–50 → $8/unit + $0 flat
Tier 3: Units 51–inf → $5/unit + $0 flat
```
Repeat for each currency you want to support. Stripe can auto-detect customer location and display the appropriate currency at checkout.
### 3.5 Add More Line Items (Optional)
Click Add Line Item to add more charges. Common combinations: base fee plus per-seat charges, monthly fee plus metered usage, or one-time setup fee plus recurring Subscription.
## 4. Save and Review Your Plan
### 4.1 Review Your Configuration
Before saving, verify your configuration:
- **Plan Name**: Use customer-facing names (appears on checkout and invoices)
- **Line Items**: Correct pricing types configured with appropriate quantities
- **Pricing Intervals**: All billing intervals have prices configured
- **Currency Support**: All target currencies have prices for each interval
- **Tier Structures**: Breakpoints are logical, final tier ends with "inf"
### 4.2 Save the Plan
Click the Save Plan button. Your Plan is now ready to accept Subscriptions.
### 4.3 Create Additional Plans (Optional)
To offer multiple tiers, repeat steps 2-4 with different configurations (_eg_ Starter at \$29/month flat rate, Professional at \$99/month per-seat, Enterprise with custom tiered pricing).
## 5. Test Your First Checkout
### 5.1 Add Your Plan to Cart
At the bottom of your Plan, you'll find the Add to Cart form:
- **Currency**: Select from your configured currencies
- **Interval**: Choose the billing frequency (Month, Year, etc.)
- **[Owner](/docs/core-concepts#owner)** (required): An ID in your system for looking up Subscriptions, typically an organisation, team, or user ID
- **[Grantee](/docs/core-concepts#grantee)** (optional): The entity receiving feature access, typically a user ID. Can be assigned later for anonymous checkouts.
Click Add to Cart to add the Plan to your **[Cart](/docs/core-concepts#cart)**.
### 5.2 View Your Cart
Click Go to Cart to view your Cart. You can review the Plan, interval, currency, quantity, and price.
### 5.3 Complete Test Checkout
Click Checkout Cart to be redirected to Stripe's checkout page. Use [Stripe's test cards](https://docs.stripe.com/testing) (_eg_ `4242 4242 4242 4242`, any future expiry, any 3-digit CVC). Complete checkout and you'll be redirected to your Success URL.
You've created your first test Subscription.
## 6. View Your Subscription
### 6.1 Navigate to Subscriptions
In your sidebar, click Subscriptions to see your newly created test Subscription.
### 6.2 Explore Subscription Details
Click on the Subscription to view:
- **Status**: Active, trialling, past_due, cancelled, etc.
- **Plans Included**: All Plans attached to this Subscription
- **Line Items and Pricing**: Breakdown of all charges
- **Entitlements Granted**: Feature access permissions
- **Billing Cycle**: Current period, next renewal date
- **Payment History**: Past invoices and records
### 6.3 Test Subscription Management
Try these management actions:
- **Update Quantities**: Adjust seat count to see proration in action
- **Add Additional Plans**: Simulate purchasing add-ons
- **View Upcoming Invoice**: Preview the next charge
- **Cancel Subscription**: Test cancel at period end vs immediate cancellation
### 6.4 Check Entitlements
Verify the subscribed user has access to the Entitlements you configured.
**Via the Dashboard**
Navigate to Entitlement Check in your sidebar. Enter the Grantee ID and click Check Grantee to see all Entitlements your Grantee has access to.
**Via the API**
```bash
curl "https://salable.app/api/entitlements/check?granteeId=user_123" \
-H "Authorization: Bearer YOUR_PUBLISHABLE_KEY"
```
The response includes an array of Entitlements. Check whether the specific Entitlement you need is in the array before granting access to that feature.
## 7. Get Your API Keys
In your sidebar, click API Keys. You'll see two types:
- **Publishable Key**: Safe to use in frontend code
- **Secret Key**: Must be kept secure on your backend
Click the copy button and store them securely (_eg_ in your `.env`). Test Mode and Live Mode have separate keys.
> **Warning** Never expose your Secret Key publicly.
## Common Questions
### Can I change pricing for existing Subscriptions?
You can update prices and sync existing Subscriptions to the new prices, or keep existing customers on their current pricing. Use the **Sync Subscriptions** feature in the Product editor.
### How do I handle Plan upgrades and downgrades?
Salable handles this with [proration options](/docs/core-concepts#proration):
- **Charge on next invoice**: Prorated adjustment on the next billing cycle
- **Charge immediately**: Instant invoice with prorated amounts
- **No refund, charge next**: Switch now, new charges start next cycle
### Can customers purchase multiple Plans at once?
Customers can add multiple Plans to their cart and checkout in a single transaction, useful for base Product plus add-ons.
> **Note** Each Plan can only be added to the cart once.
## Summary
You now have a Product with Plans and Line Items, a working checkout flow, API keys, and your first test Subscription. Your billing infrastructure is ready.
---
### Core Concepts
Source: https://salable.app/docs/core-concepts
# Core Concepts
This reference covers the core terminology in Salable. From products and plans to entitlements and grantee groups, each concept is explained to help you understand how the pieces fit together and get up and running quickly.
## Foundational Concepts
### Test Mode vs Live Mode
Salable operates in two environments. In Test Mode, you use separate test API keys and Stripe's test cards for checkout. No real money changes hands, so it's safe for development and integration testing. When you're ready, switch to Live Mode, which uses separate live API keys and processes real payments. Live Mode requires you to complete Stripe's onboarding process before you can accept payments in production.
## Pricing Hierarchy
### Product
A Product is the top-level container representing what you're selling, whether that's a SaaS application or a service offering. Each Product contains one or more Plans and includes a name, description, and settings that control how your pricing works.
### Plan
A Plan is a bundle of offerings, services, feature sets, or tiers at the payment model you define. It brings together the pricing components for a specific Subscription option: your Basic tier, Pro tier, an Analytics Add-on, or any other offering you want to sell.
Each Plan belongs to exactly one Product and can contain one or more Line Items with different pricing models. You can mix flat fees, per-seat charges, usage-based pricing, and one-time fees within a single Plan. A base Subscription fee plus per-user pricing plus metered API calls? Add those Line Items to your Plan.
Plans are the purchasable entities in your app. When a customer subscribes, they're subscribing to a Plan. Line items within a Plan can have different billing intervals and frequencies. When a customer adds a Plan to their cart, they specify an interval (like "month") and a value (like "1" for monthly or "3" for quarterly). If a currency is provided, Salable cherry-picks only the Line Items that match the requested interval, value, and currency combination. You can design sophisticated pricing models without creating dozens of separate Plans for each variation.
### Line Item
Line items are the individual pricing components that make up a Plan. You can combine multiple Line Items within a single Plan to create varied pricing models. Each Line Item can have different billing intervals and values.
There are four types of Line Items you can use:
**Flat Rate** charges a fixed amount every billing cycle, regardless of usage. For example, a $29/month platform fee that every subscriber pays.
**Per-Seat** pricing multiplies the charge by the number of users, licenses, or seats. You might charge $10 per user per month, and you can set minimum and maximum quantities. Per-seat Line Items also support tiered pricing, so you can offer volume discounts as teams grow.
**Metered** pricing is usage-based. You bill customers based on what they use during the billing period, such as $0.01 per API call or $0.05 per GB of storage. These Line Items use slugs for tracking usage across billing periods.
**One-Time** charges happen once and never recur. For example, a $99 onboarding fee billed only when someone first subscribes.
### Price
A Price represents the actual monetary value and configuration for a Line Item in a specific currency. Each Line Item can have Prices in multiple currencies, and each Price includes the amount, currency, and billing scheme details.
You can modify Prices at any time. Existing Subscriptions continue using the Price version they started with and won't update to new pricing unless you explicitly move them. You can grandfather existing customers on old pricing or migrate them to your new rates.
### Interval
The interval defines the billing frequency unit: day, week, month, or year. Combined with an interval value (a multiplier), you can create any billing frequency. An interval of "month" with a value of 1 means monthly billing. Change that value to 3 for quarterly billing, or use "week" with a value of 2 for biweekly billing.
When customers add a Plan to their cart, they specify the interval and optionally the interval value (which defaults to 1 if not provided). Salable then cherry-picks only the Line Items from that Plan that match the requested combination. Intervals also affect how proration is calculated when Subscription changes occur mid-cycle.
### Currency
Currency represents the monetary unit for pricing and billing. How currency works depends on whether you specify it when creating a cart.
**When you provide a currency**: Salable cherry-picks only the Line Items from the Plan that have Prices in that specific currency and interval combination. You can create region-specific pricing models, such as different Line Items or pricing structures for USD vs EUR customers.
**When you don't provide a currency (geolocation mode)**: Stripe detects the customer's location and displays the appropriate currency. Every Line Item in the Plan must have the same default currency, and each Line Item should have Prices for every currency you want to support. If the defaults don't match, the checkout link will error. If a Line Item is missing a Price for a specific currency, that Line Item falls back to its default currency. In this mode, all Line Items are used—no cherry-picking occurs.
## Access Control
### Owner
An owner is the tenant that a Subscription, cart, receipt, or usage record belongs to in Salable. It's the entity in your application that a purchase is scoped to, most often a team, organization, or workspace. For a single-user product, the tenant and the user are the same thing, so the owner is simply that user's ID.
The owner is used to scope and organize data by tenant. For example, per-seat Subscriptions that also include metered billing record usage against the owner ID rather than each user's ID. Subscriptions can be grouped by owner, but your application's RBAC still determines who can modify or cancel Subscriptions, and your business logic still determines who is financially responsible.
You must provide an owner when creating carts and Subscriptions, though you can update it later. This is useful when converting an anonymous session into an authenticated tenant after signup, for example, swapping a session ID for the new organization's ID. A single tenant can own multiple Subscriptions and grantee groups, and you can filter entitlement checks by owner to scope results to one tenant.
### Grantee
A grantee is any entity that receives access to features or entitlements through a Subscription. Grantees are identified by unique IDs from your system (a granteeId string you provide). These can represent users, teams, projects, boards, workspaces, or any other entity in your application that needs feature access.
Grantees can belong to one or more grantee groups, and they receive entitlements through these group memberships. You can optionally provide a name for display purposes, making it easier to manage your grantees in the dashboard. For example, you might have a user with ID `user_abc123` or a project with ID `project_xyz789`. Both would be grantees in Salable.
### Group
A group is a collection of grantees that share access to features from a Subscription. Groups are how you implement team or organization Subscriptions in Salable. Each group has an owner (usually an organization or team lead) and contains zero or more grantees.
When you create a Subscription, you assign groups to Subscription items to grant access to all the grantees in that group. You can create groups before checkout (useful for pre-onboarding teams) or during the checkout process. A single owner can have multiple groups, which is perfect for organizations with different departments or teams.
Once you've created a group for a team or organization, you can reuse it for future add-ons and additional Subscriptions. You don't need to recreate the group structure. Assign the existing group to new Plans as the team purchases more features.
A single grantee can belong to multiple groups and gains cumulative access from all their memberships. This handles scenarios like contractors working with multiple clients or employees on cross-functional teams.
### Seat
A seat is a license or slot for a grantee within a per-seat Line Item. The seat count is the quantity on your per-seat Line Item and must be greater than or equal to the number of grantees in the assigned group.
You can define minimum and maximum limits for seats on your Line Item to control how teams scale. Seat count and grantee count are managed independently, so you can have headroom to allow for growth without an immediate upgrade.
If a grantee belongs to multiple groups with different Plans that have per-seat pricing, the lowest seat limit across all those Plans applies. This prevents under-provisioning of seats and ensures consistent access.
### Entitlement
An entitlement is a named permission or feature that you can check to control access in your application. Entitlements are named using lowercase snake_case (like `advanced_analytics` or `export_pdf`) and are attached to Plans rather than individual Line Items.
When a Subscription is created with a Plan, the entitlements from that Plan are inherited by the Subscription. To check access, you use the grantee ID. The chain of relationships: a grantee is in a group, the group is assigned to a Subscription, the Subscription contains a Plan with an entitlement, and so the grantee has access to that entitlement.
## Tier Tags and Tier Sets
Tier tags are string "tags" on Plans that **restrict Owners from purchasing multiple Plans that share the same tag**. Plans sharing a tier tag belong to the same **tier set**, and an **Owner can only subscribe to one Plan in a tier set at a time**. Tier tags and tier sets make your Plans **mutually exclusive purchases**.
What tier tags and tier sets prevent:
- Adding multiple Plans of one tier set to the same cart
- Adding Plans to a cart that belong to the same tier set as a Plan the cart's owner already subscribes to
Note that **owners can still replace a Plan in a Subscription with another Plan belonging to the same Tier Set as they will still only be subscribed to one member of the tier set**.
You can add tier tags to your Plan upon Plan creation or when editing an existing Plan.
## Transaction Concepts
### Cart
A cart is a temporary container for Plans that an owner intends to purchase. It converts into a Subscription after successful checkout. When you add the first item to a cart, you specify a billing interval. You can also optionally provide a currency when creating the cart.
If you provide a currency, Salable cherry-picks Line Items from added Plans that match both the interval and currency combination. If you don't provide a currency, Salable only matches by interval and uses Stripe's geolocation to detect the appropriate currency at checkout.
You can assign grantee groups to cart items before checkout, which is useful for pre-configuring team access. The owner can be updated later, for example converting a session ID to a user ID after signup. Salable supports multiple active carts per owner, so customers can have different purchasing sessions going simultaneously.
### Cart Item
A cart item represents a single Plan within a cart. Each cart item references a specific Plan and includes a quantity (which matters for per-seat Line Items). It can optionally have a grantee group ID assigned to it.
### Checkout
Checkout converts your cart into a paid Subscription. When you're ready to complete a purchase, you generate a Stripe Checkout session URL that redirects your customer to Stripe's hosted payment page.
You can configure default checkout settings at the Product level, like success and cancel URLs. If you've set these defaults in your Product settings, Salable uses them automatically. If you haven't provided Product defaults, include the required configuration parameters when generating the checkout link.
Once payment succeeds, Salable creates the Subscription and any necessary grantee groups. All grantees in the assigned groups immediately gain access to the entitlements attached to their Plans. Checkout works for both authenticated users and anonymous sessions, which is useful for guest checkout flows where you assign the owner ID after the customer signs up.
### Subscription
A Subscription is an active, recurring billing relationship between an owner and one or more Plans. Created after successful checkout, each Subscription contains one or more Subscription items (which are Plans) and renews based on the billing interval.
You can modify Subscriptions after creation by adding or removing Plans, changing quantities, or updating other settings. To end a Subscription, cancel it immediately (with proration handling for any unused time) or schedule the cancellation for the end of the current billing period.
### Subscription Item
A Subscription item represents a single Plan within a Subscription. Each item links to the Plan and its Line Items, includes quantities for per-seat pricing, and may have a grantee group assigned to it. When a group is assigned, all grantees in that group receive the entitlements from the Plan. You can individually modify or remove Subscription items without affecting the entire Subscription.
## Usage Tracking
### Metered Line Item
Metered Line Items let you charge customers based on what they actually use. Throughout the billing period, you record usage via API calls using a unique slug identifier. At the end of each period, Salable automatically calculates and bills the charges.
You can use the same slug across multiple Plans, keeping your code simple. When recording usage, you increment against the slug name, like "photo_generation", regardless of which Plan the user is on. Salable figures out their Plan and bills at the appropriate rate. One Plan might charge $0.10 per photo while another charges $0.05 per photo, but you increment the same slug.
When customers change Plans, any outstanding metered usage is invoiced immediately, and new counters start fresh at zero. Plans can have multiple metered Line Items, so you could track photos, videos, and API calls all within the same Plan. Metered Line Items support per-unit, tiered, and volume pricing schemes.
### Meter Slug
A meter slug is the unique identifier used to track usage for metered Line Items. It follows lowercase snake_case format, like `api_calls` or `storage_gb`. When you record usage via the API, you reference this meter slug to increment the counter.
You can use the same meter slug across multiple Plans. This keeps one usage counter per owner per meter slug, preventing double-billing. If you have "photo generation" on both Basic and Pro Plans, use the same meter slug (`photo_generations`) on both. Your code increments one counter, but billing happens at different rates depending on which Plan the customer has.
### Usage Record
A usage record tracks metered usage for an owner during a billing period. When you record usage for the first time in a period, Salable creates a usage record. Throughout the period, this record tracks cumulative usage as you continue to increment the counter.
Usage records move through states during their lifecycle. While usage is tracked during the billing period, the record has current status. Once the next interval starts, the previous record transitions to recorded status. When the Subscription ends, the current record becomes final and the accumulated usage is billed. There's one usage record per Owner per Meter slug per period, preventing confusion about what's been billed.
## Billing Concepts
### Proration
Proration handles financial adjustments when Subscriptions change mid-cycle. There are three approaches:
**Charge on Next Invoice** is the most common approach. It refunds unused time from the old Plan and starts billing for the new Plan at the next cycle, with minimal immediate financial impact.
**Charge Immediately** refunds unused time from the old Plan and bills for the new Plan right away, creating an instant invoice. Use this when you want to settle everything at once rather than waiting for the next billing cycle.
**No Refund, Charge Next** switches to the new Plan immediately but doesn't refund unused time. The new Plan starts billing at the next cycle. The customer keeps the remaining time on their old Plan while moving to the new one.
### Billing Cycle
The billing cycle is the time period between recurring charges for a Subscription. Its length is determined by the Plan's interval: monthly, yearly, or whatever interval you've configured. The cycle starts on the Subscription creation date (called the billing anchor), and usage for metered items resets at the start of each cycle. All Plans within a Subscription share the same billing cycle.
### Invoice
An invoice is the document showing all charges for a billing period. Salable generates invoices at the end of each cycle, including flat fees, per-seat charges, and metered usage in one place. You can preview upcoming invoices before the period ends. Once generated, invoices are downloadable as PDFs. Paid invoices are immutable; they can't be changed after payment.
### Billing Anchor
The billing anchor is the date a Subscription was created, and it determines all future billing dates. Salable uses the billing anchor to set the recurring charge date and calculate proration when Subscriptions change. The billing anchor stays consistent across Plan changes. If you create a Subscription on January 15th, it will bill on the 15th of each month going forward.
## Operational Concepts
### Cancellation
Cancellation terminates a Subscription, and you have two options for how this happens.
**Immediate cancellation** ends the Subscription right away. Metered usage is finalized and billed, access is revoked, and a final invoice may be generated.
**End of period cancellation** marks the Subscription for cancellation but lets it continue until the current billing period ends. You can reverse this before the period ends. The customer keeps access until the period they paid for expires.
### Sync to Latest Price
When you update your pricing, you might want to move existing Subscriptions to the new Prices. Syncing to the latest Price does this. You can grandfather existing customers by not syncing them (they stay on old pricing) or migrate them to the new pricing by syncing. When you sync, proration rules apply during the transition to handle mid-cycle adjustments.
### Webhook
Webhooks are HTTP callbacks that Salable sends to your application when events occur. They notify your app of Subscription changes, usage updates, and payment events. Each webhook includes the event type and full payload, and you must verify the signature using HMAC to ensure authenticity.
Salable will retry failed webhooks up to 10 times with exponential backoff, and each attempt has a 15-second timeout. Common events include Subscription created, updated, and cancelled; usage recorded and finalized; receipt created; and owner updates.
For a complete guide to configuring webhook destinations, implementing handlers, and monitoring deliveries, see the [Webhooks guide](/docs/webhooks).
## Special Patterns
### Anonymous to Authenticated Conversion
This pattern lets you start a cart with a session ID and update it to a user ID after signup. You create a cart with an owner like `"session_abc123"`, let the user add Plans to their cart, and redirect them to checkout. After payment completes, the user signs up, and you update the owner to their actual user ID like `"user_xyz789"`. This enables guest checkout flows that convert to authenticated accounts.
### Pre-Purchase Team Setup
You can add grantees to a group before completing checkout. This lets you invite team members before subscribing, show who will get access, and validate that seat counts match team size. Create a grantee group, add grantees to it, create a cart item with the group assigned, set the quantity to match the group size (or higher for growth room), and proceed to checkout. Once payment succeeds, everyone in the group immediately has access.
### Cross-Plan Metering
This pattern uses the same meter slug across multiple Plans to maintain a single usage counter. Your Basic Plan might charge $0.10 per photo generation using the slug `photo_generations`, while your Pro Plan charges $0.05 per photo generation with the same slug. You increment `photo_generations` once in your code, but Salable bills at the rate of whichever Plan the customer has. Simple implementation, flexible pricing across tiers.
## Quick Reference
### Hierarchy Overview
```
Organization
└─ Product
└─ Plan (specific interval + currency)
└─ Line Item (flat/seat/metered/one-time)
└─ Price (amount in currency)
```
### Access Flow
```
Subscription Item → Assigned Grantee Group → Contains Grantees
→ Plan → Entitlements → Grantees Have Access
```
### Checkout Flow
```
Cart → Cart Items (Plans + Groups) → Checkout → Payment
→ Subscription Created → Grantee Groups Assigned → Access Granted
```
### Billing Cycle
```
Start Date → Usage Recording → End of Period → Finalize Usage
→ Generate Invoice → Process Payment → New Period Begins
```
## Next Steps
Now that you understand the core concepts, explore these guides to implement specific patterns:
- **[Getting Started Guide](/docs/getting-started-guide)** Build your first Product and Plan
- **[Understanding Entitlements](/docs/understanding-entitlements)** Implement feature gating
- **[Grantee Groups](/docs/grantee-groups)** Set up team Subscriptions
- **[Webhooks](/docs/webhooks)** Configure real-time event notifications
- **[Caching Strategies](/docs/caching-strategies-for-entitlements)** Optimize entitlement checks in production
---
### Products & Pricing
Source: https://salable.app/docs/products-and-pricing
# Products & Pricing
Design products with any combination of flat-rate, per-seat, metered, and tiered pricing across multiple currencies. This guide covers products, plans, and line items so you can set up and ship.
## Overview
**[Products](/docs/core-concepts#product)** are the top-level containers for your pricing in Salable. A Product represents what you sell: a SaaS platform, a feature set, or an add-on service. Products contain **[Plans](/docs/core-concepts#plan)** that define different pricing Tiers or options, and Plans contain **[Line Items](/docs/core-concepts#line-item)** that determine what customers pay and how they're charged.
This hierarchy supports flat-rate subscriptions, per-seat pricing with volume discounts, usage-based metered billing, and combinations of all three. You can offer the same Product at different prices in different currencies, with different billing intervals, and with different feature access through **[Entitlements](/docs/core-concepts#entitlement)**.
## Understanding the Hierarchy
1. **Product** is the top-level container. It represents what you sell and holds one or more Plans. A Product might be your core SaaS platform, an add-on service, or a feature bundle. Products carry settings that apply to all Plans within them: checkout URLs, tax collection preferences, and trial period configurations.
2. **Plan** is what customers purchase. Plans are added to the Cart and checked out as a single unit. Customers pay for all Line Items within a Plan together. Plans define which Entitlements customers receive (controlling feature access) and contain one or more Line Items that determine the charges.
3. **Line Item** defines a specific charge within a Plan. These are the individual prices that appear on invoices. Line Items are not sold individually; they're bundled together within a Plan. A Plan might have multiple Line Items: a base subscription fee, a per-seat charge, and usage-based billing for API calls. Each Line Item has a type (flat rate, per-seat, or metered), an interval (one-time or recurring), and a billing scheme (per-unit or tiered).
4. **Price** represents the Line Item's cost at a specific billing interval. A single Line Item can have multiple Prices. One for monthly billing, one for yearly billing, and so on. Each Price contains Currencies for different markets.
5. **Currency** defines the actual pricing in a specific currency. A Price can have multiple Currencies (USD, GBP, EUR), with one marked as the default. This enables global pricing without creating duplicate Line Items.
6. **Tier** (optional) defines pricing breakpoints for tiered billing schemes. Tiers let you charge different amounts based on quantity or usage levels, enabling volume discounts or graduated pricing structures.
## Product Configuration
### Creating a Product
Products can be created on the Salable dashboard and the Salable API.
> **Note** Before creating Products, set up a Payment Integration to create a Stripe Connect account. Products and Plans can be created with minimal Stripe Connect setup. Test mode checkout links require the business type and personal details forms in Stripe Connect's onboarding. Live Mode checkout links require full onboarding with **Active** status.
Navigate to **Products** in your sidebar. Enter a name for your Product in the Product Name field and click Create Product. Your new Product appears in the list below. Click the Edit Product button (pencil icon) to continue setup.
### Product Settings
Product settings define defaults that apply to all Plans within the Product.
- **Checkout URLs**: Specifies where customers are redirected after checkout. The `successUrl` is where they go after successful payment; the `cancelUrl` is where they return if they abandon checkout. These URLs can include query parameters to pass information back to your application for onboarding flows or analytics tracking.
- **Allow Promo Codes**: The **allowPromoCodes** setting enables customers to enter discount codes during checkout. When enabled, a promo code field appears on the Stripe checkout page where customers can apply codes you've configured in your Stripe dashboard.
- **Automatic Tax**: The **automaticTax** setting enables [Stripe Tax](https://stripe.com/tax) for automatic tax calculation based on customer location. When enabled, Stripe determines the correct tax rate and applies it to invoices. This requires collecting the customer's billing address to determine their tax jurisdiction.
- **Address Collection**: The **collectBillingAddress** setting controls whether billing address fields appear at checkout. This is required for automatic tax calculation, since Stripe needs the customer's location for tax rates. The **collectShippingAddress** setting adds shipping address fields for physical products that require delivery.
- **Card Pre-fill Preference**: The **cardPrefillPreference** setting controls how payment methods are saved and reused. Set it to `none` for an empty payment form, `choice` to let customers decide whether to save their card, or `always` to save payment methods for future purchases.
- **Past Due Entitlements**: The **pastDueEntitlements** setting controls feature access during payment failures. When set to true, customers keep access to Entitlements while the Subscription is in a past-due state, covering Stripe's automatic retry period. When set to false, access is revoked immediately on payment failure.
- **Trial Periods**: The **trialPeriodDays** setting is configured at the Plan level. See [Plan Properties](#plan-properties) for details.
> **Note** Product settings can be overridden at checkout time.
> **Important**: Automatic tax requires Stripe Tax to be enabled and configured in your Stripe Dashboard before you use this setting in Salable.
## Plans
Plans represent different pricing tiers, add-ons, or options within a Product. They can be Subscription tiers like Basic, Pro, and Enterprise, or add-ons that customers purchase alongside a main Plan. Each Plan has its own pricing, features, and Line Item configurations.
### Creating Plans
Plans are created within the Product editor in the dashboard. Navigate to the Edit Product page for your Product, scroll to the Plans section, and click **Create Plan**. Enter a name for your Plan in the Plan Name field, optionally set a Trial Period in days, and select any Entitlements that customers on this Plan should receive.
The Plan is saved only after you've configured at least one Line Item with pricing.
### Plan Properties
- **Name** identifies the Plan to customers. Use clear, descriptive names like "Professional Plan" or "Enterprise" rather than internal codes. This name appears in checkout flows, invoices, and customer-facing areas.
- **Trial period** gives customers free access for a specified number of days before billing starts. Trial periods must be between 1 and 730 days. During the trial, customers have full access to Entitlements. If they cancel during the trial, they are not charged.
- **Entitlements** define which features customers on this Plan can access. Use the Entitlements typeahead input to search for an existing Entitlement or create a new one inline by typing the name and clicking Create. Subscribers receive all selected Entitlements, which you check in your application to gate features.
- **[Tier Tags](/docs/core-concepts#tier-tags-and-tier-sets)** make Plans mutually exclusive by grouping them into tier sets. When you assign the same tier tag to multiple Plans, an Owner can only subscribe to one of those Plans at a time. To configure tier tags, add them when creating a Plan or when editing an existing Plan. Enter your desired tier tag in the Tier Tag field.
## Line Items
Line Items define the charges within a Plan: what customers pay, how often, and how the amount is calculated.
### Naming
Line Item names appear on Stripe invoices, receipts, and checkout pages. Use clear, customer-facing descriptions: "Platform Subscription" rather than "base_fee", "Additional Users" rather than "per_user".
### Line Item Types
- **Flat Rate** charges a fixed amount per billing cycle, regardless of usage or team size. For example, a \$29/month subscription fee. flat rate Line Items always have a quantity of one.
- **Per Seat** charges based on the number of seats (users, licenses, or units). The price is multiplied by the quantity. For example, \$10 per user per month, where a team with five users pays \$50/month. Per-seat pricing can use simple per-unit billing or tiered pricing with volume discounts. Only one per-seat Line Item is allowed per Plan to avoid ambiguity about seat counting.
- **Metered** charges based on usage during the billing period. Customers are billed for what they consume: API calls, storage, or processing time. Usage is tracked throughout the billing cycle and invoiced at the end. Metered Line Items require a meter to track usage.
### Interval
Line Items have an interval that determines when they're charged.
- **Recurring** Line Items repeat every billing cycle. The charge appears on every invoice at the configured interval (day, week, month, or year). This covers Subscription fees, per-seat charges, and recurring metered billing. The interval count property lets you create custom billing periods. An interval of "week" with a count of two creates biweekly billing; "month" with a count of three creates quarterly billing.
- **One-off** Line Items charge only once, at the start of the Subscription. Use these for setup fees, onboarding charges, or one-time purchases.
### Billing Schemes
The billing scheme determines how the Line Item Price is calculated.
- **Per Unit** applies a fixed price per unit. If you set a unit amount of \$10 and the customer purchases five units, they pay \$50. Works for flat rate, per-seat, and metered Line Items.
- **Flat Rate** (as a billing scheme) charges a single fixed amount regardless of quantity. This is typically used when the price type is flat rate with a quantity of one, but can also apply to per-seat items where you want a flat fee regardless of seat count.
- **Tiered** applies different pricing based on quantity or usage levels. Tiers define breakpoints where pricing changes. For example, units 1–10 might cost \$10 each, units 11–50 cost \$8 each, and units 51+ cost \$5 each. Tiered billing supports both volume and graduated modes (explained in the next section).
### Quantity Controls
Line Items have quantity constraints that determine valid purchase amounts.
- **Minimum quantity** sets the fewest units customers must purchase. For flat rate items, this is typically zero or one. For per-seat items, you might set a minimum of two to enforce team pricing.
- **Maximum quantity** sets the most units customers can purchase. This enforces Plan limits and prevents over-purchase.
- **Default quantity** is the pre-filled amount customers see when they add the Plan to their Cart. For flat rate items, this is usually one. For per-seat items, you might default to a reasonable number (_eg_ five users) to give customers a starting point.
- **Allow changing quantities** controls whether customers can adjust the quantity at checkout or when managing their Subscription. Enable this for flexible per-seat pricing; disable it to lock quantities to your configured values.
## Tiered Pricing
Tiered pricing charges different amounts based on quantity or usage levels, giving you volume discounts and graduated pricing.
### Tier Modes
Tiered billing schemes have two modes that determine how prices are calculated across Tiers.
#### Graduated
**Graduated** pricing charges different rates for units within each Tier. This works like progressive income tax: the first 100 units cost \$10 each, the next 100 cost \$8 each, and so on. Each unit is priced according to its Tier.
**Example of Graduated Pricing:**
```
┌─────────┬───────────┬──────────┐
│ Tier │ Units │ Rate │
├─────────┼───────────┼──────────┤
│ Tier 1 │ 1–100 │ $10/unit │
│ Tier 2 │ 101–500 │ $8/unit │
│ Tier 3 │ 501+ │ $5/unit │
└─────────┴───────────┴──────────┘
```
Customer purchases 600 units:
```
┌────────────────────────────────┬─────────┐
│ First 100 units × $10 per unit │ $1,000 │
│ Next 400 units × $8 per unit │ $3,200 │
│ Final 100 units × $5 per unit │ $500 │
├────────────────────────────────┼─────────┤
│ Total │ $4,700 │
└────────────────────────────────┴─────────┘
```
#### Volume
**Volume** pricing applies a single rate to all units based on the total quantity. When you cross into a new Tier, all units are priced at that Tier's rate.
**Example of Volume Pricing:**
```
┌─────────┬───────────┬──────────┐
│ Tier │ Units │ Rate │
├─────────┼───────────┼──────────┤
│ Tier 1 │ 1–100 │ $10/unit │
│ Tier 2 │ 101–500 │ $8/unit │
│ Tier 3 │ 501+ │ $5/unit │
└─────────┴───────────┴──────────┘
```
Customer purchases 150 units, all at the Tier 2 rate:
```
┌──────────────────────────┬─────────┐
│ 150 units × $8 per unit │ $1,200 │
└──────────────────────────┴─────────┘
```
Customer purchases 600 units, all at the Tier 3 rate:
```
┌──────────────────────────┬─────────┐
│ 600 units × $5 per unit │ $3,000 │
└──────────────────────────┴─────────┘
```
### Configuring Tiers
Each Tier has three components that define its pricing.
- **Up To** sets the upper limit of the Tier: a number representing the last unit in the Tier, or `inf` for the final Tier with no upper limit. A Tier with "up to 100" includes units 1-100. The next Tier starts at 101.
- **Unit Amount** is the Price per unit within this Tier. This amount applies to each unit (in graduated mode) or to all units if the total falls in this Tier (in volume mode).
- **Flat Amount** is an optional base fee charged when entering this Tier. This amount is added once if the customer's usage reaches this Tier. For example, you might charge a \$50 flat fee plus \$5 per unit for the top Tier.
### Tier Configuration Example
Navigate to your Line Item configuration and select **Tiered** as the billing scheme. Choose **Graduated** as the Tier mode. Then configure your Tiers:
_Example:_
```
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ Tier 1 │ │ Tier 2 │ │ Tier 3 │
├──────────────────────┤ ├──────────────────────┤ ├──────────────────────┤
│ Up To: 10 │ │ Up To: 50 │ │ Up To: ∞ │
│ Unit Amount: $10.00 │ │ Unit Amount: $8.00 │ │ Unit Amount: $5.00 │
│ Flat Amount: $0.00 │ │ Flat Amount: $0.00 │ │ Flat Amount: $0.00 │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
```
## Prices and Currencies
Prices define how much a Line Item costs at different billing intervals and in different currencies.
### Billing Intervals
A single Line Item can have multiple Prices for different billing intervals, so customers choose how often to be billed without you creating duplicate Line Items.
Create a Price for each interval you want to support: **Day**, **Week**, **Month**, or **Year**. For example, add a monthly Price at \$29/month and a yearly Price at \$290/year (offering a 17% discount).
### Multi-Currency Support
Each Price can have multiple Currencies, so you can sell globally without duplicating your Product structure.
- **Default currency** is set using the default button on each Currency in the Price form. This is the currency Stripe uses when determining Prices based on geolocation if you omit currency in the Cart. All Line Items in a Product must share the same default currency for geolocation to work correctly.
- **Additional currencies** let you expand into new markets. Add Currencies for each market you want to serve. Prices don't need to be simple conversions. You might charge \$29/month in USD, £24/month in GBP, and €27/month in EUR, adjusting for local market conditions and purchasing power.
- **Configuring currencies** in the dashboard is done in the Price configuration. After selecting an interval, click Add Currency and choose from the dropdown. Enter the unit amount and add as many currencies as you need to support.
For tiered pricing, configure Tiers separately for each currency. While Tier breakpoints (the "up to" values) are typically the same across currencies, you might adjust unit amounts and flat amounts for different markets.
### Example Price Configuration
_A per-seat Line Item with monthly and yearly billing in multiple currencies:_
```
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ Line Item │ │ Monthly Price │ │ Yearly Price │
├──────────────────────┤ ├──────────────────────┤ ├──────────────────────┤
│ Name: User Seats │ │ Interval: Month │ │ Interval: Year │
│ Type: Per Seat │ ├──────────────────────┤ ├──────────────────────┤
│ Scheme: Per Unit │ │ USD: $10.00 │ │ USD: $100.00 │
│ Min Qty: 1 │ │ GBP: $8.00 │ │ GBP: $80.00 │
│ Max Qty: 100 │ │ EUR: $9.00 │ │ EUR: $90.00 │
│ Default: 5 │ │ │ │ │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
```
## Combining Multiple Line Items
Plans can include multiple Line Items that combine into varied pricing models.
### Common Combinations
- **Base fee + Per-seat** charges a fixed platform fee plus a per-user charge. For example, \$50/month base fee plus \$10/user/month. This covers your fixed costs while scaling revenue with team size.
- **Flat rate + Metered** pairs predictable recurring revenue with usage-based charges. For example, \$99/month base Subscription plus \$0.01 per API call. Customers get a base service level and pay for additional consumption.
- **Per-seat + Metered** charges for both team size and usage. For example, \$20/user/month plus \$0.50 per transaction processed. This fits when costs scale with both dimensions.
- **Multiple metered items** track different types of usage separately. For example, you might charge \$0.02 per image processed, \$0.01 per API call, and \$0.10 per GB of storage used. Each has its own meter and pricing.
- **One-time setup + Recurring** charges customers once for onboarding or setup, then bills recurring fees. For example, a \$500 setup fee (one-off) plus \$199/month (recurring). The setup fee appears only on the first invoice.
## Troubleshooting
### Cannot Add Per-Seat Line Item
If you get an error adding a per-seat Line Item, check whether the Plan already has one. For different per-seat pricing, use tiered pricing within the single per-seat Line Item rather than creating multiple items.
### Tiered Pricing Validation Errors
Tiers must be configured in ascending order without gaps. Each Tier's starting point is automatically calculated from the previous Tier's "up to" value plus one. The final Tier must have "up to: inf" to handle all quantities beyond the previous Tier.
Unit amounts cannot be negative. To offer discounts at higher Tiers, reduce the unit amount compared to lower Tiers.
### Currency Amount Format
Salable accepts Prices with or without decimals. You can enter 29 or 29.00 for \$29.00; both are valid.
For zero-decimal currencies (like JPY, KRW), you must enter whole numbers without decimal places. For example, 1000 JPY must be entered as 1000. Entering 1000.00 will cause an error.
### Default Currency Mismatch
If you use Cart geolocation (omitting currency when creating Carts), all Line Items across all Plans in your Product must share the same default currency. Check each Line Item's default Currency. If Plan A defaults to USD and Plan B defaults to GBP, either standardise the defaults or require explicit currency selection in Carts.
## Summary
Use flat rate Line Items for fixed charges, per-seat for team-based pricing, and metered for usage-based billing. Add multi-currency pricing for global markets and tiered pricing with graduated or volume modes for quantity discounts.
For more on how the Entitlements control feature access, see the [Understanding Entitlements guide](/docs/understanding-entitlements). For managing team access and seats, see [Grantees & Groups](/docs/grantee-groups). For checkout flows and Cart management, see [Cart & Checkout](/docs/cart-and-checkout).
---
### Understanding Entitlements
Source: https://salable.app/docs/understanding-entitlements
# Understanding Entitlements
Every SaaS app must keep feature access synchronised with billing. When a customer subscribes, they need immediate access. When they upgrade, new features should unlock instantly. When a payment fails or a Subscription ends, access must be revoked.
**[Entitlements](/docs/core-concepts#entitlement)** solve this. An Entitlement is a string identifier (`analytics`, `sso`) that grants access to a feature in your application. You define Entitlements for each feature you want to gate, attach them to **[Plans](/docs/core-concepts#plan)**, and check whether a user has them before granting access.
You focus on building features and defining which Plans include them. Salable tracks Subscription status, manages grace periods, and keeps access synchronised with billing.
## How Entitlements Work
**[Subscriptions](/docs/core-concepts#subscription)** have a **[Group](/docs/core-concepts#group)** associated with them. All **[Grantees](/docs/core-concepts#grantee)** in a Group receive the Entitlements from that Subscription's Plan. A Grantee can belong to multiple Groups and receive Entitlements from each.
> **Note** Only Subscriptions to Plans with a per-seat Line Item have Groups attached.
Example:
```
Plan "Pro"
├─ Entitlements: ['analytics', 'api_access', 'export_csv']
└─ Subscription (active)
└─ Group "Acme Corp"
└─ Grantee "user_123"
└─ Has: analytics, api_access, export_csv
```
### Lifecycle in Action
Salable automatically adjusts access when Subscriptions change:
**Subscription created or renewed:** Entitlements are immediately available. When a Pro Subscription is created, `analytics` and `api_access` are granted instantly.
**Subscription upgraded:** New Entitlements are added. When a Subscription upgrades from Pro to Enterprise, `sso` and `priority_support` are granted immediately.
**Subscription downgrade:** Entitlements are removed. When a Subscription downgrades from Enterprise to Pro, access to `sso` and `priority_support` is revoked.
**Payment fails (past_due):** You can control whether access continues. If "Return Entitlements While Past Due" is enabled on the Product, access continues while Stripe attempts to recover payment. If disabled, access is revoked immediately.
**Subscription cancelled:** Entitlements are revoked. When the Subscription ends, access stops. No manual intervention required.
## Attaching Entitlements to Plans
You can create and manage Entitlements while creating or managing your Plans. In the Product management view, each Plan has a Select Entitlements field that lets you search existing Entitlements or create new ones. Type a name to filter, or enter a new name to create it. Once created, an Entitlement can be reused across any of your Plans.
### Naming Your Entitlements
Entitlement names must use lowercase letters with underscores (snake_case). No spaces, hyphens, or special characters.
**Valid:** `api_access`, `advanced_features`, `priority_support`
**Invalid:** `API_Access` (uppercase), `api-access` (hyphens), `api access` (spaces), `api_access_` (trailing underscore)
There are two common conventions for selecting Entitlement names:
**Feature-based naming** ties Entitlements to specific capabilities: `api_access`, `export_data`, `custom_reports`. This gives you granular control. You can mix and match Entitlements across Plans, create bespoke Subscriptions for specific customers, and move features between tiers as your pricing evolves.
**Tier-based naming** bundles features by plan level: `basic_features`, `pro_features`, `enterprise_features`. This is convenient at first, but can limit you if you later need to sell individual features separately or create custom arrangements for enterprise customers.
You can combine both approaches based on your needs.
## Checking Entitlements
If a Grantee has access to multiple Subscriptions (a base plan plus an analytics add-on, for example), they receive Entitlements from all of them. Entitlements are returned from Subscriptions that are `active`, `trialing`, or optionally `past_due` if you've enabled "Return Entitlements While Past Due" on the Product.
### Via the Dashboard
To verify a user's access, navigate to Entitlement Check in the dashboard. Enter a Grantee ID and click Check Grantee to see their current Entitlements.
### Via the API
**Endpoint:** `GET /api/entitlements/check`
**Query Parameters:**
- `granteeId` (required): The Grantee to check
**Example Request:**
```bash
GET /api/entitlements/check?granteeId=user_alice
```
```javascript
import { Salable } from '@salable/sdk';
const salable = new Salable('your-secret-key');
const { data } = await salable.api.entitlements.check.get({
queryParameters: {
granteeId: 'user_alice'
}
});
```
```javascript
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=user_alice', {
headers: {
Authorization: `Bearer ${process.env.SALABLE_SECRET_KEY}`
}
});
if (!response.ok) {
throw new Error(`Failed to check entitlements: ${response.status}`);
}
const { data } = await response.json();
```
**Example Response:**
```json
{
"type": "object",
"data": {
"entitlements": [
{ "type": "entitlement", "value": "api_access", "expiryDate": "2026-01-15T10:00:00Z" },
{ "type": "entitlement", "value": "advanced_analytics", "expiryDate": "2026-01-15T10:00:00Z" }
],
"signature": "a3f5b8c2d9e1..."
}
}
```
The `value` is the Entitlement name. The `expiryDate` indicates when the current billing period ends; if an Entitlement is returned, it's active, and your application should grant access. If the expiry date is in the past, the Subscription is in a grace period. When a Grantee has multiple Subscriptions providing the same Entitlement, Salable returns the expiry date furthest in the future. Use the `signature` to verify that the response hasn't been tampered with.
For perpetual subscriptions (one-off purchases with no recurring billing), `expiryDate` will be `null`, indicating the entitlement never expires.
### Subscription Status Reference
| Status | Returned? | Notes |
| -------------------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
| `active` | Yes | Normal active Subscription |
| `trialing` | Yes | During trial period |
| `past_due` | Conditional | Only if **[Product](/docs/core-concepts#product)** setting "Return Entitlements While Past Due" is enabled |
| `canceled` | No | Subscription has ended |
| `incomplete` | No | Payment not completed |
| `incomplete_expired` | No | Payment attempt expired |
| `unpaid` | No | Failed to collect payment |
## Implementing Access Control
Authorise your API endpoints by returning a 403 when the required Entitlement is missing:
```javascript
app.get('/api/advanced-analytics', async (req, res) => {
const { entitlements } = await getEntitlements(req.user.id);
const hasAccess = entitlements.some(ent => ent.value === 'advanced_analytics');
if (!hasAccess) {
return res.status(403).json({ error: 'This feature requires a Pro Subscription' });
}
res.json({ data: getAdvancedAnalytics() });
});
```
On your frontend, use Entitlements to control what users see. Hide unavailable features or show upgrade prompts:
```javascript
function AdvancedAnalytics({ entitlements }) {
const hasAccess = entitlements.some(ent => ent.value === 'advanced_analytics');
if (!hasAccess) {
return