# Salable > Salable handles the entire billing and subscription lifecycle so you can focus on building your product. > > You get the flexibility to experiment with pricing — flat-rate subscriptions, per-seat licensing, usage-based metering, or hybrid models — without rewriting your billing logic each time. Powered by Stripe, Salable manages products, plans, line items, checkout, subscriptions, invoicing, and entitlements (feature access control) through a single integration. > > Salable takes care of the Stripe webhook complexity you'd otherwise build yourself — subscription lifecycle events, payment failures, invoice updates, plan changes — so your engineering time goes toward features, not billing plumbing. > > Everything runs through infrastructure designed for real SaaS businesses: a dashboard for managing your billing configuration, a REST API and webhooks for deep integration, a shopping cart for multi-plan checkout flows, and an MCP server that lets AI agents and tools like Claude Code create and manage your billing setup directly. ## Docs ### Salable Quick Start Guide Source: https://salable.app/docs/quick-start # Salable Quick Start Guide ## Step 1: Enable Test Mode Toggle **[Test Mode](/docs/core-concepts#test-mode-vs-live-mode)** on using the switch at the bottom left. Everything we create in this guide will be test data. ## Step 2: Create and Set Up a Payment Integration **Payment Integrations** connect Salable to Stripe so you can accept payments for your Products. Click the Payment Integrations tab in your sidebar and click the Create Payment Integration button. Stripe's Connect onboarding flow opens. After completing onboarding, Stripe redirects you back to Salable. > **Note** For this guide, you will only have to complete the Business Type form and the Personal Details section. To take live payments, you would also need to enter your banking details and verify your identity as instructed throughout the onboarding process. ## Step 3: Create a Product A **[Product](/docs/core-concepts#product)** on Salable contains your pricing model, Plans, and features. Click on the Products tab on the sidebar to navigate to the Products page. Provide a name for your Product in the Product Name field (_eg_ "My SaaS Product") and click the Create Product button. ## Step 4: Create a Plan **[Plans](/docs/core-concepts#plan)** allow you to define a payment model and the Entitlements you want to make available for it. Click the Manage Product button (pencil icon) on your Product. Provide a name for your Plan (_eg_ "Basic Plan") on the Plan Name field and click the Create Plan button. ## Step 5: Create an Entitlement **[Entitlements](/docs/core-concepts#entitlement)** grant access to features (_eg_ `export_pdf`, `generate_images`) in your Product. When a user subscribes to a Plan, they receive these Entitlements. To create an Entitlement, locate the Select Entitlements form field. Enter `entitlement_one` for your Entitlement name and click the (+) button to create and add the Entitlement to your Plan. ## Step 6: Create a Line Item **[Line Items](/docs/core-concepts#line-item)** are the pricing components that make up your Plan's payment model. Each type has its own pricing structure. For this guide, we will create a **Flat Rate** Line Item that charges a fixed fee each billing cycle. Click the Add Line Item button to pull up the Line Item form. Provide a name for your Line Item in the Line Item Name field (_eg_ Monthly Subscription Fee). This is the name that will appear on Stripe invoices, so be sure to name your Line Items accordingly. We will leave the Interval Type set to Recurring and the Price Type set to Flat Rate. Next, we will set up a **[Price](/docs/core-concepts#price)**, set the Currency to USD, and set the Unit Amount to \$4.99. We will leave Interval set to Month and Interval Count set to one. Click the Save Plan button to provide your Plan with your new Line Item. ## Step 7: Generate a Checkout Link We have a Payment Integration, a Product, a Plan with an Entitlement and a Line Item. Now, let's purchase a **[Subscription](/docs/core-concepts#subscription)**. Scroll below the Plan form to find the form that adds your Plan to your **[Cart](/docs/core-concepts#cart)**. Select USD for the currency, month for the interval, and set the interval count to 1. You will see two fields: Owner and Grantee. The **[Owner](/docs/core-concepts#owner)** is an ID in your system for looking up and managing the Subscription. The **[Grantee](/docs/core-concepts#grantee)** is the entity that receives access to features in your application, typically a user ID, but it could also be an organisation, team, or any other entity ID ([Read more about Owners and Grantees here](/docs/core-concepts#access-control)). Enter `owner_one_id` for the Owner field and click the (+) button, then enter `grantee_one_id` for the Grantee field. Click the Add to Cart button and click Go to Cart to navigate to the Manage Cart page. Review the contents of your Cart. When ready, click the Checkout Cart button to begin **[Checkout](/docs/core-concepts#checkout)**. ## Step 8: Complete Test Checkout On Stripe’s checkout page, enter the following test payment details: - Email Address `someone@example.com` - Card number: `4242 4242 4242 4242` - Expiry: Any future date - CVC: Any 3 digits - Postal code/Zip code: Any code > **Note** The actual fields may vary depending on your region Click Subscribe to complete the checkout. ## Step 9: Verify Your Subscription Salable redirects you to the Subscriptions page. You should see your newly created Subscription with the Active status. If you click on the Subscription to view details, you will see: - The Owner - The associated Plan - A list of **[Invoices](/docs/core-concepts#invoice)**, including a draft of the next Invoice ## Step 10: Check Entitlement Access Perform an Entitlement Check with your Subscription and Grantee to confirm they have access to the Entitlement. - Navigate to the Entitlement Check tab - In the Grantee ID field, enter `grantee_one_id` - Click Check Grantee You should see the following response: ```json { "type": "object", "data": { "entitlements": [ { "value": "entitlement_one", "type": "entitlement", "expiryDate": "2026-01-02T17:28:41.000Z" } ], "signature": "3045022054188fb22b12a9e8565beda67a9859a7e3eb23e31f806a1dccf7b551267e46b9022100b29021c7b579e36a63d6b1b6c1e2be55c64a285a15223119ab1b17f88410047b" } } ``` Your Grantee has access to the Entitlement you created earlier. ## Conclusion In this guide, you: - Created and onboarded a Payment Integration - Created a Product - Created a Plan - Created an Entitlement - Created a Line Item with a price, currency, and billing interval - Purchased a Subscription - Performed an Entitlement Check on a Grantee You are now familiar with the core concepts of Salable. For more detail, [review core concepts](/docs/core-concepts). --- ### Flat-Rate Billing Quick Start Source: https://salable.app/docs/flat-rate-quick-start # Flat-Rate Billing Quick Start ## Introduction Flat-rate billing means charging a fixed amount per billing cycle. **£99 per/year**, **£14 per/14 days** and **£20 per/month** are all examples of this billing model. Following this short guide will take you from zero to being up-and-running with a flat-rate billing model in your application. ## 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 music streaming service. You may offer “Basic” and “Pro” plans, with `ad_free_listening`, `curated_playlists`, and `unlimited_playlists` as your entitlements. To charge money for your "Pro" plan, set up a Line Item with a Pricing Type of "Flat Rate". ## 2. Generate a 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 ``` ## 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. } ``` ## 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 Subscription](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/cancel) - [Subscription management portal](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/portal) --- ### Per-Seat Billing Quick Start Source: https://salable.app/docs/per-seat-quick-start # Per-Seat Billing Quick Start ## 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. Following this short guide will take you from zero to being up-and-running with a per-seat billing model in your application. ## 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 checkout link When creating a checkout for per-seat billing, you can optionally specify a group (or let Salable create one automatically on purchase). The checkout will automatically calculate the quantity based on the number of group members. ```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', // if group id (starts with grp_) -> group is re-used // if any other grantee -> new group with grantee added as first member // if undefined -> new group created with no members 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' }); ``` ## 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. } ``` ## 4. Managing team members As your team grows, 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' } ]); ``` ## 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) - [Subscription management portal](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/portal) - [Learn more about Groups and seat management](/docs/grantee-groups) --- ### Usage-Based Billing Quick Start Source: https://salable.app/docs/usage-quick-start # Usage-Based Billing Quick Start ## 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. Following this short guide will take you from zero to being up-and-running with a usage-based billing model in your application. ## 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 ``` 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 }); ``` ## 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. } ``` ## 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; ``` ## 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) - [Subscription management portal](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/portal) - [Learn more about Metered Usage and billing cycles](/docs/metered-usage) --- ### Hybrid Pricing Quick Start Source: https://salable.app/docs/hybrid-pricing-quick-start # Hybrid Pricing Quick Start ## 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. Following this short guide will take you from zero to being up-and-running with a hybrid pricing model that combines flat-rate, per-seat, and usage-based billing in your application. ## 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 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 component of your hybrid pricing, you'll need to create a group that represents your 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' } ] }); ``` ## 3. Generate a checkout link When creating a checkout for a hybrid pricing plan, specify the group and ensure the quantity matches the number of grantees. ```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 ``` 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 }); ``` ## 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. } ``` ## 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; ``` ## 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) - [Subscription management portal](/docs/openapi/scalar#tag/subscriptions/post/api/subscriptions/{id}/portal) - [Learn more about combining multiple line items](/docs/products-and-pricing#combining-multiple-line-items) - [Learn more about Groups and seat management](/docs/grantee-groups) - [Learn more about Metered Usage and billing cycles](/docs/metered-usage) --- ### Getting Started with Salable Source: https://salable.app/docs/getting-started-guide # Getting Started with Salable ## 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. --- ## Step 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. > **Important** This requires opting in to Stripe Tax in your Stripe Dashboard. Enable and configure Stripe Tax in your Stripe account settings before using this option. - **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. ### 1.6 Save Product Settings Click Save to persist your changes. --- ## Step 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. --- ## Step 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. --- ## Step 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). --- ## Step 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. --- ## Step 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. --- ## Step 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 ## 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 an identifier used to scope Subscriptions, carts, and usage records in Salable. Typically a user ID for individual Subscriptions, or a team or organization ID for shared Subscriptions where multiple people need access to the same Subscription data. The owner ID should be an identifier accessible to anyone who needs to view Subscription data or increment usage meters. In a team Subscription, all team members who might record usage would share the same owner ID (like a team or organization ID). Your application's RBAC determines who can modify or cancel Subscriptions, and your business logic determines who is financially responsible. The owner ID is the scoping mechanism for organizing Subscription data in Salable. You must provide an owner when creating carts and Subscriptions, though you can update it later. This is useful when converting anonymous sessions to authenticated user IDs after signup. A single owner can have multiple Subscriptions and grantee groups, and you can filter entitlement checks by owner to scope results. ### 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**: Build your first Product and Plan - **Understanding Entitlements**: Implement feature gating - **Grantee Groups**: Set up team Subscriptions - **Webhooks**: Configure real-time event notifications - **Caching Strategies**: Optimize entitlement checks in production --- ### Products & Pricing Source: https://salable.app/docs/products-and-pricing # Products & Pricing ## 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** 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** 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 ``` **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 ; } return ; } // Conditionally render based on entitlements { entitlements.some(ent => ent.value === 'export_data') && ; } ``` > **Important** Frontend checks improve user experience but can be bypassed. Always enforce access on your backend for sensitive features. ## Modifying Entitlements on Active Plans When you add or remove Entitlements from a Plan with active Subscriptions, changes take effect on the next Entitlement check. **Adding an Entitlement to a Plan:** All existing subscribers immediately gain access to the new feature. This is useful when you're launching a new capability and want to roll it out to current customers. **Removing an Entitlement from a Plan:** All existing subscribers immediately lose access. Make sure to communicate changes to your customers before removing Entitlements they're actively using. ## Advanced Topics ### Entitlement Signatures Every Entitlement check response includes a cryptographic signature that proves the response came from Salable. When passing Entitlement data from your backend to your frontend, you can verify the data hasn't been tampered with. ```javascript const crypto = require('crypto'); function verifyEntitlements(entitlements, signature, publicKey) { const data = JSON.stringify(entitlements); const verify = crypto.createVerify('sha256'); verify.write(data); verify.end(); return verify.verify(publicKey, signature, 'hex'); } ``` ### Filtering by Owner The `owner` query parameter scopes Entitlement checks to Subscriptions owned by a specific Owner. Use this when a Grantee belongs to multiple Groups with different Subscriptions, such as a user who works with several teams. ```bash # Check user's access within Team A GET /api/entitlements/check?granteeId=user_john&owner=team_a # Check user's access within Team B GET /api/entitlements/check?granteeId=user_john&owner=team_b ``` By including the `owner` parameter, the response also includes metered Line Item slugs, letting you check both feature access and usage permissions in a single call. ## Troubleshooting ### Entitlement Not Returned If an expected Entitlement doesn't appear: 1. Verify the Entitlement is attached to the Plan 2. Confirm the Grantee is in a Group with an active Subscription to that Plan 3. Check the Subscription status is `active` or `trialing` 4. Verify the Grantee is in the correct Group ### Entitlement Check Returns 404 A 404 indicates that the Grantee doesn't exist. Create the respective Grantee and add them to a Group with an active Subscription. --- ### Grantees & Groups Source: https://salable.app/docs/grantee-groups # Grantees & Groups ## Overview Subscription systems often assume one subscription equals one user. Real applications are more complex: teams share access, organisations manage multiple departments, and a single subscription often needs to grant access to many people. Building this yourself means tracking group memberships, managing seat limits, and keeping access in sync as teams grow. The Grantee and Group system handles all of this. You manage Subscriptions for entire teams, add users to Groups before they purchase, and allocate seats on the fly. A single Subscription can control access for an entire team. Grantees can represent any entity in your system: users, boards, workspaces, projects, or whatever fits your application. ## Terminology ### Owners An **[Owner](/docs/core-concepts#owner)** scopes Subscriptions, Carts, and metered usage in Salable. In your application, this typically maps to a user ID for individual Subscriptions, or a company, organisation, team, or project ID for shared Subscriptions where multiple people need access to the same Subscription data. Choose an Owner ID that anyone who needs to view Subscription data or increment usage meters can access. In a team Subscription, all team members who might record usage would share the same Owner ID (like a team or organisation ID). Your application's RBAC determines who can modify or cancel Subscriptions, and your business logic determines who is financially responsible. The Owner ID is the scoping mechanism for organising Subscription data in Salable. Each Owner can own multiple Subscriptions and Grantee Groups. You'll need to provide an Owner when creating Carts and Subscriptions. ### Grantees **[Grantees](/docs/core-concepts#grantee)** are the individual identities that receive access to your application's features. They can represent users, boards, workspaces, projects, API keys, service accounts, or any other entity in your system. Each Grantee is identified by a `granteeId` that you provide. You can optionally include a display name for easier management in the dashboard. Grantees can belong to multiple Groups, and their access is checked via the `/api/entitlements/check` endpoint. ### Groups **[Groups](/docs/core-concepts#group)** are collections of Grantees under a single Owner, linking Subscriptions to individual access. Each Group belongs to one Owner and can contain multiple Grantees (members). When you create Subscriptions, you attach Plans to Groups rather than individual Grantees. Groups can have an optional name for identification. One Owner can have multiple Groups, which works well for organisations with different departments or teams. ### Memberships Memberships are the join records that link Grantees to Groups. Each Membership links one Grantee to one Group, and they're managed automatically when you add or remove Grantees from Groups. ## The Relationship Model ``` Owner (pays for subscription) └─ Has many Groups └─ Has many Grantees (via Memberships) └─ Has many Subscription Plans (with seats) Subscription └─ Belongs to Owner └─ Has many Subscription Plans └─ Each plan is linked to a Group └─ Each plan has a seat count ``` ## How Access Works When you check if a Grantee has access to a feature, Salable looks up the Grantee by their `granteeId` and finds all Groups they belong to through their Memberships. It then checks all Subscription Plans attached to those Groups for the requested Entitlement. After validating that the Subscriptions are active, it returns the Entitlements with their expiry dates. ## Common Use Cases ### Individual User Subscriptions For single-user Subscriptions, the simplest approach is to use the same ID for both Owner and Grantee: ```bash # Create a Group with the user as both Owner and Grantee POST /api/groups { "owner": "user_123", "name": "User 123's Personal Workspace", "grantees": [ { "granteeId": "user_123", "name": "John Doe" } ] } ``` ### Team Subscriptions For team Subscriptions, create a Group with a team identifier as the Owner: ```bash # Create a Group for a team POST /api/groups { "owner": "team_acme", "name": "Acme Corp Development Team", "grantees": [ { "granteeId": "user_alice", "name": "Alice Smith" }, { "granteeId": "user_bob", "name": "Bob Johnson" } ] } ``` ### Multi-Team Management One Owner can manage multiple teams by creating separate Groups for each. This works well for larger organisations with different departments: ```bash # Engineering team POST /api/groups { "owner": "company_xyz", "name": "Engineering", "grantees": [...] } # Marketing team POST /api/groups { "owner": "company_xyz", "name": "Marketing", "grantees": [...] } ``` ## Creating Groups Groups can be created in two ways: explicitly through the dashboard or API, or automatically during Checkout if you haven't created one yet. Creating a Group beforehand lets you set up team members before purchase. You can create an empty Group and add members later, or include Grantees from the start. ### API: Create a Group **Endpoint:** `POST /api/groups` **Request Body:** ```json { "name": "Development Team", "owner": "company_acme", "grantees": [ { "granteeId": "user_alice", "name": "Alice Smith" }, { "granteeId": "user_bob", "name": "Bob Johnson" } ] } ``` The `grantees` array is optional. When you include Grantees, Salable creates Owners and Grantees if they don't exist and creates Membership records linking Grantees to the Group. If Grantees already exist elsewhere, Salable adds new Memberships without duplicating the Grantee records. **Response:** ```json { "type": "object", "data": { "id": "Group_01HXXX", "organisation": "org_xxx", "ownerId": "Owner_01HYYY", "name": "Development Team", "createdAt": "2024-01-15T10:00:00Z", "updatedAt": "2024-01-15T10:00:00Z" } } ``` ### Dashboard: Create a Group In the dashboard, navigate to **Groups** in the sidebar. The "Create a Group" form takes a Group name (optional) and an Owner ID (required). Click **Create Group** to create an empty Group. From there, add Grantees individually through the Group management interface. ## Managing Grantees ### Adding Grantees to Groups You can add Grantees to a Group at any time, whether before or after creating a Subscription. **API Endpoint:** `POST /api/groups/{groupId}/grantees` **Request Body:** ```json [ { "type": "add", "granteeId": "user_charlie", "name": "Charlie Brown" } ] ``` **Dashboard:** Navigate to **Groups** and click the **Manage Group** icon (eye icon) for the Group you want to modify. You'll see an "Add a grantee" form where you can enter a Grantee name (optional) and Grantee ID (required). Click **Add Grantee** and they'll be added to the Group through a new Membership record. > **Note** If the Group has per-seat Plans attached, you cannot exceed the allocated seat count. ### Removing Grantees from Groups Removing a Grantee from a Group removes their Membership, not the Grantee record itself. The Grantee still exists and can be added back to this Group or to other Groups later. **API Endpoint:** `POST /api/groups/{groupId}/grantees` **Request Body:** ```json [ { "type": "remove", "granteeId": "user_charlie" } ] ``` **Dashboard:** Navigate to **Groups**, click **Manage Group**, find the Grantee in the table, and click the **Sign Out** icon. This removes their Membership from the Group while preserving the Grantee record itself. ### Replacing Grantees You can swap one Grantee for another in a single atomic operation, for example when a team member leaves and someone new joins. **API Endpoint:** `POST /api/groups/{groupId}/grantees` **Request Body:** ```json [ { "type": "replace", "granteeId": "user_old", "newGranteeId": "user_new", "name": "New User Name" } ] ``` The old Grantee's Membership is removed and the new one is added simultaneously, keeping your seat count consistent. ### Batch Operations When you need to make multiple changes at once, batch them in a single API request. ```json [ { "type": "remove", "granteeId": "user_1" }, { "type": "remove", "granteeId": "user_2" }, { "type": "add", "granteeId": "user_3", "name": "User Three" }, { "type": "add", "granteeId": "user_4", "name": "User Four" } ] ``` Remove operations run first, then add operations. This frees up seats before filling them, so seat limits are respected. ## Seat Management When a Group has per-seat Subscription Plans attached, seat quantity limits how many Grantees can be in that Group. ### How Seats Work Per-seat **[Line Items](/docs/core-concepts#line-item)** define seat-based pricing. Each Subscription Plan has a quantity representing the number of seats. When you add Grantees to a Group, Salable validates the Group size against the seat count. You cannot add more Grantees than you have seats. If the Group has multiple Plans with per-seat pricing, the lowest seat count across all Plans becomes the limit. ### Example: Seat Constraints Imagine a Group called "Acme Dev Team" with 5 current members. The Group has Plan A with 10 seats and Plan B with 7 seats. The lowest count is 7, so that's the maximum Group size, leaving 2 available seats. ### Viewing Seat Information In the dashboard, navigate to **Groups**, click **Manage Group**, and look at the **Plans** section. The "Seats" column shows allocated seats for each Plan, and the seat count that's limiting your Group size is shown in bold. ### Increasing Seats To add more Grantees than your current seat allocation allows, increase the seat count first. In the **Plans** section, find the Plan and click the seats icon in the **Actions** column. Enter a new value in the **Seats** field and click **Update Seats**. The seat count updates via Stripe, and then you can add more Grantees. **API:** ```bash PUT /api/subscription-plan-line-items/{subscriptionPlanLineItemId} { "quantity": 15 } ``` ### Pre-Purchase Team Setup You can add Grantees to Groups before purchasing a Subscription. Create a Group with Grantees, add the Group to a Cart, and when checking out, ensure the quantity matches or exceeds the Group size. After Checkout, all Grantees have access immediately. This lets you onboard teams upfront, collect user information before payment, and simplify the post-purchase experience. ## Subscriptions and Groups ### Linking Subscriptions to Groups Subscriptions are linked to Groups through Subscription Plans. Each Subscription Plan within a Subscription is associated with a specific Group. When adding items to a Cart during Checkout, you can specify which Group should receive access: ```bash POST /api/cart-items { "cartId": "Cart_01HXXX", "planId": "Plan_01HXXX", "grantee": "grp_01HYYY" } ``` If you don't provide a `grantee`, Salable creates a new empty Group automatically. ### Multiple Plans per Group A single Group can have multiple Subscription Plans from different Subscriptions. For example, your "Engineering Team" Group might have Subscription A with a Pro Plan (10 seats) and Subscription B with an Analytics Add-on (5 seats). The same lowest-seat-count rule applies, so the Group would be limited to 5 members. ### Viewing Group Subscriptions In the dashboard, navigate to **Groups**, click **Manage Group**, and look at the **Subscriptions** section. Click the magnifying glass icon to filter Entitlements by Subscription, or click the eye icon to view full Subscription details. Via the API, `GET /api/groups/{groupId}` returns all Subscription Plans linked to the Group. ## Checking Access ### API: Check Entitlements To check if a Grantee has access to a feature: **Endpoint:** `GET /api/entitlements/check` **Query Parameters:** - `granteeId` (required): The Grantee to check - `owner` (optional): Filter to Subscriptions owned by this Owner **Example:** ```bash GET /api/entitlements/check?granteeId=user_alice&owner=acme_corp ``` **Response:** ```json { "type": "object", "data": { "entitlements": [ { "type": "entitlement", "value": "advanced_features", "expiryDate": "2024-02-15T10:00:00Z" }, { "type": "entitlement", "value": "priority_support", "expiryDate": "2024-02-15T10:00:00Z" }, { "type": "meter", "value": "api_calls", "expiryDate": "2024-02-15T10:00:00Z" } ], "signature": "hex_signature_string" } } ``` > **Note** Meter entries are only returned when the `owner` query parameter is provided. Without it, only Entitlement entries appear in the response. ### Subscription Status Handling Entitlements are returned for Subscriptions with these statuses: - `active`: Subscription is active and paid - `trialing`: Subscription is in trial period - `past_due`: Only if the Product setting "Return Entitlements While Past Due" is enabled ### Testing Access in Dashboard **Dashboard:** 1. Navigate to **Entitlements** → **Check** 2. Enter a **Grantee ID** 3. Optionally enter an **Owner ID** to filter results 4. Click **Check Entitlements** 5. View the results showing all accessible Entitlements ## Group Lifecycle Management ### Updating Groups **API:** `PUT /api/groups/{groupId}` Update the Group name and existing Grantee details. Returns `204 No Content` on success. **Request Body:** ```json { "id": "Group_01HXXX", "name": "Updated Group Name", "grantees": [ { "id": "grantee_01HYYY", "granteeId": "user_new_id", "name": "Updated Name" } ] } ``` The `id` must match the Group ID in the URL. The `grantees` array updates existing members only; you cannot add or remove members through this endpoint. Use `POST /api/groups/{groupId}/grantees` to add or remove members. **Dashboard:** 1. Navigate to **Groups** → **Manage Group** 2. Update the "Group Name" field 3. Click **Save Group** ### Deleting Groups **API:** `DELETE /api/groups/{groupId}` **Dashboard:** 1. Navigate to **Groups** 2. Find the Group in the table 3. Click the **Trash** icon 4. Confirm deletion > **Warning** Deleting a Group will remove all Memberships (Grantees lose access), potentially affect Subscriptions linked to the Group, and cannot be undone. ## Advanced Patterns ### Cross-Team Grantees A single Grantee can belong to multiple Groups, even across different Owners: ``` Grantee "user_alice" ├─ Member of Group "Acme Engineering" (owner: acme_corp) └─ Member of Group "Beta Industries Dev" (owner: beta_industries) ``` This covers scenarios like consultants working across multiple clients, shared team members between departments, or multi-organisation access for admins. **Checking access with Owner filter:** ```bash # Check Alice's access within Acme Corp GET /api/entitlements/check?granteeId=user_alice&owner=acme_corp # Check Alice's access within Beta Industries GET /api/entitlements/check?granteeId=user_alice&owner=beta_industries ``` ### Anonymous to Authenticated Conversion For applications where users can start without authentication, you can use a temporary Owner ID during the purchase flow and convert it later. Start by creating a Cart with a temporary Owner ID like a session ID. The user completes Checkout without authentication, and a Group is created with that temporary Owner. After the user signs up, update the Cart Owner to their authenticated ID before checkout, or use the Cart update endpoint to reassign ownership. ```bash # Before checkout, update the Cart Owner to the authenticated user PUT /api/carts/{cartId} { "owner": "user_authenticated_123", "currency": null, "cartItems": [] } ``` ### Handling Team Growth As teams grow beyond seat limits, you should handle this gracefully: monitor Group size as it approaches seat limits, notify admins when capacity is reached, provide UI to increase seat counts, and adjust Subscriptions via Stripe. Here's one approach: ```javascript // Check if group is at capacity const maxSeats = getLowestSeatCount(group.plans); const currentSize = group.members.length; const isAtCapacity = currentSize >= maxSeats; if (isAtCapacity) { // Show upgrade prompt or prevent adding members } ``` ## Best Practices ### Seat Management Enforce that seat counts remain equal to or greater than Group size to avoid access issues. Give customers tools to monitor their seat utilisation so they can optimise costs and plan for growth. Alert customers before they reach capacity so they can increase seats ahead of time. ### Context-Aware Access Control When Grantees belong to multiple organisations, use the `owner` parameter to filter Entitlement checks to the relevant context. This prevents Entitlements from other organisations bleeding through. ### Security Validate that `granteeId`s match authenticated users to prevent unauthorised access. Never expose internal database IDs (the `id` fields) to end users; use your own identifiers instead. Use the signature in Entitlement responses to verify authenticity. Implement rate limiting on Entitlement checks to protect against abuse. ### Data Consistency Create Groups before Checkout when possible for better control over the process. Match Cart quantities to Group sizes for seated Plans to avoid validation errors. Handle edge cases where Grantees belong to multiple Groups. Audit Group Memberships and Subscription status regularly to catch inconsistencies early. ## Troubleshooting ### Grantee Has No Access When a Grantee should have access but doesn't, work through these checks. Verify the Grantee exists by calling `GET /api/grantees?groupId={groupId}`. Check if the Grantee is in any Groups by looking at their Memberships. Do those Groups have active Subscription Plans? Are the Subscriptions in a valid state like active or trialing? Confirm that the Plans include the Entitlement you're checking for. ### Cannot Add Grantee to Group If you see "This group has reached its maximum number of grantees," you've hit the seat limit. Check the current Group size against seat limits, increase the seat count on per-seat Line Items, or remove existing Grantees before adding new ones. ### Seat Limits Not Matching Expectations When your Group size limit is lower than you expect, check all Plans attached to the Group. The lowest seat count determines the limit, so increase seats on that Plan or remove it. ### Entitlements Not Updating If you've changed a Subscription but the Grantee still has (or doesn't have) access, several things could be wrong. The Subscription status might not have updated yet; wait for the Stripe webhook to process. The Product setting "Return Entitlements While Past Due" might be affecting results. You might be looking at the wrong Grantee or Owner. Your application cache might need refreshing. To debug: check the raw Entitlement check response, verify Subscription status in the dashboard, confirm Grantee Group Memberships, and review webhook event logs. ## Summary Owners scope Subscription data, Groups organise Grantees under those Owners, and Grantees are the individual identities that receive access. Subscription Plans link to Groups rather than individual Grantees, and seats control the maximum Group size for per-seat Plans. When checking Entitlements, Salable traverses from Grantee to Groups to Subscriptions to Plans. --- ### Subscriptions & Billing Source: https://salable.app/docs/subscriptions-and-billing # Subscriptions & Billing ## Understanding Subscriptions ### How Subscriptions Are Created When a customer completes checkout, Stripe notifies Salable of the successful payment. Salable then creates a Subscription from the Plans purchased in the [Cart](/docs/core-concepts#cart). The Subscription inherits properties from the Cart (currency, billing interval, Grantee Group assignments, and any metadata) and belongs to the [Owner](/docs/core-concepts#owner) who made the purchase. If the Cart included Grantee Group assignments, Salable links these Groups to the appropriate [Subscription Items](/docs/core-concepts#subscription-item), giving team members immediate access to [Entitlements](/docs/core-concepts#entitlement). ### Subscription Status Subscriptions move through several states during their lifecycle: - **active**: in good standing with successful payments; full Entitlement access - **past_due**: payment failed; Stripe is retrying via [Smart Retries](https://stripe.com/blog/how-we-built-it-smart-retries). You can configure whether customers retain Entitlement access during this period - **cancelled**: terminated, will not renew. Immediate cancellation revokes access right away. End-of-period cancellation maintains access until the billing cycle ends - **trialling**: trial period with full access but no charge yet. Transitions to active when the trial ends, unless cancelled - **incomplete**: initial payment failed. Stripe retries briefly before moving to cancelled ### Subscription Items Subscriptions contain [Subscription Items](/docs/core-concepts#subscription-item), one for each Plan purchased. A customer buying your main Product plus add-ons has multiple Subscription Items, each tracked separately. Within each Subscription Item, individual Line Items maintain their own quantities. A per-seat Line Item tracks purchased seats, a consultancy Line Item might track purchased hours, and a metered Line Item tracks usage records. Subscription Items inherit Entitlements from their Plan and can be associated with Grantee Groups. If a customer assigned a team Plan to a Grantee Group during checkout, that association persists, ensuring all group members receive the appropriate Entitlements. ## Managing Subscription Items You can add, remove, or replace Plans on a Subscription in a single request. You specify the proration behaviour to control billing. All actions in the request are processed together to calculate a single billing adjustment. Plans must match the Subscription's currency and billing interval. - **Add** adds a new Plan to the Subscription. - **Remove** removes an existing Plan from the Subscription. You cannot remove the last one. - **Replace** swaps one Plan for another. Useful for upgrades or downgrades. ### Tier Tags If your Plans use [Tier Tags](/docs/core-concepts#tier-tags-and-tier-sets), you can replace one Plan with another from the same tier set. For example, if a customer is on the "Basic" Plan, it can be replaced with the "Pro" Plan even if they share the same tier tag. This is how to handle upgrades/downgrades within a tier set. You cannot add a Plan that shares a tier tag with a Plan already on the Subscription. If you need to move a customer to a different Plan within the same tier set, use the replace action rather than adding the new Plan separately. ## Quantity Management After a customer purchases a Subscription, you can adjust the quantity of each Line Item on their Plan. You specify the proration behaviour to control billing. The minimum and maximum limits configured on the Line Item are enforced. For per-seat Line Items, the quantity determines how many seats are available for the associated Grantee Group. You can increase the quantity at any time to add seats, but you cannot decrease it below the current number of group members. You must remove members from the group first. ## Proration When a customer upgrades, downgrades, adds Plans, or changes quantities between billing dates, the amount owed for the partial period is prorated based on the time remaining until the next billing anchor. For example, if a customer with a \$100/month Plan upgrades to a \$150/month Plan on day 10 of a 30-day cycle, the prorated charge is approximately \$33.33 (the \$50 difference × 20/30 remaining days). At the next billing date, the full \$150 is charged. This applies to seat additions, plan changes, and add-ons. All changes synchronise to the single billing anchor, so customers receive one invoice per period. ### Proration Behaviours Salable supports these proration behaviours: - **create_prorations** calculates the difference between the old and new Subscription value immediately and generates an invoice or credit. If a customer upgrades from a \$50/month Plan to a \$100/month Plan halfway through the month, they are charged approximately \$25 (the prorated difference for the remaining half-month). At their next billing date, they pay the full \$100. - **always_invoice** creates an invoice immediately for any proration amount, even if the change results in a credit. This generates invoices for every change (useful for audit purposes), though credits still apply to the customer's balance for future billing. - **none** turns off proration entirely. The customer continues paying the old rate until their next billing date, then starts paying the new rate. Simpler for customers to understand and avoids mid-cycle charges, but you may be giving away upgraded access for free during the transition period. Works well for downgrades where you want customers to keep premium features until the end of their paid period. ### Choosing the Right Proration Strategy The proration behaviour you specify depends on the update: - Upgrades: **create_prorations** works well. Customers expect to pay more for better features, and immediate billing compensates you for the upgrade. - Downgrades: **none** lets customers keep premium features until the end of their paid period before switching to the lower price. - Add-ons: **create_prorations** makes sense because the customer is actively requesting a feature and expects to pay for it. The immediate charge confirms access. - Multiple changes: **create_prorations** calculates the net difference, resulting in a small charge, a small credit, or nearly zero change. ## Cancellation Management ### End-of-Period Cancellation Schedule a Subscription to cancel at the end of the current billing period by disabling auto-renewal. The Subscription remains active until the period ends, then terminates. No refund is issued. The customer receives the full value of what they paid. After scheduling, the Subscription includes `cancelAtPeriodEnd: true`. Use this to show customers when access ends. To reverse a scheduled cancellation, re-enable auto-renew. ### Immediate Cancellation Immediate cancellation terminates the Subscription and revokes Entitlements. Salable issues a prorated refund for the unused portion of the billing period. Use immediate cancellation for terms of service violations, account deletions, or when switching billing intervals. ## Billing Cycles and Intervals ### How Billing Anchors Work Every Subscription has a billing anchor date: the day of the month when the Subscription renews and the next billing period begins. Salable sets this anchor when the Subscription is created, based on the checkout completion date. If a customer subscribes on January 15th with monthly billing, their anchor is the 15th. They are billed on the 15th of each month for the upcoming period. Customers know when charges will occur each month. For annual Subscriptions, the anchor works the same way, spanning years instead of months. ### Month-End Edge Cases Billing anchors on the 29th, 30th, or 31st require special handling because month lengths vary. Stripe and Salable handle this by billing on the last available day of shorter months. A January 31st anchor bills on February 28th (or 29th in leap years), then March 31st, April 30th, May 31st, and so on. The anchor remains conceptually tied to the 31st, but actual billing happens on the last available day of each month. Customers never miss a billing date, and the cycle stays as close to monthly as possible. Billing periods do vary slightly in length: a period ending on February 28th is shorter than one ending on March 31st. ## Invoice Management Stripe generates an invoice for every Subscription billing event. Each invoice includes Line Items showing what was billed, payment status, total amount, and an `invoicePdf` URL for PDF receipts. For metered Line Items, invoices show usage charges with quantity consumed, per-unit price, and total (_eg_ "API Calls: 1,450 × \$0.10 = \$145.00"). You can also preview what customers will be charged on their next billing date before it is finalised. The preview includes recurring charges, pending metered usage, account credits, and the final amount. ## Price Synchronisation ### Price Changes When you update a price in Salable, existing Subscriptions continue billing at the old price by default. Existing customers are grandfathered in. > **Note** You can configure the billing model to apply updated prices to all customers. ### Syncing to New Prices The Price Synchronisation endpoint migrates a Subscription to the latest pricing. It updates the Subscription Items to reference the current prices for their Plans. The proration behaviour determines when the new pricing takes effect. Using `none` means the Subscription continues at the old price through the current billing period, then switches to the new price at the next renewal. Using `create_prorations` means the price change takes effect immediately with appropriate prorated charges or credits. ### Communicating Price Changes When changing prices: - Announce at least 30 days in advance - Explain the reasons and any value changes - State the dates when the change takes effect for existing customers - Consider offering options to lock in current pricing End-of-period price synchronisation respects the customer's current billing cycle and gives them time to adjust. > **Important** Price notification requirements vary by jurisdiction. Some require 30 days' notice, others 60 days or more. Research legal requirements for your customer locations or consult legal counsel before changing prices. ## Payment Failures and Dunning ### Past Due Status If a scheduled Subscription payment fails, the Subscription enters a `past_due` state. Stripe attempts [Smart Retries](https://docs.stripe.com/billing/revenue-recovery/smart-retries), adapting the retry schedule based on failure reason and historical patterns. During this period, you can control whether customers retain Entitlement access. Maintaining access reduces frustration if the failure is temporary, but it means providing service without current payment. ### 3D Secure and Strong Customer Authentication In the European Economic Area, Strong Customer Authentication (SCA) regulations require additional verification for certain transactions, commonly through 3D Secure (3DS). Recurring Subscription payments benefit from exemptions, but several scenarios trigger authentication: - **Price increases** beyond certain thresholds (often 30 EUR or equivalent) - **Initial Subscription payments** (handled automatically by Stripe checkout) - **Payment method changes** - **Large mid-cycle charges** from upgrades or expensive add-ons When a payment fails due to SCA, the customer must authenticate. Updating their card will not resolve the issue. Notify customers that verification is required, and provide Stripe's billing portal link to complete the authentication challenge. For price increases or mid-cycle modifications that may trigger authentication, tell customers in advance that their bank may require verification. Consider offering the option to delay changes until the next billing date to avoid mid-cycle triggers. > **Note** SCA requirements are mandatory in the European Economic Area. Other regions may have different requirements or none at all. Consider your customer distribution when planning communication strategies. ### Customer Communication During Dunning Stripe handles payment retries, but you are responsible for customer communication. When a payment fails, notify the customer through email or in-app messages. Explain the failure, provide a link to update their payment method, and tell them how much time they have before access is affected. If retries continue to fail, escalate urgency. After several days, make it clear that cancellation is imminent. If a payment succeeds after a retry, confirm the charge was successful so customers know the issue is resolved. ### Providing Payment Update Links Stripe's hosted billing portal is the simplest way for customers to update their payment method. You can generate portal links that take customers to a secure page where they update cards, view billing history, and manage their Subscription. Include these portal links in your payment failure communications. ## Perpetual Subscriptions A Perpetual Subscription is one that remains active indefinitely. Unlike interval-based Subscriptions that renew on a billing cycle, a Perpetual Subscription has no expiry date and no renewal period and stays active until you explicitly cancel it. Perpetual Subscriptions are one-off purchases with no recurring billing, making them well suited for lifetime access deals where a customer pays once and retains access permanently. Perpetual Subscriptions behave identically to any other Subscription from your application's perspective. Entitlement checks return the same results, Grantee Groups work the same way, and Tier Tag constraints apply as normal. The only distinction is that Salable never schedules an end date, renewal event, or recurring charge for the Subscription. ### When to Use Perpetual Subscriptions Perpetual Subscriptions are the right choice when access should continue without any time constraint and no ongoing billing relationship is needed. Lifetime deals, permanent partner integrations, and internal tooling access for your own team are all good fits. Perpetual Subscriptions must be cancelled manually in order to terminate. If you would like a subscription to cancel on a specified date create a subscription with an expiry date with auto-renew disabled. ## Salable Only Subscriptions Salable also offers the ability to generate Subscriptions without having to go through the standard checkout process. Aptly named **Salable Only Subscriptions**, these Subscriptions can be generated directly from the Subscriptions page and skip the Stripe checkout process that standard Subscriptions go through. A Salable Only Subscription is functionally identical to a normal Subscription. It carries Entitlements, supports Grantee Groups, and participates in the same Tier Tag constraints that prevent conflicting Plans on a single Subscription. The only meaningful difference is that no invoice is generated and no payment is collected through a checkout link. From your application's perspective, when checking Entitlements via the API, a Salable Only Subscription is indistinguishable from one created through checkout. ### When to Use Salable Only Subscriptions Salable Only Subscriptions are the right tool when you want to generate Subscriptions without having to go through the standard checkout process. A few common scenarios illustrate how this plays out in practice. **Internal team access.** Your engineering, support, and product teams need to use your application to do their jobs. Putting them through a checkout flow is unnecessary friction, and charging your own employees doesn't make sense. A Salable Only Subscription gives them full access under the same Entitlement system your paying customers use, which also means their experience accurately reflects what customers see. **Partners and integrations.** Technology partners, agency partners, or integration developers often need access to build on top of your platform. The value exchange isn't monetary—it's the ecosystem growth they enable. A Salable Only Subscription establishes the access relationship formally, with a proper Subscription record and Entitlements, without requiring a billing arrangement. **Trials and demos you control.** Some customer journeys benefit from access that you provision directly rather than through self-serve checkout. A sales-led trial might start with you granting the prospect a time-limited Subscription. A demo environment for a prospective enterprise customer can be set up in seconds without needing the customer to enter payment details. **Compensation and goodwill.** When something goes wrong—extended downtime, a billing error, a support failure—granting a period of free access is a meaningful gesture. A Salable Only Subscription lets you do this precisely, for exactly the Plans and duration you intend, without manual workarounds. **Early access and beta programs.** Beta testers and early access users are providing feedback and validation rather than paying for a finished product. A Salable Only Subscription with an expiry date matching your beta window gives them proper access while keeping your customer records clean and your Entitlement checks consistent with production. **Manual invoicing.** There may be cases where you want to collect payment manually outside of Salable such as having customers in regions where card payments aren't practical, or deals negotiated with custom payment terms. When you collect outside of Salable, you still need to grant access to your application. A Salable Only Subscription lets you provision access immediately when payment is confirmed, without going through the Salable checkout process. This can also be handy for enterprise customers who you would like to manually invoice via purchase order and give immediate access to your application while awaiting funds. ### Creating a Salable Only Subscription Salable Only Subscriptions are created from the Subscriptions page in your dashboard by clicking **Create Subscription**. You will need to add: - **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 that's just a string you provide. These can represent users, teams, projects, boards, workspaces, or any other entity in your application that needs access to features. - **Owner** — An owner is an identifier used to scope subscriptions and usage records in Salable. This is typically a user ID for individual Subscriptions, or a team or organization ID for shared Subscriptions where multiple people need access to the same Subscription data. If the Owner doesn't exist yet in your account, it will be created automatically. - **Grantee** — optionally, the specific user or Group who receives the Entitlements. If you're granting team-level access, you'd specify a Grantee Group here. - **Lifecycle Options** — because there's no Stripe billing cycle to inherit, you define the life cycle of the Subscription directly. ### Lifecycle Options Salable Only Subscriptions give you precise control over how long access lasts. When creating a Subscription, you choose one of three approaches. **Interval-based billing** works like a standard Subscription. You set an interval (day, week, month, or year) and an interval count, and the Subscription renews accordingly. Unlike a paid Subscription, there's no payment collected at each renewal, but the Subscription remains active and Entitlements remain valid across renewal periods. For example, if you want to create a Subscription that cycles every month you would set the interval to "month" and the interval count to "1", for subscriptions that cycle quarterly you would set the interval to "month" and the interval count to "3", so on and so forth. You can also enable **Cancel at Period End** to schedule the Subscription to terminate when the current period completes, without renewing. **Expiry date** lets you set a fixed date when the Subscription terminates automatically. This is the right option for time-limited trials, beta access, or compensation periods where you want access to end on a specific day regardless of any other logic. You specify the date when creating the Subscription; no billing interval is needed. **Perpetual** creates a Subscription that never expires and never renews—it remains active indefinitely until you explicitly cancel it. This is useful for permanent partner access, internal tooling, or any scenario where access should continue without any time constraint. Note that selecting between an expiry date and being perpetual are mutually exclusive, subscriptions can either expire at the expiry date specified or continue perpetually. ### Metered Plans and Salable Only Subscriptions Interval and interval count must be specified when creating a Salable Only Subscription. This is necessary because metered billing aggregates usage over a billing period. At the end of the period the final usage count is recorded and a new record is created for the next period. Everything else about metered billing works the same way. You record usage through the API, Salable keeps track of the consumption for each cycle on the Subscription. Note that the same meter restrictions also apply to Salable Only Subscriptions, an Owner cannot subscribe to a metered Plan if they're already subscribed to the associated meter elsewhere. ### What Salable Only Subscriptions Don't Include Salable Only Subscriptions do not generate invoices, receipts, or any billing records. Because there's no Stripe involvement, there are no payment method requirements, no dunning retries, no proration calculations, and no SCA challenges. Price synchronisation doesn't apply since there's no price being charged. If you later want to transition a customer from a Salable Only Subscription to a paid Subscription, you would cancel the Salable Only Subscription and direct them through the standard checkout flow to create a new paid Subscription. --- ### Cart & Checkout Source: https://salable.app/docs/cart-and-checkout # Cart & Checkout ## Overview Salable's Cart system lets you build shopping experiences for your Subscription Products. Carts let customers select multiple Plans and complete the purchase in a single transaction, whether you're implementing a self-service pricing page or a custom checkout flow. Carts support anonymous users who haven't signed up for your application. Guests can add Plans to their Cart before creating an account. When they sign up, you update the Cart's Owner to link to their new account. ## How Carts Work A Cart belongs to an [**Owner**](/docs/core-concepts#owner) (an identifier used to scope the Subscription) and contains Cart items representing the Plans the customer wants to purchase. Each Cart has a billing **interval** (_ege_ monthly, yearly etc), and can optionally specify a **currency**. If no currency is provided, Stripe determines one during checkout based on the customer's location. When you add items to a Cart, Salable validates that the selected Plans support the Cart's currency and interval, so customers only see compatible pricing options at checkout. The Cart has three states: **active** for Carts being built, **complete** for Carts that have checked out, and **abandoned** for Carts explicitly marked as no longer needed. When the customer is ready to purchase, you generate a Stripe Checkout session URL from the Cart. After successful payment, Salable creates a Subscription with the specified Grantee Groups and Entitlements. ## Cart Concepts ### Owner The Owner is the entity responsible for paying for the Subscription. For individual Subscriptions, this might be a user ID. For team or organization Subscriptions, it's typically an organization or company ID. You choose whatever identifier makes sense for your business model. Owners are created automatically when you create a Cart. If an Owner with that identifier already exists in your organization, Salable reuses it. This means you can create multiple Carts for the same Owner without duplicating Owner records. ### Currency and Interval **Providing an Explicit Currency** We recommend explicitly defining the currencies your Product supports. When you provide a currency: - You can design different pricing strategies for different markets, offering region-specific discounts or adjusting prices based on purchasing power parity - You can target specific currencies without configuring all currency options across every Line Item - Salable cherry-picks only the Line Items that have pricing in that specific currency, so customers see consistent prices from your pricing page through to Stripe checkout - What customers see on your site matches what they pay at checkout This approach gives you the most control and the most predictable customer experience. **Omitting Currency (Geolocation Mode)** If you don't specify a currency, Stripe uses geolocation at checkout to determine the customer's currency. This approach has trade-offs: - Stripe detects the customer's location and displays prices in their local currency - Salable includes all Line Items from the Plans in your Cart (no cherry-picking) - The currency at checkout might differ from what you displayed on your pricing page if the customer is in a different region - All Line Items across all Plans must have the same default currency (a Stripe requirement) - You must configure pricing for all supported currencies across all Line Items **Critical Requirement for Geolocation Mode** If you omit currency, all Line Items across all Plans in your Cart must share the same default currency (a Stripe requirement). If Plan A's Line Items default to USD and Plan B's Line Items default to GBP, the checkout will fail. You must provide an explicit currency when creating the Cart in that case. You don't need to specify an interval and interval count when creating a Cart, but you must provide one when adding the first item. The interval can be **day**, **week**, **month**, or **year** for plans with recurring line items. Or, for one-off line items it can be **null**. Once the first item sets the interval and interval count, all subsequent items added to the Cart must use the same interval (or currency for one off items). ### Cart Items A Cart item represents a Plan the customer wants to purchase. Each Cart item includes the Plan ID, optional metadata for specifying quantities above the minimum for specific Line Items, and optionally a Grantee ID or Grantee Group ID to assign access. The metadata structure is an object where keys are Line Item slugs and values are objects with quantity information. You only need to include Line Items in metadata when setting their quantity above the configured minimum. Line items omitted from metadata automatically use their minimum quantity. Metered Line Items should never be included in metadata. ### Cart Status Carts move through statuses during their lifecycle. **Active** Carts are being built: you can add items, remove items, and modify them. **Complete** Carts have been checked out and converted into Subscriptions. **Abandoned** Carts have been marked as no longer needed, which helps you track Cart abandonment rates. ### One-Off Purchases Salable supports one-off purchases through the Cart system. They can be purchased individually or alongside recurring plans that share the same currency. After purchasing a one-off item, a Receipt is generated as a proof of purchase which can be viewed in the Salable dashboard. A `receipt.created` webhook event is also emitted. **Creating One-Off Only Carts** For purchases that contain only one-off items (no recurring charges), create the Cart with `interval` and `intervalCount` set to `null`: ```json { "owner": "company_acme", "currency": "USD", "interval": null, "intervalCount": null } ``` When adding items to a one-off Cart, also set `interval` and `intervalCount` to `null`: ```json { "cartId": "Cart_01HXXX", "planId": "Plan_01HYYY", "interval": null, "intervalCount": null } ``` **Mixed Plans: One-Off Plus Recurring** Plans can contain both one-off and recurring Line Items. When you add such a Plan to a Cart with a specified interval and currency: - Recurring Line Items that match the Cart's interval and have pricing in the Cart's currency are included - One-off Line Items that have pricing in the Cart's currency are automatically included (they don't need to match the interval) For example, consider a Plan with three Line Items: a $99 one-time setup fee, a $29/month base subscription, and a $10/user/month per-seat charge. When you add this Plan to a monthly USD Cart, all three Line Items are included. The customer sees a checkout with the one-time setup fee plus the recurring monthly charges. The setup fee appears only on the first invoice, while the subscription and per-seat charges recur each month. ## Creating a Cart ### API: Create Cart **Endpoint:** `POST /api/carts` **Request Body:** ```json { "owner": "company_acme", "currency": "USD", "interval": "month", "intervalCount": 1 } ``` Or for geolocation-based currency selection: ```json { "owner": "company_acme", "interval": "month", "intervalCount": 1 } ``` **Parameters:** The **Owner** is an ID from your system, typically a user, team, or org ID. The **currency** is optional. Provide a three-letter currency code in uppercase (automatically converted if you provide lowercase) for explicit currency selection, or omit it to let Stripe use geolocation to determine the best currency for the customer. The **interval** and **intervalCount** are required fields but accept `null` values. Set **interval** to `day`, `week`, `month`, or `year`, or `null` to set it later. Set **intervalCount** to a number, or `null` if interval is null. **Response:** ```json { "type": "object", "data": { "id": "Cart_01HXXX", "organisation": "org_xxx", "ownerId": "Owner_01HYYY", "currency": "USD", "interval": "month", "intervalCount": 1, "status": "active", "createdAt": "2024-01-15T10:00:00Z", "updatedAt": "2024-01-15T10:00:00Z" } } ``` ### Creating Carts for Anonymous Users For anonymous users who haven't authenticated yet, use a temporary session identifier as the Owner. This could be a session ID from your application, a temporary UUID, or any unique identifier you can track. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); // Example: Create a Cart when user visits pricing page app.post('/api/create-cart', async (req, res) => { const { sessionId, currency, interval, intervalCount } = req.body; try { const { data: cart } = await salable.api.carts.post({ owner: `session_${sessionId}`, interval, intervalCount, ...(currency && { currency: currency.toUpperCase() }) }); res.json({ cartId: cart.id }); } catch (error) { res.status(500).json({ error: error.message }); } }); ``` ### Creating Carts for Authenticated Users For authenticated users, use their user ID or organisation ID as the Owner identifier directly. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); const { data: cart } = await salable.api.carts.post({ owner: userId, interval, intervalCount, ...(currency && { currency: currency.toUpperCase() }) }); ``` ## Adding Items to a Cart You can add multiple different Plans to a Cart, but each Plan can only be added once. If you need multiple quantities, adjust the quantity in the Line Item metadata rather than adding the same Plan multiple times. ### API: Create Cart Item **Endpoint:** `POST /api/cart-items` **Request Body:** ```json { "cartId": "cart_01KHRPSY70VTYJVQEYBBR8M3KD", "planId": "plan_01KHNZHBA28YMY720VVD8KVKF5", "interval": "month", "intervalCount": 1, "metadata": { "per_seat_charge": { "quantity": 5 } }, "grantee": "user_alice" } ``` **Parameters:** The **cartId** identifies which Cart to add the item to. The **planId** specifies which Plan the customer wants to purchase. The **interval** sets or confirms the Cart's billing interval: use `day`, `week`, `month`, or `year` for recurring purchases. For adding only one-off line items, set **interval** to `null`. If the Cart already has an interval set, the value must match. The **intervalCount** works alongside interval to define the billing frequency (_eg_ `2` with `week` for biweekly billing). For one-off purchases, set **intervalCount** to `null`. The **metadata** object is optional and maps Line Item slugs to quantity objects. You only need to include Line Items where the quantity should be above the configured minimum. Line items not included in metadata will use their minimum quantity. Never include metered Line Items in metadata. The **grantee** is optional and can be a grantee ID or a group ID (starting with `grp_`). **Response:** ```json { "type": "object", "data": { "id": "ci_01KHRPSY70XWAW1Y9WP8SVZ744", "organisation": "org_xxx", "cartId": "cart_01KHRPSY70VTYJVQEYBBR8M3KD", "planId": "plan_01KHNZHBA28YMY720VVD8KVKF5", "metadata": { ... }, "granteeId": "user_alice", "groupId": null, "createdAt": "2024-01-15T10:05:00Z", "updatedAt": "2024-01-15T10:05:00Z" } } ``` ### Understanding Metadata The metadata structure specifies quantities for Line Items when you need to set them above their minimum values. Each Line Item slug maps to an object with a `quantity` property. You only need to include Line Items in the metadata when their quantity should be higher than the configured minimum. If you omit a Line Item from the metadata, Salable uses the minimum quantity for that Line Item. ```javascript // Example: Plan with three Line Items const metadata = { // Only include Line Items where quantity > minimum user_seats: { quantity: 10 } // Per-seat Line Item, setting 10 seats // monthly_base would use its minimum (typically 1) if not specified // api_calls is metered, so it's not included here }; ``` **Important:** Do not include metered Line Items in the metadata. Metered Line Items track quantities through usage recording after the Subscription is created. Including a metered Line Item in your Cart metadata will return an error. You can only increment metered item quantities after purchase, when you record usage. ### Adding Items Example ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); // Example: Add a Plan to Cart app.post('/api/cart/add-plan', async (req, res) => { const { cartId, planId, seats } = req.body; try { // Build metadata - only include Line Items with quantities above minimum const metadata = {}; // Only add per-seat if quantity is above its minimum if (seats > 1) { // Assuming minimum is 1 metadata.per_seat = { quantity: seats }; } // base_subscription uses its minimum (typically 1) when not specified // metered Line Items are never included in metadata const { data: cartItem } = await salable.api.cartItems.post({ cartId, planId, interval: 'month', metadata, ...(req.user.id && { grantee: req.user.id }) }); res.json({ success: true, cartItem }); } catch (error) { res.status(400).json({ error: error.message }); } }); ``` ### Validation Rules These validation rules apply when adding items to a Cart: - The same Plan cannot be added more than once - If the Cart has an explicit currency set, the Plan must have pricing configured for that currency and the Cart's interval. - If the Cart's currency was omitted, all Line Items from the Plan are included. - Quantities must be within the Line Item's minimum and maximum quantity limits. For per-seat Line Items, if you provide a Grantee Group ID, the quantity must be at least equal to the number of members in that Grantee Group. - You cannot add a metered Line Item if the Owner already has an active Subscription with that same meter slug (to prevent duplicate usage tracking). If [Tier Tags](/docs/core-concepts#tier-tags-and-tier-sets) are assigned to Plans, additional validation rules apply when adding items to a Cart: - You cannot add multiple Plans with the same tier tag to the same Cart. - You cannot add a Plan to a Cart if the Owner already has an active Subscription to another Plan with the same tier tag. ## Managing Cart Items ### Retrieving a Cart **Endpoint:** `GET /api/carts/{cartId}` Retrieve the full Cart with all its items, expanded metadata showing Line Item details, and quantity validation rules for each Line Item. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); // Display Cart contents to user app.get('/api/cart/:cartId', async (req, res) => { try { const { data: cart } = await salable.api.carts.byId(req.params.cartId).get(); // Transform for frontend display // cart.cartItems is { type: 'list', data: [...] } const cartSummary = { currency: cart.currency, interval: cart.interval, cartItems: cart.cartItems.data.map(item => ({ planName: item.plan.name, lineItems: item.plan.lineItems.data.map(li => ({ name: li.name, priceType: li.priceType })) })) }; res.json(cartSummary); } catch (error) { res.status(500).json({ error: error.message }); } }); ``` ### Removing Cart Items **Endpoint:** `DELETE /api/cart-items/{cartItemId}` Remove a specific item from the Cart. The Cart must be in **active** status to remove items. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); await salable.api.cartItems.byId(cartItemId).delete(); ``` ### Updating a Cart **Endpoint:** `PUT /api/carts/{cartId}` Update a Cart's Owner, currency, and Cart Item details. Use this to convert anonymous Carts to authenticated user Carts, change currency, or update Cart Item grantees and metadata. Returns `204 No Content` on success. **Request Body:** ```json { "owner": "user_alice_authenticated", "currency": "USD", "cartItems": [ { "id": "ci_01KHXX1P13NK3R7E951GPWYT2B", "granteeId": "user_alice", "metadata": { "team_seats": { "quantity": 5 } } } ] } ``` All three fields (`owner`, `currency`, `cartItems`) are required. Set `currency` to `null` to let Stripe determine currency via geolocation. The `cartItems` array must reference existing Cart Item IDs. Each Cart Item can optionally update its `granteeId` and `metadata`. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); await salable.api.carts.byId(cartId).put({ owner, currency, cartItems }); ``` ### Abandoning a Cart **Endpoint:** `DELETE /api/carts/{cartId}` Mark a Cart as abandoned. Use this to track Cart abandonment metrics or clean up Carts that users closed without purchasing. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); await salable.api.carts.byId(cartId).delete(); ``` ## Anonymous to Authenticated Flow Salable's Cart system supports anonymous users. Visitors can add Plans to a Cart, purchase them with a temporary owner ID, then sign up after checkout. You can associate the purchase with their new ID once they create an account. ### Step 1: Create Anonymous Cart When an anonymous user visits your pricing page, create a Cart using a session identifier. ```javascript // Frontend: When user visits pricing fetch('/api/create-anonymous-cart', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: getSessionId(), // Your session tracking currency: 'USD', interval: 'month' }) }) .then(res => res.json()) .then(({ cartId }) => { // Store cartId in localStorage or state localStorage.setItem('cartId', cartId); }); ``` ### Step 2: Add Plans as Anonymous User Let the anonymous user add Plans to their Cart normally. The Cart exists and functions fully before authentication. ```javascript // Frontend: User clicks "Add to Cart" on pricing page function addPlanToCart(planId, seats) { const cartId = localStorage.getItem('cartId'); return fetch('/api/cart/add-plan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cartId, planId, seats }) }); } ``` ### Step 3: Prompt for Authentication When the user proceeds to checkout, redirect them to sign up or log in if they haven't already. ```javascript // Frontend: User clicks "Proceed to Checkout" function proceedToCheckout() { if (!isAuthenticated()) { // Store intent to return to checkout after auth localStorage.setItem('checkoutAfterAuth', 'true'); window.location.href = '/signup'; } else { continueToCheckout(); } } ``` ### Step 4: Update Cart Owner After Authentication Once the user completes authentication, update the Cart's Owner from the session ID to the authenticated user ID. ```javascript // Backend: After successful signup/login app.post('/api/auth/login', async (req, res) => { // ... authentication logic ... const user = authenticatedUser; const cartId = req.body.cartId; // Passed from frontend if (cartId) { try { await updateCartOwner(cartId, user.id); } catch (error) { console.error('Failed to transfer Cart:', error); // Cart transfer failing shouldn't block login } } res.json({ user, success: true }); }); ``` ### Step 5: Continue to Checkout With the Cart now linked to the authenticated user, proceed to generate the checkout URL. ```javascript // Frontend: After login redirect if (localStorage.getItem('checkoutAfterAuth') === 'true') { localStorage.removeItem('checkoutAfterAuth'); continueToCheckout(); } ``` ## Generating Checkout URLs ### API: Generate Checkout Link **Endpoint:** `POST /api/carts/{cartId}/checkout` Generate a Stripe Checkout session URL for customers to complete payment. **Request Body:** ```json { "successUrl": "https://yourapp.com/welcome", "cancelUrl": "https://yourapp.com/pricing", "email": "customer@example.com", "allowPromoCodes": true, "automaticTax": false, "collectBillingAddress": true, "collectShippingAddress": false, "cardPrefillPreference": "choice", "trialPeriodDays": 14 } ``` **Parameters:** These parameters can be provided in the API call or configured in your Product settings. If a value exists in the Product settings, it will be used as the default. Providing a value in the API call overrides the Product settings. **Required (unless configured in Product settings):** - **successUrl** - URL where Stripe redirects customers after successful payment - **cancelUrl** - URL where customers return if they abandon checkout If multiple Products in your Cart have conflicting URL defaults, you must provide explicit values in the API call. **Optional:** - **email** - Pre-fills the customer email in the checkout form - **allowPromoCodes** - Enables promotional code entry at checkout (boolean) - **automaticTax** - Enables Stripe Tax for automatic tax calculation (boolean) - **collectBillingAddress** - Requires billing address at checkout (boolean) - **collectShippingAddress** - Requires shipping address at checkout (boolean) - **cardPrefillPreference** - Controls saving payment methods: `none`, `choice`, or `always` - **trialPeriodDays** - Number of days for trial period before billing begins (1-730 days) **Response:** ```json { "type": "object", "data": { "url": "https://checkout.stripe.com/c/pay/cs_live_..." } } ``` ### Checkout Implementation ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); // Example: Handle checkout button app.post('/api/cart/:cartId/checkout', async (req, res) => { try { const { data } = await salable.api.carts.byId(req.params.cartId).checkout.post({ email: req.user.email, successUrl: `${process.env.APP_URL}/welcome`, cancelUrl: `${process.env.APP_URL}/pricing`, allowPromoCodes: true, collectBillingAddress: true, cardPrefillPreference: 'choice' }); res.json({ checkoutUrl: data.url }); } catch (error) { res.status(400).json({ error: error.message }); } }); ``` ### Checkout Behavior When you generate a checkout URL, Salable looks up the existing Grantee Group if you provided a Group ID (starting with `grp_`) on the Cart Item. If you provided a plain Grantee ID instead, that individual receives access directly. The Cart status changes to **complete** after successful payment, preventing any further modifications. Once Stripe confirms the payment is successful, Salable creates Subscription and Subscription Plan records, and sets up usage tracking for metered items in your organization. ## Quick Checkout Quick Checkout lets customers purchase a single Plan without building a Cart first. This works well for "Buy Now" buttons, simple pricing pages where customers select a single tier, or situations where you want fewer steps between plan selection and payment. You provide a Plan ID, interval, and optional parameters directly to the checkout endpoint, and Salable generates a Stripe Checkout session URL in a single API call. ### When to Use Quick Checkout Use Quick Checkout when customers are purchasing a single Plan and you want the shortest path to payment. This works well for pricing pages with clear tier selection, upgrade flows where users move from one plan to another, or trial-to-paid conversions where the plan is predetermined. Use the Cart flow instead when customers need to purchase multiple Plans in a single transaction, when you want to support anonymous users building a Cart before authentication, or when you need to modify quantities or configurations before checkout. ### API: Create Quick Checkout **Endpoint:** `POST /api/checkout` **Request Body:** ```json { "owner": "user_alice", "planId": "plan_01KHRQ9T3W4WFDARCVX3QG70EB", "interval": "month", "intervalCount": 1, "currency": "USD", "successUrl": "https://yourapp.com/welcome", "cancelUrl": "https://yourapp.com/pricing", "email": "customer@example.com", "metadata": { "per_seat_charge": { "quantity": 5 } }, "grantee": "user_alice", "allowPromoCodes": true, "trialPeriodDays": 14 } ``` **Parameters:** **Required:** - **owner** - The Owner identifier for the Subscription (user ID, organization ID, etc.) - **planId** - The Plan the customer is purchasing - **interval** - Billing interval: `day`, `week`, `month`, `year`, or `null` for one-off purchases - **intervalCount** - Number of intervals between billings (_eg_ `2` with `month` for bimonthly). Use `null` for one-off purchases. - **successUrl** - URL where Stripe redirects after successful payment (unless configured in Product settings) - **cancelUrl** - URL where customers return if they abandon checkout (unless configured in Product settings) **Optional:** - **currency** - Three-letter currency code (USD, GBP, EUR, etc.). If omitted, Stripe uses geolocation to determine currency. - **email** - Pre-fills the customer email in checkout - **metadata** - Object mapping Line Item slugs to quantity objects. Only include non-metered Line Items where quantity should exceed the minimum. Metered Line Items should never be included. - **grantee** - Grantee ID or Grantee Group ID (starting with `grp_`) to assign access - **allowPromoCodes** - Enable promotional code entry (boolean) - **automaticTax** - Enable Stripe Tax for automatic tax calculation (boolean) - **collectBillingAddress** - Require billing address at checkout (boolean) - **collectShippingAddress** - Require shipping address at checkout (boolean) - **cardPrefillPreference** - Controls saving payment methods: `none`, `choice`, or `always` - **trialPeriodDays** - Number of trial days before billing begins (1-730 days) **Response:** ```json { "type": "object", "data": { "url": "https://checkout.stripe.com/c/pay/cs_live_..." } } ``` ### Currency Handling in Quick Checkout Quick Checkout follows the same currency logic as the Cart checkout system, applied to a single plan. **Providing an Explicit Currency** When you provide a currency in the request, Salable validates that the Plan has pricing configured for that currency and interval combination. Only Line Items with matching currency are included in the checkout, so customers see consistent pricing from your pricing page through to Stripe checkout. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); const { data } = await salable.api.checkout.post({ owner: 'user_alice', planId: 'plan_01KHRQ9T3W4WFDARCVX3QG70EB', interval: 'month', intervalCount: 1, currency: 'GBP', successUrl: 'https://yourapp.com/welcome', cancelUrl: 'https://yourapp.com/pricing' }); ``` **Omitting Currency (Geolocation Mode)** If you don't provide a currency, Salable looks for a default currency across the Plan's Line Items. All Line Items with `isDefault: true` on their currency options are included. Stripe then uses geolocation at checkout by automatically selecting the correct currency options based on their location, if their currency does not exist on the plan then the price will fallback to the default currency. This approach requires all Line Items in the Plan to have the same default currency. If Line Items have different default currencies, the checkout will fail. In that case, you must provide an explicit currency. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); const { data } = await salable.api.checkout.post({ owner: 'user_alice', planId: 'plan_01KHRQ9T3W4WFDARCVX3QG70EB', interval: 'month', intervalCount: 1, successUrl: 'https://yourapp.com/welcome', cancelUrl: 'https://yourapp.com/pricing' // currency omitted - Stripe will detect based on customer location }); ``` ### One-Off Purchases with Quick Checkout Quick Checkout supports one-off purchases just like the Cart flow. Set both `interval` and `intervalCount` to `null` when purchasing Plans that contain only one-off Line Items. ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); const { data } = await salable.api.checkout.post({ owner: 'user_alice', planId: 'plan_01KHRQ9T3W4WFDARCVX3QG70EB', // Plan with one-off Line Items interval: null, intervalCount: null, currency: 'USD', successUrl: 'https://yourapp.com/confirmation', cancelUrl: 'https://yourapp.com/products', email: 'alice@example.com' }); ``` ## Best Practices ### Session Management for Anonymous Carts Use a consistent session identifier throughout the anonymous user's journey. Store the Cart ID in localStorage or a cookie so it persists across page refreshes. When the user authenticates, make sure to transfer the Cart Ownership immediately to prevent losing their selections. ### Currency Selection Strategy Choose the currency approach that best fits your business model and customer experience. **Explicitly defining currency** is recommended when you know the customer's currency context. Detect the customer's location or preferences in your application, display your pricing page in that currency, and then create the Cart with that currency explicitly set. Customers see consistent pricing from your pricing page through to Stripe checkout, with no unexpected currency changes. With an explicit currency, you can implement region-specific pricing strategies: offer discounts in emerging markets, adjust for purchasing power parity, or experiment with different price points in different currencies, without configuring every currency option across every Line Item. You can use various methods to determine currency: detect the customer's location using their IP address with a geolocation service, allow customers to select their preferred currency from your pricing page, use the customer's browser locale or account settings, or default to your primary market currency. Once you know the currency, provide it explicitly when creating the Cart. **Omitting currency** works when you want Stripe to handle currency detection at checkout. This suits cases where you're comfortable with Stripe picking the currency for each customer. The currency at checkout may differ from what the customer saw on your pricing page if you displayed prices in a specific currency, which can confuse customers who encounter different pricing than expected. When omitting currency, all Line Items across all Plans in your Product must have the same default currency configured. You can have prices in multiple currencies (USD, GBP, EUR, etc.), but one must be marked as default, and that default must be consistent across all Line Items. If your Products have different default currencies, you must provide an explicit currency when creating Carts that include Plans from multiple Products. ### Validation Before Checkout Before generating a checkout URL, validate that the Cart has at least one item and that all quantities are within acceptable ranges. This prevents errors during the Stripe checkout session creation. ### Multiple Plans in One Purchase Let customers add multiple Plans to their Cart in a single purchase. This works well for base Subscriptions plus add-ons, or for purchasing access to multiple Products at once. Each Plan can have different Line Items and pricing structures, and Salable handles creating a single checkout from all of them. > **Important**: Each Plan can only be added to the Cart once. Attempting to add the same Plan multiple times will fail. If you need multiple quantities of an item, use the quantity parameter on the Line Item instead. ### Handling Checkout Failures Not all checkout sessions result in completed payments. Customers might abandon the Stripe checkout page, their payment might fail, or they might close the browser. Keep Carts in **active** status until you receive webhook confirmation of successful payment, so customers can return to their Cart and try checking out again. ## Troubleshooting ### Cart Creation Fails with 400 Error If Cart creation fails with a 400 error, check that the currency code is valid and recognized by Stripe. Common valid codes include USD, GBP, EUR, CAD, AUD, and many others. The currency must be supported by your Stripe account's configuration. ### Cannot Add Item: Plan Already in Cart Each Plan can only be added to a Cart once. If you need different configurations of the same Plan (like different seat counts), you'll need to use different Plans rather than adding the same Plan multiple times. Alternatively, update the Cart item's metadata with the new quantities rather than adding a duplicate. ### Cannot Add Item: Invalid Metadata for Metered Line Item If you include a metered Line Item in your Cart item metadata, the request will fail with an error. Metered Line Items track usage after Subscription creation and should never be included in the metadata object. Only include non-metered Line Items where you need to set quantities above their configured minimum. ### Cannot Add Item: Owner Already Subscribed to Metered Item If you try to add a metered Line Item to a Cart and the Owner already has an active Subscription with that same meter slug, the request will fail. This prevents duplicate usage tracking which would cause billing issues. To resolve this, the customer would need to either cancel their existing Subscription with that meter or choose a different Plan. ### Checkout Link Generation Fails: Missing URLs If you don't provide `successUrl` and `cancelUrl` in the checkout request and the Plans in the Cart don't have Product-level defaults configured, the request will fail. Always either configure these URLs in your Product settings or provide them explicitly in the checkout API call. ### Checkout Link Generation Fails: No Payment Integration or Missing Business Information If checkout link generation fails with an error like "In order to use Checkout, you must set an account or business name," your Stripe account is missing required information. **For Test Mode:** You must complete both the **business type** form and the **personal details** form in Stripe's onboarding. Navigate to Payment Integrations in your dashboard, access your Stripe Connect settings, and complete both required forms. You don't need to complete full onboarding (banking, identity verification) for test mode checkout links. **For Live Mode:** You must have a fully onboarded Stripe account with **Active** status. This includes business information, banking details, and identity verification. Navigate to Payment Integrations to check your onboarding status and complete any pending requirements. ### Checkout Link Generation Fails: Currency Default Mismatch If you created a Cart without specifying a currency and checkout fails with a Stripe error about currencies, this means the Line Items in your Cart have different default currencies. For example, one Plan's Line Items might default to USD while another Plan's Line Items default to GBP. To resolve this, either configure all your Line Items to use the same default currency, or create the Cart with an explicit currency that all Line Items support. When using explicit currency, Salable will cherry-pick only the Line Items with that currency, avoiding the conflict. ### One-Off Items Not Appearing in Cart If your one-off Line Items aren't being included when you add a Plan to the Cart, check the currency configuration. One-off Line Items are automatically included regardless of the Cart's interval, but they still need to have pricing configured in the Cart's currency. If the Cart has an explicit currency set, ensure your one-off Line Items have a Price in that currency. And, if using cart geolocation for the currency, ensure the default currency of any one-off line items match the default currency of all other items in the cart. ## Summary Salable's Cart system lets you build checkout experiences for both anonymous and authenticated users. You can add multiple Plans with custom quantities and generate Stripe Checkout sessions with a single API call. Salable handles Owner management, validates quantities and compatibility, and creates all necessary grantee groups during checkout. The Cart system supports both recurring subscriptions and one-off purchases. For recurring billing, specify an interval like `month` or `year`. For one-off purchases, set `interval` and `intervalCount` to `null`. Plans can combine both one-off and recurring Line Items, Salable automatically includes one-off items regardless of the Cart's interval. Choose explicit currency selection for precise control or geolocation-based pricing to show customers prices in their local currency. The anonymous-to-authenticated flow reduces friction in your signup process, letting users explore and configure their purchase before creating an account. --- ### Metered Usage Source: https://salable.app/docs/metered-usage # Metered Usage Fixed subscription fees don't fit every business model. When your costs scale with customer activity (API calls, storage, processing time), you need pricing that reflects consumption. Metered billing charges customers for what they use. ## How Metered Billing Works You define a **[Meter](/docs/core-concepts#meter-slug)** for each type of usage you want to track: `api_calls`, `storage_gb`, `messages_sent`. Meters belong to your organisation and can be reused across Plans at different rates. Your Basic Plan might charge $0.01 per API call, while Pro charges $0.005. When a customer subscribes to a Plan with a **[Metered Line Item](/docs/core-concepts#metered-line-item)**, Salable creates a **[Usage Record](/docs/core-concepts#usage-record)** to track their consumption. You record usage via the API using the **[Owner](/docs/core-concepts#owner)** identifier and Meter slug throughout the billing period. At period end, Salable finalises the count, calculates charges, and generates an Invoice. ``` Subscription Created └─ Usage Record created (count: 0) │ ├─ You record usage → count increments ├─ You record usage → count increments │ Billing Period Ends └─ Usage Record finalised └─ Charge calculated → Invoice generated └─ New Usage Record created (count: 0) ``` The Owner is typically a user ID for individual Subscriptions, or an organisation/team ID for shared Subscriptions. Usage is tracked at the Owner level regardless of how many Grantees access the Subscription. ## Setting Up Metered Billing ### Adding Metered Line Items to Plans You can create Meters inline while building Plans. In the Product editor, navigate to the Plan and click Add Line Item. Fill in a descriptive Line Item Name, such as "API Usage". This appears on Stripe Invoices, so use customer-facing language. Leave Interval Type set to Recurring since metered charges repeat each billing cycle. Under Pricing Type, select Metered. Choose your Billing Scheme: Per Unit multiplies the Price by the usage quantity, while Tiered offers volume-based or graduated pricing based on usage levels. In Select Meter, choose an existing Meter or create one by typing the slug you want (like `api_calls` or `photo_generations`) and clicking Create. The Meter registers with Salable and Stripe immediately. ### Configuring Metered Pricing After selecting your Meter, set up pricing for each billing Interval and Currency. Click Add Price and select your Billing Interval (Month, Year, etc.). Click Add Currency and choose your Currency (USD, GBP, EUR, etc.). For per-unit pricing, enter the Unit Amount as Price per unit. To charge $0.01 per API call, enter `0.01`. For tiered pricing, configure each tier with its range and pricing: First Unit is auto-calculated from the previous tier, Last Unit is the upper limit or `inf` for the final tier, and Unit Amount is the Price per unit. Optionally add a Flat Amount as a base fee for reaching each tier. **Example Per-Unit Pricing:** ``` Meter: api_calls Price: 0.01 (= $0.01 per call) Usage: 5,000 calls Charge: 5,000 × $0.01 = $50.00 ``` **Example Graduated Tiered Pricing:** ``` Tier 1: 1–10,000 calls at $0.01 each Tier 2: 10,001–50,000 calls at $0.008 each Tier 3: 50,001+ calls at $0.005 each Usage: 60,000 calls Charge: (10,000 × $0.01) + (40,000 × $0.008) + (10,000 × $0.005) = $470.00 ``` ## Recording Usage ### API: Record Usage Record usage returns `204` immediately while processing happens in the background, so you can track millions of events without slowing your application. **Endpoint:** `POST /api/usage/record` **Request Body:** ```json { "owner": "company_acme", "meterSlug": "api_calls", "increment": 50 } ``` **Parameters:** - **owner**: identifier for the entity being charged (user ID, organisation ID, or team ID) - **meterSlug**: Meter identifier to increment (must match a Meter in the customer's Subscription) - **increment**: amount to add to the usage counter (minimum 1) **Headers:** - **idempotency-key** (optional): unique key to prevent duplicate increments; requests with the same key are deduplicated within 24 hours. Use a V4 UUID or similar unique string. **Response:** Returns `204 No Content` on success. No response body. > **Note** The Usage Record must exist, which happens automatically when a customer subscribes to a Plan with that Metered Line Item. Recording usage for an `owner` and `meterSlug` combination without an active Subscription returns `404 Not Found`. ### Implementation Example ```javascript import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); await salable.api.usage.record.post({ owner, meterSlug, increment }); // Record after processing a request app.post('/api/analyze-image', async (req, res) => { const result = await analyzeImage(req.body.imageUrl); await recordUsage(req.user.organizationId, 'api_calls', 1); res.json(result); }); ``` For high-volume scenarios, increment batches locally (every minute or every 100 calls), then record the accumulated total. ## Retrieving Usage Data ### API: List Usage Records **Endpoint:** `GET /api/usage-records` **Query Parameters:** - `owner` (required): Owner identifier - `meterSlug` (required): Meter slug to query - `status` (required): One or more statuses, comma-separated (`recorded`, `current`, `final`) - `before` (optional): Cursor for pagination (previous page) - `after` (optional): Cursor for pagination (next page) - `sort` (optional): Sort order, `asc` or `desc` - `limit` (optional): Number of records to return, 1-100 (defaults to 25) **Example Request:** ```bash GET /api/usage-records?owner=company_acme&meterSlug=api_calls&status=current ``` **Example Response:** ```json { "type": "list", "data": [ { "id": "UsageRecord_01HXXX", "organisation": "org_xxx", "status": "current", "count": 5432, "recordedAt": null, "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T14:23:11Z" } ], "previousCursor": null, "nextCursor": null, "hasMore": false } ``` **Response Fields:** - **status**: record state (`recorded`, `current`, or `final`) - **count**: accumulated usage for this period - **recordedAt**: null for current records; set when finalised Query with `status=current` for real-time usage dashboards, or `status=final` to retrieve historical billing periods. Results are sorted by creation date in ascending order; use `nextCursor` and `previousCursor` for pagination. ## Billing Cycle Behaviour Metered usage follows the Subscription's billing cycle. If a customer subscribes on January 15th with monthly billing, usage periods run January 15th to February 15th, then February 15th to March 15th. At period end, Usage Records transition from `current` to `final` status. The accumulated count is used to calculate charges and generate an Invoice. The counter resets to zero, and a new Usage Record is created for the next period. ### Immediate Finalisation Certain mid-cycle changes trigger immediate finalisation and a prorated Invoice: - A Plan with Metered Line Items is removed from a Subscription - A Subscription is cancelled immediately (not at period end) - A Subscription's billing anchor changes For end-of-period cancellation, usage accumulates normally until period end, then finalises on schedule. --- With Meters attached to Plans, per-unit or tiered pricing configured, and usage recording in place, Salable handles the billing cycle, finalisation, and Invoice generation. For more on configuring Products and pricing models, see the [Products & Pricing guide](/docs/products-and-pricing). For managing team access and understanding how Owners scope usage data, see [Grantees & Groups](/docs/grantee-groups). --- ### Dashboard & Admin Source: https://salable.app/docs/dashboard-and-admin # Dashboard & Admin ## Overview The Salable dashboard gives you and your team a visual interface for configuring products, monitoring subscriptions, and tracking revenue. This guide walks you through the major areas of the dashboard, explaining not just what each section does but when and why you'd use it. Understanding these workflows helps you operate your subscription business efficiently and respond quickly to customer needs. ## Organization Management Organizations are the top-level container for everything in Salable. All your products, plans, subscriptions, and settings belong to your organization. ### Creating and Switching Organizations When you first sign up for Salable, an Organization is created for you. Your account can belong to multiple organizations, which is useful if you manage several businesses or work with clients who each have their own Salable account. The organization switcher in the sidebar shows your current organization and lets you switch to others. Switching is instant: the entire dashboard updates to show that organization's data, settings, and resources. Each organization has its own API keys, Stripe connection, products, and customer base. You can't accidentally modify one business's products while viewing another's subscriptions. ### Organization Settings Organization settings control how your account operates. Access them through the sidebar: **Organization Name** Your organization's name appears throughout the dashboard and in some customer-facing contexts. Choose a name that clearly identifies your business. **Team Management** View and manage team members who can access your organization's data and settings. **API Keys** Access your API keys for both test and live modes, which your application uses to integrate with Salable. **Webhook Endpoints** Configure webhook endpoints where Salable sends real-time event notifications about subscription changes. **Stripe Integration** Manage your Stripe connection for processing payments in live mode. ### Team Management Organizations support multiple team members, so your team can access the tools they need without sharing login credentials. Invite team members by entering their email address in the team management section. They receive an invitation email with a link to join your organization. If they don't already have a Salable account, they'll create one as part of accepting the invitation. Team members can view and modify products and plans, access subscription data, manage customer support issues, and configure organization settings based on their permissions. Your support team can investigate subscription issues while your product team configures new pricing tiers, all from their own accounts. When team members leave your organization, remove them from the team to revoke their access immediately. ## Test Mode and Live Mode The dashboard operates in two modes that mirror the API's test and live environments. ### Understanding Mode Separation Test mode and live mode maintain completely separate data. Products created in test mode don't appear in live mode and vice versa. Subscriptions, customers, usage records, and all other data are similarly isolated. You can experiment with pricing models, test integration flows, and validate changes in test mode without affecting real customers or processing payments. Once you've tested changes thoroughly, replicate them in live mode. The mode toggle in the sidebar shows your current mode and switches with a single click. The entire dashboard updates to show that environment's data. A colored banner or badge always indicates which mode you're in, preventing accidental changes to production data. ### Working in Test Mode Use test mode during initial setup, development, and when planning pricing changes: **Product Configuration** Create products and plans that mirror your intended live mode structure so tests accurately represent production behavior. **Checkout Testing** Test the complete checkout flow using Stripe's test cards. Verify payments process correctly and customers are redirected to the right pages. **Subscription Verification** Create subscriptions and verify that grantees receive the correct entitlements based on their subscription. **Modification Testing** Experiment with subscription modifications and cancellations. Test upgrades, downgrades, quantity changes, and the various proration options. **Webhook Integration** Test webhook delivery and verify your application handles subscription events correctly. Test mode connects to Stripe's test environment, which accepts special test card numbers. These test cards let you simulate successful payments, failed payments, authentication requirements, and other scenarios without moving real money. Changes in test mode never affect your live customers. You can try different pricing structures, test edge cases, and refine your subscription flows without risk to production. ### Transitioning to Live Mode Once you've tested your setup and integrated your application with the test mode API, you're ready to transition to live mode. First, switch to live mode in the dashboard. Your product catalog will be empty because test and live mode data are separate. Recreate your products and plans in live mode, matching the structure you finalized in test mode. Update your application to use live mode API keys instead of test keys. This typically means changing environment variables or secrets management configuration. Verify your application points to live mode endpoints and handles live mode webhooks correctly. Complete the Stripe onboarding process for your live mode organization. Test mode doesn't require full Stripe setup, but live mode does before you can process real payments. Follow the Stripe Connect flow from the dashboard to provide your business information, banking details, and identity verification. Test your first real subscription using a small amount or a test purchase by a team member before announcing to customers. ### Maintaining Both Environments After launching in live mode, keep your test mode environment as a staging area. When planning new features, pricing updates, or integration changes, prototype them in test mode first. ## Product and Plan Management The product and plan configuration interface lets you build pricing structures visually. The underlying configuration is also available in YAML format for infrastructure-as-code workflows. ### Product Creation Access the products section from the dashboard sidebar to view your product catalog. In test mode, this starts empty. In live mode, it shows your active products and any archived products you've deprecated. Creating a product begins with the **Create Product** button. Enter a product name and click create. The product appears in your catalog immediately with default settings. The product name appears in the dashboard, in API responses, and in customer-facing areas depending on your integration. Choose names like "SaaS Platform" or "Analytics Add-on" rather than internal codes or abbreviations. Once created, click the product to open the detailed configuration editor. This is where you define plans, configure settings, and build out your complete pricing structure. ### Product Settings Configuration Product settings apply to all plans within the product, providing defaults for checkout and billing behavior. You can override these per-checkout through the API. **Checkout URLs** define where customers go after the payment process. The success URL is where they land after subscribing, typically your application's welcome page or dashboard. The cancel URL is where they return if they abandon checkout, often your pricing page. These URLs can include query parameters to pass information back to your application. For example, `https://yourapp.com/welcome?plan=pro` lets your application know which plan the customer selected, even though the subscription information also arrives via webhook. **Payment Collection Settings** Control various aspects of the checkout experience: - **Promotional Codes**: Enable to add a field where customers can enter discount codes you've created in Stripe - **Automatic Tax**: Enable Stripe Tax for jurisdiction-appropriate tax calculation (requires opting in to Stripe Tax in your Stripe Dashboard) - **Billing Address Collection**: Display billing address fields during checkout, required for tax calculation and useful for invoicing - **Shipping Address Collection**: Display shipping address fields during checkout, useful for physical goods that require delivery **Card Storage Preferences** Controls how payment methods are saved: - **None**: Don't save payment methods. Customers must enter payment details for each purchase - **Choice**: Let customers decide whether to save their card during checkout - **Always**: Automatically save payment methods for streamlined future payments and subscription renewals **Entitlement Access During Payment Issues** The past due entitlements setting controls feature access when payments fail: - **Enabled**: Customers retain access to their entitlements while Stripe retries payment - **Disabled**: Entitlements are revoked immediately when payment fails The right choice depends on your business model and tolerance for providing service without confirmed payment. ### Creating and Configuring Plans Plans represent pricing tiers, add-ons, or options within a product. The plans section of the product editor shows existing plans and lets you create new ones. Click **Create Plan** to open the plan configuration interface. This guided flow walks you through the essential elements: plan name, trial period, tier tags, entitlements, and line items. The plan name appears in checkouts, invoices, and your customer portal. Use clear names like "Professional" or "Enterprise" rather than codes. Trial periods give customers free access for a specified number of days before charging them. Enter the number of days (between 1 and 730) or leave it blank for no trial. During trials, customers have full access to entitlements but aren't charged. If they cancel before the trial ends, they're never billed. [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. This is useful for subscription tiers where customers should choose one option (Basic, Pro, or Enterprise) rather than subscribing to multiple. Enter the tier tag string in the Tier Tag field, plans that share the same value belong to the same tier set. Entitlement selection determines which features customers on this plan can access. The typeahead input lets you search for existing entitlements or create new ones inline. Type the entitlement name and click **Create** if it doesn't exist yet. Name entitlements in lowercase with underscores, like `advanced_analytics` or `unlimited_exports`. ### Line Item Configuration Line items define the charges within a plan: flat fees, per-seat charges, usage-based billing, or combinations. Click **Add Line Item** to open the line item editor. Start by selecting the line item type: flat rate for fixed recurring fees, per-seat for team-based pricing, metered for usage-based billing, or one-time for setup fees and single charges. **For flat rate line items**, configure the billing interval (monthly, yearly, or custom intervals), the price in each currency you support, and optionally any tiered pricing if you want volume-based discounts even for flat fees. **For per-seat line items**, the min seats setting enforces a minimum purchase quantity, useful for team plans requiring at least 5 seats. The max seats setting caps the maximum quantity. Variable seating allows customers to choose their quantity within these bounds, while fixed seating sells plans with a predetermined number of seats. Tiered per-seat pricing lets you offer volume discounts. Configure tiers with breakpoints like 1–10 seats at $10 each, 11–50 seats at $8 each, and 51+ seats at $6 each. Customers automatically get the appropriate tier pricing based on their quantity. **For metered line items**, configure the slug that identifies this usage type, the price per unit, and the aggregation method. The slug is what you provide when recording usage through the API, and it must be unique across your organization. Use descriptive slugs like `api-calls` or `storage-gb`. Each line item can have prices for multiple billing intervals and currencies. The editor shows tabs or sections for monthly, yearly, and custom intervals. Within each interval, specify prices in multiple currencies with one marked as default. ### YAML Import and Export The dashboard supports YAML import and export for infrastructure-as-code workflows or replicating products across environments. Click **Export to YAML** on any product to download a configuration file with all plans, line items, prices, and settings. You can version-control this file, share it for review, and use it to document your pricing structure. The **Import from YAML** feature lets you upload a configuration file to create or update products. This is useful when transitioning from test mode to live mode: export your tested configuration, review it, and import it into live mode rather than recreating everything manually. YAML import validates the configuration before applying changes, catching missing required fields, invalid prices, or references to non-existent entitlements. If validation fails, error messages tell you what to fix. ### Managing Entitlements The entitlements section in the sidebar shows all entitlements across your organization, including those shared across multiple products and plans. Create standalone entitlements here if you prefer to define your entitlement structure before building products, or manage entitlements that were created inline during plan configuration. Each entitlement has a name (the slug used in API checks), a description for team reference, and an optional expiry date for automatic revocation. The interface shows which plans include each entitlement, so you can audit feature access across your pricing tiers. Archive entitlements you no longer use rather than deleting them. Archived entitlements don't appear in plan configuration interfaces but remain in the system so historical data remains consistent. If subscriptions still reference an archived entitlement, those customers retain access until their subscription changes. ## Subscription Management The subscriptions section is where you monitor subscription health, investigate customer issues, and make administrative changes. ### Subscription List and Filtering The subscriptions list shows all subscriptions in the current mode, with filtering and search to find specific subscriptions quickly. The default view shows active subscriptions sorted by creation date. Filter by status (active, past_due, canceled, trialing), search by customer email or owner ID, filter by product or plan, and sort by creation date, next billing date, or subscription value. These filters combine. You can find "all past_due subscriptions for the Pro plan" or "all subscriptions created in the last week." Each subscription in the list shows key information at a glance: the customer's email or owner ID, current status, subscription plans, and the next billing date. Click any subscription to open the detailed view. ### Subscription Detail View The subscription detail page shows full information about a single subscription and provides tools for modifications and issue resolution. The summary section shows the customer information including owner ID and contact details if available, current subscription status and any scheduled changes (like upcoming cancellation), creation date and subscription age, and the next billing date and amount. The subscription items section lists all plans included in the subscription. For each item, you see the plan name and details, quantity (for per-seat plans), associated grantee group if applicable, and the price being charged per interval. The billing history section shows all invoices for this subscription. Each entry displays the billing date, amount charged, payment status (paid, failed, or pending), and a link to download the PDF receipt. For subscriptions with metered line items, the usage section shows current period usage. You can see recorded usage quantities, the calculated charges based on usage, and historical usage from previous billing periods. ### Making Subscription Modifications The subscription detail page includes actions for modifying the subscription without API calls. **Change Plan** opens an interface for upgrading or downgrading the customer. Select the new plan, choose the proration behavior (immediate charge, end of period change, or no proration), and apply the change. The interface shows a preview of any prorated charges before you confirm. **Adjust Quantity** modifies the seat count for per-seat plans. Enter the new quantity, see the prorated amount that will be charged immediately, and confirm the change. The system enforces minimum and maximum seat limits configured on the line item. **Add Plans** lets you attach additional plans to the subscription, useful when customers want to add features or add-ons. Select the plan to add, specify the quantity, and choose proration behavior. The new plan becomes part of the subscription and bills on the same cycle. **Cancel Subscription** offers both end-of-period and immediate cancellation options. End-of-period cancellation schedules termination for the current billing period's end, so the customer retains access for the time they've paid for. Immediate cancellation terminates the subscription right away, with optional prorated refunds. These are the same operations available through the API, presented with visual feedback for support scenarios. ### Webhook Configuration Configure webhook endpoints to receive real-time notifications of subscription events. You can add multiple endpoints, each with its own URL and event type selection. Enter the webhook URL where Salable should send events. This must be a publicly accessible HTTPS endpoint. Select which event types this endpoint should receive. You might send all events to one endpoint or route different event types to different handlers. After creating a webhook endpoint, the dashboard shows the signing secret you need to verify webhook authenticity. Copy this secret into your application's environment configuration. The webhook detail view shows recent deliveries: which events were sent, whether delivery succeeded or failed, and the full request and response. If deliveries are failing, you can see the exact error and manually retry individual webhooks. Test your webhook endpoints using the **Send Test Event** button. This sends a sample event without creating the associated resource, so you can verify your endpoint works before real events occur. ### API Keys and Access Tokens Your personal access tokens for dashboard API usage appear in your user settings (organization-level API keys are in organization settings). Use these tokens for scripting dashboard operations or building custom tooling. Generate personal access tokens with appropriate scopes, name them descriptively, and revoke them when no longer needed. Store them securely and never commit them to version control. --- ### Testing & Development Source: https://salable.app/docs/testing-and-development # Testing & Development ## Overview Before handling real payments, verify that your Salable payment model works as intended. Salable's [Test Mode](/docs/core-concepts#test-mode-vs-live-mode) provides a sandbox environment where you can experiment, validate your Payment Integration, and test your Subscription flows without affecting customers. Test Mode is a fully functional parallel environment with its own Products, Subscriptions, API keys, and data. You can develop new features, test edge cases, or validate changes without touching production. Test Mode integrates with [Stripe's test infrastructure](https://docs.stripe.com/testing-use-cases) for realistic payment simulation. This guide covers testing strategies from initial integration development through production deployment. ## Test Mode Fundamentals ### Complete Environment Isolation Test Mode and Live Mode are separate environments. A Product created in Test Mode exists only in Test Mode. This isolation extends to every resource: Plans, Subscriptions, Carts, Usage Records, Entitlements, Grantee Groups etc. You can create test Products with experimental pricing, simulate Subscription lifecycles, test edge cases that would be difficult to reproduce in production, and validate integration changes with no risk to real customers. The only difference between Test Mode and Live Mode is that Test Mode does not process real money. All payments use Stripe's test environment. > **Note** Test Mode API keys can only access Test Mode data. Webhooks are also segregated: test events go to test webhook endpoints, production events to production endpoints. ### Stripe Requirements Both Test and Live Mode require a Stripe account connected through Payment Integrations. Once connected, you can build your pricing structure (creating Products, configuring Plans, and setting up Line Items) even with minimal Stripe account setup. To generate checkout links in Test Mode, complete two forms in the Stripe Connect onboarding process: the **business type** form and the **personal details** form. Without both forms, Stripe rejects checkout link generation with an error. Full onboarding (banking details, identity verification) is not required for Test Mode checkout links. Live Mode requires full Stripe Connect onboarding. You can create Products and Plans with incomplete onboarding, but generating checkout links requires your Stripe account to be fully onboarded with **Active** status, including banking information and identity verification. Stripe's test environment accepts special test card numbers that simulate different scenarios. The card number `4242 4242 4242 4242` always succeeds. The number `4000 0000 0000 0002` always declines. Other test cards simulate specific scenarios like authentication requirements, insufficient funds, or expired cards. [Read about Stripe test environments here](https://docs.stripe.com/testing-use-cases). ## Setting Up Your Test Environment ### Initial Configuration Check the mode indicator in the dashboard sidebar. If you are in Live Mode, switch to Test Mode before proceeding. Start by creating your Product(s) how you would intend to on Live Mode. This may be a single Product if you are testing a simple Subscription model, or multiple Products if you offer different services or add-ons. Next configure Plans for your test Products with the pricing models you have in mind. For example a Basic Plan at $29/month, a Pro Plan at $79/month, and a Premium Plan at $99/month. Create and include the same Entitlements you will use in production so you can test feature access correctly. Keep your test data accurate so you are simulating a production-level environment. ### Test API Keys Generate test API keys from the organization settings in the dashboard. These keys grant access only to Test Mode data. Store test API keys as environment variables. Use separate configuration for development, staging, and production environments so each uses the appropriate API keys and endpoints. ```bash # .env.development SALABLE_API_KEY=test_abc123... SALABLE_BASE_URL=https://salable.app/test # .env.production SALABLE_API_KEY=live_xyz789... SALABLE_BASE_URL=https://salable.app/live ``` This configuration separation prevents accidentally using production keys during development or test keys in production. ### Webhook Configuration for Testing Configure test webhook endpoints that point to your development or staging servers. For local development, tools like [ngrok](https://ngrok.com/) provide public URLs that forward to your local server, allowing Salable to deliver webhooks to your development environment. ```bash # Start ngrok to expose local port 3000 ngrok http 3000 # Use the ngrok URL as your test webhook endpoint # Example: https://abc123.ngrok.io/webhooks/salable ``` Configure this ngrok URL as a test webhook endpoint in the Salable dashboard. Select the event types you want to receive, and save the signing secret for verifying webhook authenticity. The dashboard's webhook delivery log shows each event sent to your endpoint, the payload, your endpoint's response, and whether delivery succeeded. ## Testing Checkout Flows ### Basic Subscription Purchase Start by testing a customer subscribing to a Plan. This validates your checkout integration end-to-end. Create a Cart in your application using your test API key. Add a Plan to the Cart, generate a checkout link, and navigate to it in your browser. Stripe's checkout page loads in Test Mode, indicated by a "Test Mode" badge. Enter test payment information using Stripe test cards. Use the card number `4242 4242 4242 4242`, any future expiry date, any CVC, and any postal code. Click the payment button. Stripe processes the test payment instantly and redirects you to your configured success URL. Shortly after, your webhook endpoint receives a `Subscription.created` event. Verify the Subscription appears in the Salable dashboard under Subscriptions. Check that your application provisioned access correctly. The customer should have the Entitlements associated with their Plan. ### Testing Payment Failures Payment failures are inevitable, so test that your application handles them gracefully. Stripe provides test card numbers that simulate failed or declined payments. Use card number `4000 0000 0000 0002`, which always declines with a generic decline code. Use card number `4000 0000 0000 9995`, which declines with an "insufficient funds" error. Test authentication requirements using card number `4000 0025 0000 3155`, which requires 3D Secure authentication. Complete the authentication challenge in the test flow to verify your checkout process handles it correctly. ### Testing Different Currencies If you support multiple currencies, test the checkout flow with each one. Create Carts specifying different currencies and verify that checkout displays amounts correctly, Stripe processes payments in the expected currency, and Subscriptions are created with correct pricing. ### Team Subscription Checkout For applications with team-based Subscriptions, test the team onboarding flow. Create a Grantee Group and add members to it. Next create a Cart with a per-seat Plan, assign the Grantee Group to the Cart item with the appropriate quantity. Complete checkout and verify that all team members receive appropriate Entitlements. Test adding members after Subscription creation to validate seat management works correctly. ## Testing Subscription Management ### Upgrade and Downgrade Flows Test Plan upgrades and downgrades with the Subscriptions you just created. In your application's Subscription management interface, trigger a Plan change from Basic to Pro. Verify that the API request succeeds, proration charges are calculated correctly, Entitlements update immediately to reflect the new Plan, and your application UI shows the new Plan. ### Quantity Adjustments For per-seat Plans, test increasing and decreasing seat quantities. Verify that quantity increases succeed and charge prorated amounts, quantity decreases respect minimum seat requirements, attempts to reduce below minimum seats are rejected, and Grantee Group size constraints are enforced if applicable. ### Adding and Removing Plans Test adding add-ons or additional Products to existing Subscriptions. Create another Subscription for one of your test Products, then use the Subscription modification API to add an add-on or another Product. Verify the new Plan appears on the Subscription, is included in invoices, and grants its Entitlements to the customer. Test removing Plans by selecting a Subscription item and deleting it. Verify that the Plan is removed, associated Entitlements are revoked, and the customer is not charged for the removed Plan in future billing cycles. Verify that attempts to remove the last Plan from a Subscription are rejected. Subscriptions must always have at least one Plan. ### Cancellation Testing Test both types of cancellation to verify your implementation handles each correctly. Cancel a Subscription with end-of-period timing and verify that it remains active until the billing period ends, the customer retains access until the scheduled cancellation date, no refund is issued, and your application shows the upcoming cancellation to the customer. Test immediate cancellation and verify that the Subscription terminates right away, Entitlements are revoked immediately, a prorated refund is issued if configured, and your application handles the sudden access revocation appropriately. Test reversing scheduled cancellations by resuming a Subscription after scheduling end-of-period cancellation. Verify that the Subscription continues to auto-renew normally. ## Testing Metered Usage ### Recording Usage If you have metered Line Items, test that usage is recorded correctly. In your application, trigger actions that should record usage: making API calls, consuming storage, or whatever your metered metric tracks. Verify that usage recording API calls succeed, the correct quantity is recorded, and the slug correctly identifies the usage type. Usage increments accumulate by default. To test idempotency, send requests with the same `idempotency-key` header value; duplicate requests within 24 hours are deduplicated. Without the header, repeated calls increment the usage counter each time. Test usage recording across multiple billing periods. Record usage, wait for the Subscription to bill (or manually advance time in your test), and verify that usage records finalize and appear on the invoice. ### Checking Current Usage Test your application's usage display by retrieving current usage counts through the API and displaying them to customers. Verify that counts are accurate, update in real-time as usage is recorded, and reset appropriately at the start of new billing periods. If your application enforces usage limits, test that these limits work correctly. Record usage up to the limit and verify that your application prevents further usage or prompts the customer to upgrade. ### Cross-Plan Metering If you use the same metered slug across multiple Plans (such as shared API call tracking for Basic and Pro Plans at different per-unit prices), verify that usage recorded against one slug appears correctly on invoices for Subscriptions with different Plans, each customer is charged at their Plan's rate, and a single usage counter tracks consumption regardless of which Plan the customer has. Shared metering requires careful testing to verify billing calculates correctly. ## Testing Entitlements and Access Control ### Entitlement Checking Verify that your customers have access to the correct features through Entitlements. Call the Entitlement check API with a Grantee ID and Entitlement name, then confirm the response matches the user's Subscription status. Test positive cases: verify that subscribed users with the appropriate Plan receive positive Entitlement checks. Test negative cases: verify that unsubscribed users, users on Plans without the Entitlement, and users with cancelled Subscriptions receive negative Entitlement checks. Test timing boundaries by checking Entitlements immediately after Subscription creation, right before Subscription expiry, immediately after cancellation, and during trial periods. These boundary conditions surface timing bugs that grant or deny access incorrectly. ### Grantee Group Access For team Subscriptions, test that all members of a Grantee Group receive Entitlements from the group's Subscription. Add a user to a group associated with a Subscription, check that user's Entitlements, and verify they have access to features granted by the group Subscription. Test removing users from groups and verify that their Entitlements update appropriately. If they have no other Subscriptions granting those Entitlements, they should lose access immediately. Test users who belong to multiple Grantee Groups with different Subscriptions. Verify that they receive the union of Entitlements from all groups. If one group Subscription grants `feature_a` and another grants `feature_b`, the user should have both. ### Entitlement Expiry If you use Entitlements with expiry dates, test that access is granted before expiry and denied after. Create Entitlements with expiry dates in the near future, then check access before and after the expiry timestamp. ## Testing Webhook Handling ### Event Processing Trigger each event type in Test Mode and verify your handler processes it correctly. Create Subscriptions to trigger `subscription.created` events. Verify your handler provisions access, updates your database, or performs whatever initialization your application requires for new Subscriptions. Modify Subscriptions to trigger `subscription.updated` events. Verify your handler detects the changes and updates your application state accordingly. Cancel Subscriptions to trigger `subscription.cancelled` events. Verify your handler revokes access and updates Subscription status in your system. Use Stripe test cards that simulate payment failures (like `4000 0000 0000 0002`) to verify your application handles declined payments gracefully. Note that `payment.failed` is a Stripe webhook event, not a Salable event. Configure Stripe webhooks separately if you need to handle payment failures directly. ### Idempotency Testing Webhook deliveries can be duplicated, so your handlers must be idempotent. Test by resending the same webhook event multiple times through the dashboard's webhook delivery interface. Verify that processing the same event multiple times has the same effect as processing it once: no duplicate records, no double provisioning, no multiple notifications. The event ID included in webhook payloads is your tool for implementing idempotency. Store processed event IDs and skip events you have already seen. ### Error Handling Test how your webhook handler behaves when processing fails. Introduce errors in your handler code (for example, by commenting out database access temporarily) and observe webhook deliveries failing. Verify that Salable retries failed deliveries according to the documented retry schedule. Check the dashboard's webhook delivery log to see retry attempts. Fix the error in your handler and verify that a subsequent retry succeeds. This verifies your production system can recover from transient issues without losing events. ### Webhook Security Verify that your webhook handler validates signatures. Send a webhook with an invalid signature (modify the signature header before processing) and confirm your handler rejects it with a 401 response. Send a webhook with no signature and verify rejection. Send a webhook with the correct signature and verify acceptance. ## Performance and Load Testing ### Rate Limit Verification Verify that your application handles rate limits gracefully. Send more than 100 requests per second to the API and observe the 429 responses. Verify that your client implements exponential backoff and retries. Test that your application continues to function under rate limiting: requests eventually succeed, users see no errors, and your system does not enter a retry storm. ### Concurrent Operations Test scenarios where multiple operations happen simultaneously. Create several Carts concurrently, modify the same Subscription from multiple requests, record usage from multiple sources simultaneously, and check Entitlements for many users in parallel. Verify that your application handles concurrency without race conditions or duplicates. Database transactions, idempotency keys, and proper error handling are critical here. ### Large Dataset Handling If you expect many Subscriptions or high usage volumes, test with realistic data sizes. Create dozens or hundreds of test Subscriptions, record large volumes of usage, and retrieve large Subscription lists with pagination. Verify that pagination works correctly, your application does not load excessive data into memory, and performance remains acceptable with realistic data volumes. ## Integration Testing Strategies ### End-to-End Test Suites Build automated test suites that exercise flows from your application through Salable to Stripe and back. An end-to-end Subscription test might create a Cart, add a Plan, generate a checkout link, simulate completing checkout (using test cards), wait for the webhook, verify the Subscription was created, check Entitlements, and verify your application state. ### Continuous Integration Integrate your test suite into your continuous integration pipeline. Configure your CI environment with Test Mode API keys, run the full test suite on every commit or pull request, and block deployments if tests fail. ## Transitioning to Production ### Pre-Launch Checklist Before launching in Live Mode, verify your test mode data is accurate. Confirm your Test Mode integration works with all flows tested: success paths, error handling, edge cases, and webhook processing. Your Stripe Connect onboarding must be fully complete with an **Active** status. Incomplete Stripe Connect accounts prevent checkout links from working. Once your test Products are accurate, navigate to the Product configuration page and click the Copy to Live Mode button. This copies your Product and all corresponding data (Plans, Line Items etc) to Live Mode. Update your application configuration to use Live Mode API keys and endpoints. Configure production webhook endpoints with appropriate URLs and verify they are reachable. ### Gradual Rollout Consider launching to a limited audience first. A limited rollout lets you verify production behaviour with real customers and real money while limiting the impact of undiscovered issues. Monitor error rates, webhook delivery success, payment success rates, and customer feedback during initial rollout. If everything looks good, expand access to your full customer base. ### Production Monitoring Once live, monitor key metrics: Subscription creation rates and success/failure patterns, payment success rates and decline reasons, webhook delivery success and processing times, Entitlement check response times, and error rates across all API operations. Set up alerts for anomalies: sudden spikes in payment failures, webhook delivery failures, API error rates, or unexpected drops in Subscription creation. ## Common Testing Scenarios ### Free Trial to Paid Conversion Test the trial lifecycle: create a Plan with a trial period, subscribe a test customer, verify they have full access during the trial, simulate time passing (or note when the trial ends), and verify that billing occurs correctly when the trial expires. Test trial cancellation by canceling before the trial ends and verifying the customer is never charged. ### Team Onboarding Flows Test the team signup journey: an administrator creates an account, invites team members before purchasing, subscribes to a team Plan with appropriate seat quantity, and all team members receive access immediately. After purchase, test team management: adding members, adjusting seat quantities, removing members, and verifying Entitlements update correctly. ### Complex Multi-Plan Subscriptions For customers who purchase multiple Products or add-ons together, test creating Carts with multiple Plans, completing checkout, verifying all Plans appear on the Subscription, and checking that all Entitlements from all Plans are granted. Test modifying these multi-Plan Subscriptions by adding additional Plans, removing some Plans while keeping others, and changing quantities on specific Plans. ### Migration from Other Systems If you are migrating customers from another billing system to Salable, test your migration process thoroughly. Create test data representing your existing customer structures, run your migration scripts against Test Mode, verify Subscriptions are created with proper billing dates, and confirm Entitlements map correctly from old system to new. ## Debugging and Troubleshooting ### API Response Inspection When tests fail or behaviour does not match expectations, examine API responses. Error messages include error codes for programmatic handling, human-readable descriptions, and details about validation failures or constraint violations. Enable detailed logging in your application to capture API requests and responses during development. ### Dashboard Investigation The Salable dashboard provides another angle for investigating issues. View resources your API calls created or modified, check Subscription states and histories, review webhook delivery logs and event payloads, and examine usage records and invoice Line Items. The dashboard complements API logs and often makes issues visible that would be opaque from request/response data alone. ### Webhook Replay Use the dashboard's webhook replay feature to resend events to your handler. This lets you fix bugs and reprocess events without recreating entire Subscription scenarios. The replay preserves the original event ID, so your idempotency checking must allow for replays during development. You might need to clear your processed events table or add a development mode bypass. ### Support Resources If you cannot resolve an issue, contact Salable support. Include relevant Subscription IDs, API request/response details with full JSON payloads, webhook event IDs and payloads if applicable, and a description of what you expected versus what happened. This context helps support staff diagnose issues and provide specific solutions. --- Test checkout flows, Subscription management, usage tracking, Entitlements, and webhooks in Test Mode before launching. Catching issues here prevents production problems and support overhead. --- ### Caching Strategies for Entitlements Source: https://salable.app/docs/caching-strategies-for-entitlements # Caching Strategies for Entitlements ## Overview Caching entitlement checks speeds up your application and reduces API calls. This guide covers caching strategies for Node.js and Next.js applications, helping you balance performance with data accuracy. **Benefits:** - Fewer API calls and faster response times - Lower latency for feature access checks - Better user experience during high traffic - Less load on Salable's API **Trade-offs:** - Cached data may be stale during subscription changes - Cache invalidation adds complexity - Memory usage for cache storage - Consistency across distributed systems ## When to Cache ### Good Candidates for Caching - **Frequent entitlement checks**: Features checked on every page load or API request - **Stable subscriptions**: Long-term subscriptions that rarely change - **Read-heavy patterns**: More reads than subscription updates - **Session-based access**: User sessions with consistent subscription status ### Poor Candidates for Caching - **Critical security checks**: Financial transactions, admin access, sensitive operations - **Real-time subscription changes**: Immediately after upgrade/downgrade flows - **Background jobs**: Batch processes that can tolerate API latency - **Infrequent checks**: Features checked rarely don't benefit from caching ## Recommended Cache Durations Choose cache TTL (Time-To-Live) based on your application's needs: | Use Case | Recommended TTL | Rationale | | --------------------- | --------------- | ------------------------------------------ | | Short-lived sessions | 5–10 minutes | Quick invalidation for logged-out users | | Long-lived sessions | 15–30 minutes | Balance between performance and freshness | | Background jobs | No cache | Always check fresh data | | Critical features | 2–5 minutes | Shorter TTL for important access decisions | | Non-critical features | 30–60 minutes | Longer TTL for less sensitive features | **Consider your billing cycle:** - Monthly billing: Longer cache TTLs are acceptable (15–30 minutes) - Usage-based billing: Shorter TTLs to reflect consumption changes (5–10 minutes) - Perpetual licence: cache for the lifetime of the app ## Backend/API Implementation Example A simple in-memory caching pattern for your backend: ```javascript // entitlementCache.js class EntitlementCache { constructor(ttlMs = 5 * 60 * 1000) { // 5 minute default this.cache = new Map(); this.ttl = ttlMs; } get(granteeId) { const cached = this.cache.get(granteeId); if (!cached) return null; const age = Date.now() - cached.timestamp; if (age > this.ttl) { this.cache.delete(granteeId); return null; } return cached.entitlements; } set(granteeId, entitlements) { this.cache.set(granteeId, { entitlements, timestamp: Date.now() }); } invalidate(granteeId) { this.cache.delete(granteeId); } } export default new EntitlementCache(); ``` **Usage in your API/backend:** ```javascript import cache from './entitlementCache.js'; import { Salable } from '@salable/sdk'; const salable = new Salable('your-secret-key'); const cached = cache.get(granteeId); if (cached) return cached; const { data } = await salable.api.entitlements.check.get({ queryParameters: { granteeId } }); cache.set(granteeId, data.entitlements); return data.entitlements; ``` **Cache Invalidation via Webhooks:** ```javascript app.post('/webhooks/salable', async (req, res) => { const event = req.body; if (['subscription.created', 'subscription.updated', 'subscription.cancelled'].includes(event.type)) { const grantees = await getGranteesFromSubscription(event.data.id); grantees.forEach(id => cache.invalidate(id)); } res.json({ received: true }); }); ``` ## Best Practices - **Choose appropriate TTLs**: 5–15 minutes for most use cases; shorter (2–5 min) for critical features - **Invalidate on subscription changes**: Use webhook events to clear stale cache entries - **Handle errors gracefully**: Fail securely by denying access when API calls fail - **Consider distributed caching**: Use Redis or Valkey for multi-instance deployments - **Use shorter TTLs for critical features**: Financial operations, admin access, etc. ## Backend/API Caching Considerations **For single-instance backends**: In-memory caching (as shown above) is sufficient **For production/multi-instance backends**: Use Redis or Valkey (Redis-compatible) for distributed caching to ensure consistency across instances ## Frontend Caching For frontend applications, check entitlements through your own backend API endpoints rather than calling Salable directly. Your backend should implement the caching strategy above, and your frontend can use standard HTTP caching or state management libraries (React Query, SWR, etc.) to cache responses from your backend. **Important: Cache Invalidation Limitations** Frontend caches are difficult to invalidate: - Frontends don't receive webhook events when subscriptions change - You can't notify the client when entitlements change server-side - Frontends must poll or refetch from your backend to get fresh state **Recommendations for frontend caching:** - Use shorter TTLs (2–5 minutes) to reduce staleness - Refetch entitlements after user actions that might change subscriptions (_eg_, after redirecting back from checkout) - Consider manual refresh options for users ("Refresh subscription status") - Accept that some delay in reflecting subscription changes is unavoidable on the frontend Caching is an optimization. Implement what fits your architecture and scale. --- ### Webhooks Source: https://salable.app/docs/webhooks # Webhooks ## Overview **[Webhooks](/docs/core-concepts#webhook)** are HTTP callbacks that Salable sends to your application when events occur: subscription changes, usage updates, payment events. Your application receives instant notifications instead of polling. ## Understanding Webhook Destinations A Webhook Destination is an endpoint on your server that receives event notifications from Salable. Each destination has a URL where events are delivered, a unique signing secret for verifying authenticity, and a selection of event types it listens to. You can create multiple webhook destinations for different purposes. Each one operates independently with its own configuration, delivery history, and retry schedule. Every webhook destination must listen to at least one event type, but it can listen to any number of the events Salable sends. ## Available Event Types **Subscription Events** notify you when subscriptions change state: `subscription.created` fires when a customer successfully completes checkout and a new subscription has been created. `subscription.updated` fires whenever subscription details change: plan upgrades or downgrades, quantity adjustments, billing interval changes, or status transitions. `subscription.cancelled` fires when a subscription has ended, either immediately or at the end of the current billing period. **Usage Events** track metered billing activity: `usage.recorded` fires when a usage subscription cycles and the last period's usage has been processed. `usage.finalised` fires when a usage subscription has been cancelled or otherwise ends and the last usage record has been processed. **Payment Events** keep you informed about financial transactions: `receipt.created` fires when a user purchases a one-off line item and a receipt has been created. **Access Control Events** notify you about customer information updates: `owner.updated` fires when an owner's email address is updated following them successfully purchasing a plan ## Creating Webhook Destinations Create a webhook destination in the Salable dashboard to configure where events are sent, which events to send, and to generate the signing secret for verification. ### Setting Up Your First Destination Navigate to **Webhooks** and click the **Create Webhook** button. Provide the URL where events will be sent and which event types this destination should receive. **Webhook URL Configuration** Enter the full URL to your webhook endpoint in the URL field (_eg_ `https://yourapp.com/webhooks/salable`). This must be a publicly accessible HTTPS endpoint that can receive POST requests. Your endpoint should be ready to receive events before creating the destination. While Salable will retry failed deliveries, having your handler ready from the start prevents unnecessary retry cycles. **Event Type Selection** Select which event types this destination should receive. You must choose at least one event type, but you can select as many as needed. ### Signing Secret After creating your webhook destination, the dashboard displays a unique signing secret. This secret proves that incoming webhooks are from Salable and haven't been forged. Copy the signing secret and store it in your environment configuration. You'll need it to verify webhook signatures. Never commit signing secrets to version control or expose them in client-side code. > **Important** Each webhook destination has its own unique signing secret. If you create multiple destinations, you'll need to store each signing secret separately and use the appropriate one when verifying each destination's events. ## Editing Webhook Destinations You can modify webhook destinations after creation to update the URL or change which events are delivered. Find the destination in the webhooks list and click the **Edit** button to update the URL. You can also modify the event type selection. Adding new event types starts delivery immediately. Removing event types stops future delivery, though any events already in transit will still be sent. > **Note** Editing a webhook destination does not change its signing secret. The same secret remains valid, so you don't need to update your verification code when editing URLs or event types. ## Deleting Webhook Destinations Delete a webhook destination from the webhooks list when you no longer need it. Deleted destinations stop receiving events, and their configuration is removed from Salable. ## Implementing Webhook Handlers Your webhook handler receives and processes events from Salable. A good handler verifies signatures and responds quickly to avoid timeouts. ### Handler Requirements Webhook handlers must respond within 15 seconds. If your endpoint takes longer, Salable marks the delivery as failed and schedules a retry. Your handler should return a 2xx status code (typically 200 or 204) to indicate successful receipt. Any other status code indicates a failure and triggers a retry. The handler receives a POST request with a JSON payload containing the event data. Reject non-POST requests with a 405 status code. Two headers matter: `x-salable-signature` contains the HMAC signature for verification, and `x-salable-timestamp` contains the ISO 8601 timestamp when the request was sent. ### Basic Handler Structure Here's a basic webhook handler structure in Node.js: ```javascript import { createHmac, timingSafeEqual } from 'crypto'; export async function handleWebhook(req, res) { // Only accept POST requests if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { const signature = req.headers['x-salable-signature']; const timestamp = req.headers['x-salable-timestamp']; const body = req.body; // Verify timestamp and signature if (!verifySignature(body, timestamp, signature)) { return res.status(401).json({ error: 'Invalid signature' }); } const { type, data } = body; // Process the event based on type await processEvent(type, data); return res.status(200).json({ received: true }); } catch (error) { console.error('Error processing webhook:', error); return res.status(500).json({ error: 'Processing failed' }); } } ``` ### Signature Verification Every webhook request includes two headers: `x-salable-signature` (the HMAC SHA-256 signature) and `x-salable-timestamp` (the ISO 8601 timestamp when the request was sent). Salable computes the signature from the timestamp and request body using your destination's signing secret. Verification has two steps. First, verify the timestamp is recent to prevent replay attacks; reject requests older than 5 minutes. Second, compute the expected signature using your signing secret, the timestamp, and the request body, then compare it to the received signature using a constant-time comparison to prevent timing attacks. ```javascript import { createHmac, timingSafeEqual } from 'crypto'; function verifySignature(body, timestamp, signature) { const secret = process.env.SALABLE_WEBHOOK_SECRET; // Check timestamp to prevent replay attacks (5 minute window) const currentTime = new Date(); const requestTime = new Date(timestamp); const timeWindow = 5 * 60 * 1000; // 5 minutes in milliseconds if (Math.abs(currentTime.getTime() - requestTime.getTime()) > timeWindow) { return false; } // Construct the signed payload (timestamp + body) const rawBody = typeof body === 'string' ? body : JSON.stringify(body); const payload = `${timestamp}.${rawBody}`; // Compute expected signature const expectedSignature = createHmac('sha256', secret).update(payload).digest(); // Constant-time comparison to prevent timing attacks return timingSafeEqual(expectedSignature, Buffer.from(signature, 'hex')); } ``` > **Important** Always verify both the timestamp and signature before processing webhook data. The timestamp check prevents replay attacks (old requests resent maliciously). The signature check confirms requests came from Salable and haven't been tampered with. ### Timestamp Validation The `x-salable-timestamp` header contains an ISO 8601 formatted timestamp indicating when Salable sent the webhook. Validating this timestamp prevents replay attacks, where an attacker captures a legitimate webhook request and resends it later. The standard validation window is 5 minutes. Reject any request with a timestamp older than 5 minutes or more than 5 minutes in the future. This window accounts for minor clock drift between systems while protecting against replay attacks. If your webhook processing includes time-consuming operations that might exceed the 15-second timeout, ensure the timestamp validation happens before those operations. The timestamp check is fast and should always complete within the timeout window. ## Retry Behaviour and Failed Deliveries Salable retries failed webhook deliveries so your application receives events even during temporary outages. ### Automatic Retry Schedule After a delivery fails, Salable schedules a retry using exponential backoff. Salable makes up to 10 automatic retries per event. After the 10th attempt fails, automatic retries stop. You can then manually resend the event through the dashboard. ### Failed Delivery Handling Monitor the webhook delivery dashboard to track failed deliveries. Each failure includes the error message and status code returned by your endpoint. When you see failed deliveries, investigate the error message, fix the underlying issue in your handler or infrastructure, then manually resend the event. The resend feature lets you retry individual events once your endpoint is working correctly. ## Resending Events The webhook dashboard lets you manually resend pending, failed, and successful events. ### Resending Pending Events Pending events are scheduled for future delivery as part of the retry schedule. If you've fixed an issue with your endpoint and don't want to wait, resend a pending event to deliver it immediately. ### Resending Failed Events After automatic retries are exhausted, fix the issue that caused the failure, then click the **Resend** button on the failed event. Salable delivers it again immediately. ### Resending Successful Events You can also resend events that were already delivered. This helps with debugging, testing changes to your webhook handler, or replaying events to rebuild derived data. Resent events are treated as new delivery attempts. Your handler receives the event again with the same payload, so idempotent processing matters here. Your handler should recognize it has already processed this event ID and skip reprocessing to avoid duplicate effects. ## Monitoring Webhook Deliveries The webhook delivery dashboard shows all sent events for each destination, their delivery status, and detailed information about each attempt. ### Delivery History Each webhook destination has a delivery history showing every event sent to that endpoint. The history includes the event type, timestamp, current status (pending, success, or failed), and a summary of delivery attempts. Click on any event to view its full JSON payload, all delivery attempts with status codes and error messages, request and response headers, and timing information. ### Event Payloads The dashboard displays the complete payload for each generated event. ### Delivery Attempts Each event shows the delivery attempts for each destination it was sent to. If an event failed and was retried multiple times, you'll see every attempt with its timestamp, status code, and any error message. This history reveals patterns in failures. If every attempt times out after exactly 15 seconds, your handler is too slow. If attempts return 500 errors, your handler likely has a bug. If early attempts failed but later ones succeeded, correlate the success with deployments or infrastructure changes. ## Testing Webhook Handlers Test Mode provides a safe environment for webhook development without affecting production data or processing real payments. ### Test Mode Webhooks Webhook destinations created in Test Mode only receive events from Test Mode actions. When you complete a test checkout or record test usage, those events go to Test Mode webhook destinations with their associated signing secrets. Test events never reach production handlers and production events never reach test handlers. You can experiment with webhook configuration, test signature verification, and trigger failure scenarios without risk to your production system. ## Troubleshooting Common webhook issues and their solutions. ### Signature Verification Failures If signature verification fails consistently, verify you're using the correct signing secret from the webhook destination's detail page. The signed payload must combine the timestamp and body in the format `${timestamp}.${body}`, where the body is the raw JSON string before parsing. Ensure you're using HMAC SHA-256 for computing signatures and using `timingSafeEqual` for comparison to prevent timing attacks. The signature in the `x-salable-signature` header is hex-encoded, so convert it to a Buffer when comparing with your computed signature. Common issues: forgetting to include the timestamp in the payload, using the parsed JSON object instead of the raw body string, or comparing strings instead of using constant-time comparison. If timestamps are being rejected, verify your server's clock is synchronized. Clock drift beyond the 5-minute validation window causes rejection. ### Timeout Issues If deliveries consistently time out, your handler is too slow. Profile it to identify slow operations. Move time-consuming work to background jobs: return a success response immediately, then process the event asynchronously. ### Events Not Arriving If you're not receiving expected events, verify the webhook destination is configured for that event type and that your endpoint URL is correct and publicly accessible. Check the webhook delivery dashboard for sent events and your endpoint's responses. If events were sent but failed, the delivery details show the error. If events weren't sent at all, check the webhook destination's event type configuration. ## Next Steps Related guides: - **[Testing & Development](/docs/testing-and-development)** - Test webhook handlers in Test Mode - **[Subscriptions & Billing](/docs/subscriptions-and-billing)** - Understand subscription lifecycle events - **[Metered Usage](/docs/metered-usage)** - Process usage events and finalization --- ### Model Context Protocol (MCP) Source: https://salable.app/docs/mcp # Model Context Protocol (MCP) ## Overview Salable's **Model Context Protocol** server lets you manage your pricing and billing setup through conversation. Ask an AI assistant to create Products, configure Plans, set up Entitlements, or inspect Subscriptions. Anything you'd normally do in the dashboard or via API calls. Your assistant discovers the available tools automatically and figures out how to use them. ## Prerequisites You'll need a Salable API key, use your secret key: ```bash export SALABLE_API_KEY="your_api_key_here" ``` > **Important** MCP clients read environment variables from your shell, not from `.env` files. `SALABLE_API_KEY` must be exported in your shell before you launch your client. Never commit your API key to version control. ## Setting Up Claude Code The recommended path in Claude Code is the marketplace plugin: ```bash claude plugin marketplace add Salable/salable-claude-code-plugin claude plugin install salable ``` Once installed, type `/salable-monetize` in Claude Code to start the workflow. ### The `/salable-monetize` Workflow In Claude Code, type `/salable-monetize`, answer a few questions about your pricing model, and the skill handles the rest: reading your codebase, provisioning your catalog, and generating production-ready code. Your app needs authentication in place before you run the skill. The generated gating and checkout code depends on knowing who the current user is. If auth isn't set up yet, the skill will recommend solutions, but the generated code won't work end to end until you've wired it in. > **Important** Identity fields in Salable (Grantee IDs, Owner IDs) must use non-email identifiers: org IDs, user IDs, or hashed values. The skill moves through four phases, each building on what came before. It starts with **discovery**, reading your project to understand your framework, authentication provider, and existing integrations. You don't need to explain your stack; the skill figures it out from your dependencies, config files, and project structure, then maps out where monetization code should live. Next comes **monetization planning**, where the skill works with you to define which features to gate behind paid **[Plans](/docs/core-concepts#plan)**, what **[Entitlements](/docs/understanding-entitlements)** to create, and how to structure your tiers and pricing. It proposes a model based on common patterns for your type of application, but you're in control: accept the defaults, adjust individual pieces, or redesign from scratch. Once you've agreed on a model, the skill **provisions your catalog**: creating **[Products](/docs/core-concepts#product)**, Plans, **[Line Items](/docs/products-and-pricing#line-items)**, **[Prices](/docs/products-and-pricing#prices)**, and Entitlements in Salable automatically. All the API calls, ID generation, and relationship wiring happens in the background. Finally, the skill **generates code** tailored to your framework and auth setup: a pricing page with plan comparisons and checkout buttons, entitlement gating that conditionally renders features, a checkout flow wired to the Salable **[Cart](/docs/cart-and-checkout)** API and Stripe, a **[Subscription](/docs/subscriptions-and-billing)** management page, API routes, and navigation wiring. The code uses your actual Product and Plan IDs and integrates with your existing auth. The skill supports flat-rate, per-seat, metered, and hybrid pricing across multiple cadences and currencies. For more on how these models work, see the **[Products & Pricing](/docs/products-and-pricing)** guide. ### MCP-Only Setup (Without Plugin) If you only want raw MCP tools (and not the guided plugin workflow), connect Claude Code directly to the Salable MCP server. The fastest path is downloading the configuration file straight into your project: ```bash curl -o .mcp.json https://salable.app/mcp.json ``` Claude Code detects the `.mcp.json` file automatically and expands the `${SALABLE_API_KEY}` reference from your shell at startup. You can also add the server via the CLI: ```bash claude mcp add --transport http salable https://salable.app/api/mcp \ --header 'Authorization: Bearer ${SALABLE_API_KEY}' ``` By default, this stores configuration in `.mcp.json` at the project root, shared across the team with each person using their own API key. To make the server available across all your projects, add it at **user scope** instead: ```bash claude mcp add --transport http --scope user salable https://salable.app/api/mcp \ --header 'Authorization: Bearer ${SALABLE_API_KEY}' ``` ## Setting Up Codex Add the Salable MCP server with a single command: ```bash codex mcp add salable \ --url https://salable.app/api/mcp \ --bearer-token-env-var SALABLE_API_KEY ``` If Codex prompts you for an OAuth login, skip it. Salable uses API key authentication only. ### Installing the Monetization Skill The easiest way to install the skill is from within Codex itself. Paste the following: ``` Use the `$skill-installer` to install - Repo: `Salable/salable-codex-skills` - Path: `skills/salable-monetization` ``` Alternatively, you can install manually: ```bash git clone https://github.com/Salable/salable-codex-skills.git cd salable-codex-skills mkdir -p ~/.agents/skills cp -R skills/salable-monetization ~/.agents/skills/ ``` After installing, restart Codex to pick up the new skill. ## Next Steps - **[Quick Start](/docs/quick-start)**: Set up your first Product and Subscription - **[Products & Pricing](/docs/products-and-pricing)**: Pricing configuration and billing models - **[Core Concepts](/docs/core-concepts)**: Salable's data model and how the pieces fit together - **[Understanding Entitlements](/docs/understanding-entitlements)**: Feature gating patterns - **[Subscriptions & Billing](/docs/subscriptions-and-billing)**: Subscription lifecycle management - [Claude Code Plugin GitHub Repository](https://github.com/Salable/salable-claude-code-plugin): Source code and issue tracking - [Codex Skills GitHub Repository](https://github.com/Salable/salable-codex-skills): Source code and issue tracking --- ### Stripe Payment Methods Source: https://salable.app/docs/stripe-payment-methods # Stripe Payment Methods ## Overview Stripe supports a wide range of [payment methods](https://stripe.com/payments/payment-methods), but some need to be enabled through your connected account in the Stripe dashboard before it appears at checkout. ## Prerequisites You have onboarded your payment integration in Salable. This can be found in [payment integrations](/dashboard/payment-integrations) in the dashboard. ## How to manage payment methods in the Stripe dashboard Navigate to [payment integrations](/dashboard/payment-integrations) in the Salable dashboard and click the blue external link button in the actions column, this will open your connected account in Stripe. Payment method configurations in both live and test mode are managed separately, this is helpful if you are looking to test out a payment method before enabling it in live mode. However, the steps to manage payment methods are the same for both modes. If you are using test mode in Salable, make sure to switch to test mode in Stripe too. Search for “payment methods” in the Stripe dashboard and select Settings > Payments > Payment Methods. From the list of payment method configurations select “Salable Ltd account”; this will be the configuration your connected account uses with Salable. You can enable/disable payment methods from the list shown. For this example, enable Amazon Pay. To test this has worked, create a [quick checkout](/docs/openapi/scalar#tag/checkout/POST/api/checkout) for a plan or [checkout](/docs/openapi/scalar#tag/carts/POST/api/carts/{id}/checkout) a cart. This can be done from the edit product view in the Salable dashboard, each plan has a "Checkout plan" section. Once you have created a checkout you will see Amazon Pay enabled. To disable the payment method, navigate back to the Stripe dashboard, select the method you wish to disable and click the 'Disable' button. The payment method in any existing checkout sessions will be updated and disabled. ## Blog ### Per-Seat Pricing for Trello Power-Ups Source: https://salable.app/blog/monetising-trello-apps/trello-per-seat-pricing # Per-Seat Pricing for Trello Power-Ups How much should you charge for a Trello Power-Up? If you've spent any time looking at what other developers charge, the answer is all over the place—one dollar here, fifty dollars there. But look closer and a pattern emerges. The Power-Ups that sustain real businesses tend to land between five and fifteen dollars per seat per month. That's the range where someone can put it on an expense report without asking permission, but where the maths still works for you once a team of ten or twenty is paying. ## Why Per-Seat Pricing Works for Collaborative Tools Trello is inherently collaborative. Boards exist to be shared, cards move through workflows involving multiple team members, and the value of any Power-Up grows with the number of people who use it. Per-seat pricing accounts for this naturally. When revenue scales with users, pricing feels fair. A two-person team pays less than a twenty-person department. Nobody subsidises anyone else. This directly affects willingness to pay—teams getting proportional value are far less likely to churn than those locked into flat rates that feel punitive at small scale or like a bargain they might lose at large scale. ## What the Marketplace Tells Us Look at pricing across over 150 paid Power-Ups on the Trello marketplace and the five-to-fifteen-dollar range shows up again and again. Below five dollars, you're competing on impulse—dozens of Power-Ups price at one or two dollars per month, which can signal a throwaway project rather than a serious tool. Above $15, you start bumping into expense approval thresholds—the kind where someone needs a manager's sign-off before they can subscribe —which slows adoption considerably. The sweet spot sits between those two thresholds, where the price signals credibility without creating friction. Within that range, where you land depends on how much heavy lifting your Power-Up does. Simpler tools that enhance what Trello already does sit at the lower end. Tools that handle entire workflows—time tracking, project management, reporting—push toward ten dollars and above. ## Graduated and Volume Pricing Flat per-seat pricing works until customers scale. A ten-person team paying $10 per seat barely notices the $100 monthly line item. A two-hundred-person department at the same rate faces twenty-four thousand dollars annually—enough to trigger procurement review and kill deals. Flat pricing can prevent you from landing your most valuable customers. Graduated pricing solves this by reducing the per-seat cost as the number of seats increases. A Power-Up priced at ten dollars per seat might charge the full rate up to twenty-five seats, eight dollars for seats twenty-six through one hundred, and six dollars beyond that. The small team still pays ten dollars. The two-hundred-person department pays an effective rate closer to seven dollars, bringing the annual total down to around seventeen thousand, a meaningful difference in a budget conversation. Volume pricing takes a different approach: once a customer crosses a threshold, the reduced rate applies to all seats. Crossing from twenty-five to twenty-six seats drops the entire subscription from ten to eight dollars per seat. Simpler for customers to understand, but it introduces revenue cliffs where adding a single seat can actually reduce your total income. Graduated pricing avoids this by discounting only the incremental seats above each threshold. Most Trello Power-Ups haven't solved this yet. The majority charge flat rates regardless of team size, and those offering multiple tiers tend to charge by features rather than seat count. If your Power-Up can serve teams of fifty or more, graduated pricing gives you a competitive advantage over publishers whose flat rates scare off larger teams. ## The Hidden Complexity of Per-Seat Billing Per-seat pricing sounds simple until you build it. Payment providers like Stripe handle the payment side—charging cards, calculating proration, managing subscription state—but they don't handle the per-seat part. Stripe doesn't know who's in a Trello workspace or whether a new member should get access. That's all on you. You need to manage seat counts when users are added or removed, define your proration strategy, and build the entitlement logic that decides who gets access to paid features and what happens when a workspace exceeds its seat count. Each of these is solvable individually, but together they add up to a meaningful chunk of engineering work that diverts resources away from your product's features. ## Why Salable Exists for Exactly This Problem Salable picks up where Stripe leaves off. It handles seat management, entitlement checks, and access gating out of the box. You increment or decrement seats through the API, and Salable handles billing adjustments, proration, and entitlement state. Your Power-Up just asks "does this user have access?" and gets a clean yes-or-no answer. The developers who scale their Power-Ups are the ones who spent their time on features that make users want to subscribe, and let purpose-built infrastructure handle the billing. ## Finding Your Price Start with a price you can defend in conversation. If a customer asks why your Power-Up costs $10 per seat, you should have a clear answer about its value relative to alternatives. If that answer doesn't come easily, your price or your messaging needs work. Pricing isn't permanent. Starting slightly lower gives you room to raise prices as you add features and build a reputation. Starting too high creates resistance that's hard to overcome even after reductions. The goal is sustainable revenue that funds development while remaining accessible to teams that genuinely benefit from what you've built. --- ### The Trello Power-Up Monetisation Landscape Source: https://salable.app/blog/monetising-trello-apps/trello-power-up-monetisation # The Trello Power-Up Monetisation Landscape Trello's Power-Up marketplace sits at an interesting inflection point. For years, most Power-Ups were free, built as marketing tools or side projects. But as Trello Enterprise grew and the ecosystem matured, paid Power-Ups became viable businesses. Some developers now generate substantial recurring revenue from tools that enhance project management, automate workflows, or integrate with external services. The opportunity is real, but navigating it requires understanding Trello's policies, user expectations, and the technical landscape of Power-Up billing. ## The Evolution from Free to Paid The Trello Power-Up ecosystem didn't start with monetisation in mind. When Atlassian opened the platform to third-party developers, most Power-Ups served as lead generation tools for larger products or passion projects from developers who simply wanted to solve a problem. The economics made sense at small scale: a few hundred users cost almost nothing to serve, and the marketing value or personal satisfaction justified the effort. That calculus changed as successful Power-Ups grew. A Power-Up serving ten thousand workspaces requires real infrastructure. Support requests pile up. Feature requests multiply. The developer who started building on weekends now faces a choice: abandon the project, find a way to fund it, or burn out maintaining something that provides value to thousands while returning nothing. Atlassian recognised this tension. Their marketplace policies evolved to explicitly support paid Power-Ups, and the broader SaaS ecosystem demonstrated that users would pay for tools embedded in their workflows. The shift wasn't instantaneous, but it was decisive. Today, paid Power-Ups compete successfully alongside free alternatives, and users have grown accustomed to evaluating tools based on value rather than expecting everything to be free. ## Understanding Trello's Policy Framework Before building a monetisation strategy, you need to understand what Atlassian actually permits. The company maintains clear guidelines about how Power-Ups can charge users, handle data, and integrate with the Trello platform. Violating these policies risks removal from the marketplace, so building on solid ground matters. Atlassian allows Power-Ups to charge users directly rather than requiring all transactions to flow through the Atlassian Marketplace. This flexibility matters because it lets you use your own billing infrastructure, set your own prices, and maintain direct customer relationships. You can implement subscriptions, one-time purchases, or usage-based pricing as your business model demands. The policies do impose meaningful constraints. Your Power-Up must clearly communicate pricing before users commit. You cannot bait users with free functionality and then lock them out without warning. Privacy requirements mandate transparent data handling, and your Power-Up must respect the permissions users grant rather than overreaching into data you don't need. These constraints benefit responsible developers. They raise the bar for competitors and establish user trust in the ecosystem. When users know that marketplace policies protect them, they're more willing to try paid Power-Ups from unfamiliar developers. ## Where the Revenue Opportunities Actually Lie Not all Power-Up categories present equal monetisation potential. Understanding where users demonstrate willingness to pay helps you evaluate whether your Power-Up idea has commercial viability or whether you're building in a space where free alternatives will always dominate. Power-Ups that integrate Trello with external services show strong monetisation potential. Teams using Trello alongside Salesforce, HubSpot, or specialised industry tools will pay for integrations that keep their workflows connected. The value proposition is concrete: save hours of manual data entry every week, maintain data consistency across systems, and enable workflows that would otherwise require constant context-switching. Automation and workflow tools represent another lucrative category. Trello's built-in automation handles common use cases, but complex workflows often require more sophisticated tooling. Power-Ups that automate approvals, enforce processes, or orchestrate multi-board workflows solve problems that cost teams real time and money. When a Power-Up saves a project manager two hours every week, a monthly subscription pays for itself immediately. Reporting and analytics Power-Ups attract budget-holding managers who need visibility into team performance, project status, or resource allocation. These users aren't just willing to pay; they often have dedicated budgets for business intelligence tools. A Power-Up that generates the weekly status report their VP requires earns its subscription fee every time it runs. Some categories struggle to monetise. Simple visual enhancements, card aging indicators, and basic label modifications face intense competition from free alternatives. Users view these as features Trello should include rather than capabilities worth paying for. Building in these spaces isn't impossible, but expect lower conversion rates and more price sensitivity. ## The Collaborative Pricing Challenge Trello's collaborative nature creates a pricing dynamic that doesn't exist in single-user applications. When one person installs a Power-Up on a shared board, everyone on that board potentially benefits. This shared value complicates the question of who pays and for what. Consider a reporting Power-Up installed by a project manager. The PM generates reports, but the entire team benefits from improved communication and visibility. Should only the PM pay? Should the cost scale with team size? Should the workspace administrator cover the subscription for everyone? Most successful Power-Ups have converged on workspace-level billing because it matches how teams budget for tools. The workspace administrator or a designated purchaser subscribes, and everyone in the workspace gains access. This approach eliminates awkward conversations about who covers the cost, simplifies provisioning as team members join or leave, and aligns with how organisations think about software expenses. Per-seat pricing within this workspace model works particularly well. It scales costs with team size, making the Power-Up affordable for small teams while capturing appropriate revenue from larger organisations. A five-person startup pays less than a fifty-person department, but both receive value proportional to their investment. ## Setting Expectations for Your Market User expectations in the Trello ecosystem influence how much you can charge and how aggressively you can gate features. These expectations have shifted over time, but they still differ from expectations in other software markets. Trello users tend to evaluate Power-Ups comparatively. Before paying for your solution, they'll search for free alternatives. This competition keeps prices grounded but also means that clearly superior products can command premiums. If your Power-Up demonstrably outperforms free options, users will pay the difference. The price range that works best for most Power-Ups falls between five and fifteen dollars per user per month. Below this range, you signal low value and attract price-sensitive users who churn at any difficulty. Above this range, you trigger procurement processes and competitive evaluations that slow sales cycles dramatically. This pricing range works because it fits within expense report limits at most organisations. Individual contributors can subscribe without seeking approval. Managers can add the cost to their team budget without escalation. The friction between wanting a tool and having access to it virtually disappears. Trial expectations also deserve attention. Trello users expect to evaluate Power-Ups before committing. Whether you offer a limited free tier or a time-boxed trial of the full product, some evaluation path is essential. Cold conversions, where users pay without experiencing the product, remain rare in this ecosystem. ## Building on Solid Technical Foundations The technical architecture of your Power-Up billing matters as much as your pricing strategy. Trello's platform provides identity information that you'll need to map to subscriptions, and the patterns you choose early become difficult to change once users depend on them. Trello identifies users, boards, and workspaces with unique IDs that persist across sessions. Your billing system needs to map these identities to subscription records. When a user accesses your Power-Up, you check whether their identity, or their workspace's identity, corresponds to an active subscription. This check happens on every interaction, so it needs to be fast and reliable. The Power-Up client library provides methods to retrieve the current user, board, and workspace context. Use these methods rather than storing credentials or making assumptions about user identity. Trello handles authentication; you handle authorisation based on the identity Trello provides. Webhook integration enables more sophisticated billing scenarios. Trello can notify your backend when boards are created, members are added, or cards are moved. These events can trigger usage metering, seat count updates, or access recalculations. Building on webhooks rather than polling improves performance and reduces your infrastructure costs. ## Where Salable Fits At Salable, we've worked with dozens of Power-Up developers navigating these exact challenges. Our platform handles the billing infrastructure so you can focus on building features rather than payment forms. We support per-seat pricing with automatic seat management, workspace-scoped subscriptions that map to Trello's organisational model, and the trial and freemium configurations that drive conversion in this ecosystem. The technical integration maps Trello's identity model to Salable's grantee system. When a workspace administrator subscribes, their Trello workspace ID becomes the grantee group that controls access. Everyone in that workspace inherits access automatically. When members join or leave, seat counts adjust without manual intervention. This infrastructure matters because billing isn't your core competency. Every hour spent building subscription management is an hour not spent improving the features users actually pay for. Offloading billing to a purpose-built platform lets you compete on product quality rather than payment processing. ## Navigating Your Next Steps The Trello Power-Up monetisation landscape rewards developers who understand both the opportunity and the constraints. Users are increasingly willing to pay for Power-Ups that solve real problems, but pricing must respect the ecosystem's collaborative nature, where one purchaser often enables access for an entire board or workspace. Start by evaluating where your Power-Up fits within the revenue potential spectrum. Integration, automation, and reporting tools command stronger prices than visual enhancements. Consider workspace-level billing as your default model unless your use case demands something different. Price within the five-to-fifteen-dollar range to minimise purchasing friction. Build your technical foundation with Trello's identity model in mind. Map users and workspaces to subscriptions cleanly, and choose a billing platform that understands the nuances of collaborative tool monetisation. The developers who thrive in this ecosystem treat billing as a solved problem rather than a custom engineering challenge, freeing their energy for the product improvements that drive sustainable growth. --- ### Introducing Salable Only Subscriptions: Access Without Payment Source: https://salable.app/blog/features/introducing-salable-only-subscriptions # Introducing Salable Only Subscriptions: Access Without Payment You may want to handle invoicing for certain customers separately or not invoice them at all but still want to provide access to your SaaS app immediately. Your customer success team needs to use your product to do their jobs. Your integration partner needs access to build a connector. The prospect you're courting needs a trial environment that doesn't require them to hand over a credit card at the start of a sales conversation. The customer who experienced two hours of downtime last Tuesday deserves a period of complimentary access. Until now, handling these cases in Salable meant working around your own billing system—provisioning access outside the platform you use for everything else, managing it separately, and hoping the two systems stayed in sync. Today that changes. Salable Only Subscriptions let you create fully functional subscriptions directly from your dashboard without touching Stripe. No checkout flow, no payment. Just access, managed through the same entitlement system. ## The Problem with Managing Access Outside Your Billing System When your access control lives in your billing system, every exception creates a decision: do you route this person through checkout, or do you manage their access some other way? Routing users through an unnecessary checkout process can create a bad user experience. You're forcing a partner developer through a payment form they should never see. You're asking your own support staff to hold a company card number. You're creating test subscriptions in your billing provider that inflate your customer counts and cloud your analytics. Managing their access outside your billing system is worse. Now you have two sources of truth for who can use what. Your entitlement checks might look at billing for paying customers and a separate database flag for exceptions. Those systems drift apart. The support engineer who was provisioned access in the ad-hoc system still has it two years later after they've moved to a different role. The partner who stopped integrating with your platform is still active because nobody thought to revoke the manual flag. The right answer is a single entitlement system that handles both cases—and that's exactly what Salable Only Subscriptions provide. ## What They Are A Salable Only Subscription is a subscription created directly in Salable that bypasses the Stripe payment process entirely. It behaves identically to a subscription created through checkout: it assigns entitlements, supports grantee groups, supports usage recording, respects tier tag constraints, and participates in all the same lifecycle management your paid subscriptions use. The only difference is that no invoice is generated and no payment is collected in order to generate the subscription. From your application's perspective, when it calls the Salable API to check whether a user has access to a feature, the response is the same as users with standard subscriptions. ## Who This Is For There are several use cases for Salable Only Subscriptions including the following: **Your internal teams.** Your engineers, product managers, designers, and support staff all need to use your product to understand it and improve it. Running them through checkout creates unnecessary friction and produces billing records that distort your customer metrics. A Salable Only Subscription gives them full access under your standard entitlement system, which also means their experience accurately reflects what customers see—important when they're investigating bugs or evaluating whether a new feature works as intended. **Technology partners and integration developers.** When another company builds an integration on top of your platform, they need access to develop, test, and maintain it. The value exchange isn't monetary—it's the ecosystem growth and distribution they bring. A Salable Only Subscription formalises that access relationship without requiring a billing arrangement, and because it lives in your standard subscription system, it shows up in your records and can be revoked cleanly when the relationship ends. **Sales-led trials and demo environments.** Not every customer journey starts with self-serve checkout. Enterprise prospects often need access provisioned before they've signed anything, and requiring them to enter payment details at the start of a sales engagement creates friction at exactly the wrong moment. A Salable Only Subscription with an expiry date matching your trial window gives the prospect proper access while keeping your pipeline in motion. **Goodwill and service recovery.** When something goes wrong—extended downtime, a billing error, a feature that didn't work as advertised—granting a period of complimentary access is a meaningful gesture. A Salable Only Subscription lets you do this precisely, for the right plans and the right duration, documented in the same system where the rest of your customer relationships live. **Beta programs and early access.** Beta testers are providing something valuable: real usage, real feedback, and the validation that your next feature actually works in the field. Paying them with a paywall doesn't make sense—but neither does provisioning their access with a database flag that you'll forget about in six months. A Salable Only Subscription with a defined expiry keeps your beta program clean and automatically revokes access when the window closes. **Manual invoicing.** Some business relationships operate outside automated billing. Enterprise customers on purchase orders, customers in regions where card payments aren't practical, deals negotiated with custom payment terms—in all of these cases, payment happens outside Stripe but access still needs to be granted inside your application. A Salable Only Subscription lets you provision access immediately when payment is confirmed, without routing the customer through checkout. Your entitlement records stay accurate regardless of how the underlying payment was handled. ## Lifecycle Options Because Salable Only Subscriptions aren't tied to a Stripe billing cycle, you define the lifecycle directly when creating the subscription. Three models are available. **Interval-based** works like a standard recurring subscription. You set an interval—day, week, month, or year—and an interval count, and the subscription renews accordingly. This is the right choice when access should continue indefinitely with explicit renewal, such as for a permanent internal team member or an ongoing partner relationship. For example, a monthly subscription uses an interval of "month" with a count of one; a quarterly subscription uses "month" with a count of three. You can also configure the subscription to not automatically renew when it expires, scheduling it to terminate cleanly at the close of the current period. **Expiry date** terminates the subscription automatically on a date you choose. No renewal, no action required to end it. This is ideal for trials, beta programs, and goodwill periods where you know upfront exactly how long access should last. Set the date, create the subscription, and the entitlement system handles the rest. **Perpetual** creates a subscription that never expires and never renews. It stays active indefinitely until you explicitly cancel it. Permanent partner access and internal tooling where there's no meaningful renewal cycle benefit from this model—you don't want the subscription to expire on its own, but you also don't want to think about renewal dates. ## Creating Your First Salable Only Subscription Creating a Salable Only Subscription takes about thirty seconds from the Subscriptions page in your dashboard. Click **Create Subscription**, select a product and one or more plans—plans must have at least one line item and one entitlement configured to be eligible—specify the owner receiving access, optionally specify a grantee or group, configure the lifecycle, and create. The subscription is active immediately. The owner's entitlement checks will reflect the new access from your next API call. ## What Stays the Same Because Salable Only Subscriptions participate fully in the standard entitlement infrastructure, you benefit from everything that already works. Tier tag constraints prevent conflicting plans. Grantee Groups work exactly as they do for paid subscriptions. Plans with metered line items track usage correctly. Per-seat plans still support full seat provisioning and management, including group assignments and quantity adjustments. All webhook events with the exception of the `receipt.created` event fire for Salable Only Subscriptions, keeping your application in sync with lifecycle changes. What you don't get is anything tied to payment: no invoices, no receipts, no Stripe involvement, no proration calculations, no dunning retries. If you later want to transition a user from complimentary access to a paid subscription, cancel the Salable Only Subscription and direct them through your standard checkout flow. ## One System for All Access Salable Only Subscriptions make decoupling payment and app access possible. Whether someone earned access by paying, by being on your internal team, by joining a beta program, or by receiving goodwill credit after a service interruption—your application sees a subscription with active entitlements. One system, one check, one source of truth. Your paying customers' access is already managed through Salable. Now the rest of your access relationships can be too. --- ### Feature Flags and Entitlements: A Practical Guide Source: https://salable.app/blog/insights/entitlements-future-feature-management # Feature Flags and Entitlements: A Practical Guide Every SaaS application must answer two questions: "Is this feature ready?" and "Has this user been granted access?" Feature flags answer the first. Entitlements answer the second. The distinction matters because the tools optimise for different things. Feature flags are operational—they help you control rollouts, run experiments, and kill misbehaving code. Entitlements are commercial—they connect feature access to what customers have paid for. Using one tool for both creates friction that compounds over time: operational concerns tangled with billing logic, access rules scattered across systems, pricing changes that require code deployments. Understanding what each tool does well is the first step to using them together effectively. ## What Feature Flags Do Well Feature flags emerged from operational necessity. Engineers needed to deploy code without immediately exposing it to users. Turn the flag on, and the code runs. Turn it off, and it doesn't. Simple, powerful, and entirely about code readiness. The taxonomy is well-established: release toggles for gradual rollouts, experiment toggles for A/B testing, ops toggles for runtime control, and permission toggles for user access. The first three are clearly operational concerns. The fourth—permission toggles—is where confusion enters. Large engineering organisations deploy code behind feature flags constantly. Percentage rollouts gradually expose new functionality while monitoring error rates and performance. Instant rollback when problems emerge. Tens of thousands of experiments running simultaneously. Degraded service modes during incidents. These are all operational decisions: is the code ready? Is the system healthy? Should this experiment continue? The question feature flags answer is: "Is this feature ready?" That's operational, not commercial. ## What Entitlements Do Well Plans define what customers get. A Pro plan might include analytics, integrations, and priority support. An Enterprise plan adds SSO and audit logs. When someone subscribes to a plan, they get access to those features. When they upgrade, their access expands. When they downgrade or cancel, it contracts. Entitlements sit between your plans and your application. Your app doesn't check what plan someone is on—it checks whether they have a specific entitlement. The plan grants the entitlement; the app just asks "do they have it?" This means you can restructure plans without touching code. Move a feature from Pro to Enterprise by updating which plan grants that entitlement. Grandfather existing customers by leaving their entitlement in place even after you change the plan. Create a custom enterprise deal by granting entitlements directly, without inventing a new plan. ## Using Them Together Both checks can happen on the same feature. The entitlement check asks "does this user have access to analytics?" The feature flag asks "should this user see the new analytics dashboard or the old one?" Say you're redesigning your analytics dashboard. Users on Pro and Enterprise plans have the analytics entitlement—that doesn't change. But you want to roll out the new design gradually, monitor for problems, and roll back if something breaks. The entitlement controls who can access analytics at all. The feature flag controls which version they see. The two systems don't overlap. Feature flags never need to know about plans or billing. Entitlements never need to know about rollout percentages or experiments. Each does its job. ## Practical Guidance How do you know which tool applies to a given access decision? Ask: "Would this change if the user upgraded their subscription?" If yes, it's an entitlement. Ask: "Would this change based on deployment state or experiment assignment?" If yes, it's a feature flag. The grey areas usually resolve when you identify the source of truth. If the answer comes from your billing system, use entitlements. If the answer comes from your deployment pipeline or experiment platform, use feature flags. ## Connecting Entitlements to Billing The full value of entitlements emerges when they're connected to subscription state automatically. When a customer upgrades, their entitlements expand. When they downgrade, entitlements contract. When they churn, premium access disappears. Building this yourself means writing webhook handlers for every subscription event, reconciliation jobs for when webhooks fail, and debugging logic for when state drifts. Every payment provider has different webhook schemas and retry semantics. The overhead scales with your pricing complexity. ## Where Salable Fits Salable provides the entitlement layer that connects directly to subscription state. Plans grant entitlements. Subscriptions grant plans. When subscription state changes—upgrades, downgrades, renewals, cancellations—entitlements update automatically. Your application makes a single check: does this user have this entitlement? The answer reflects current subscription state without you having to build webhook handlers, reconciliation jobs, or sync logic. The operational/commercial distinction stays clean. Use your feature flag platform for rollouts, experiments, and kill switches. Use Salable for everything tied to what customers have paid for. Each system does what it's good at, and the boundary between them is clear. If you're building subscription software and want pricing flexibility without the access control overhead, [explore Salable's entitlements](https://salable.app/docs/understanding-entitlements). --- ### From Free to Paid: A SaaS Migration Guide Source: https://salable.app/blog/saas-startup-guides/free-to-paid-saas-migration # From Free to Paid: A SaaS Migration Guide You built a product, gave it away for free, and now teams depend on it. Introducing pricing can feel like a betrayal, but running infrastructure costs you time and money. Sooner or later, you have to take the plunge. With the right plan, you can turn free users into paying customers while keeping the goodwill you've built. It comes down to communication and grandfathering, backed by solid billing infrastructure. ## Making the Case to Your Users Whether you're turning a hobby project into something sustainable or converting a validated idea into revenue, you need to get the communication and implementation right. Some churn is acceptable if the alternative is being out of pocket, but handling this with care keeps more users paying than not. Infrastructure costs scale with users, and at some point, the numbers stop being trivial. Feature requests stack up, and without revenue, it's hard to justify building them. Free users who depend on your product for work already expect uptime and active development. Pricing lets you meet the standard they're holding you to. Third-party services raise prices or shut down without warning, and you need revenue to absorb those hits. Give your users honest, specific reasons. They run businesses too, and these are arguments they'll understand. ## The Grandfathering Philosophy You can make free-to-paid transitions work through generous grandfathering. Your longest-standing users deserve the best treatment, because it's right and because you'll benefit from their goodwill for years. Grandfather existing active users for three to six months without requiring payment. You're giving them time to budget for the new expense and evaluate alternatives if they choose. It signals that you're building something sustainable, not squeezing them for cash. Consider offering lengthy or permanent discounts to your first users. A thirty to fifty percent lifetime discount shows you value their early support, and that kind of loyalty is hard to buy through marketing. Define "existing active user" with a wide lens. Include anyone who has used your product within the last three months, not just yesterday's active users. Include accounts that signed up but didn't engage much, giving them room to explore during the grace period. The cost of including more people is minimal, and you'll build goodwill that pays for itself over time. ## Communicating the Change Announce early, well before any billing begins. The grace period gives users months, but the announcement itself should land as soon as you've confirmed pricing. Lead with gratitude and honesty. Thank users for shaping the product, explain the costs driving the change, and share what paid funding lets you build. Charge confidently. Apologising for your pricing undercuts it. Be specific. Name the date pricing takes effect, the grandfathering terms, and what users need to do. Everyone is free to walk away. Make each path towards payment as frictionless as possible. A single announcement won't reach everyone. Plan a sequence of reminders leading up to the grace period expiry: an initial announcement, a follow-up that answers common questions from the first round, a final notice close to the billing date, and a post-launch message that thanks converters and confirms grandfathered status. Answer replies yourself, not through an automated system. ## Implementing the Transition Once your users know what's coming and when, you need to build the billing infrastructure behind it. Keep tabs on your pre-existing users. You need to know who qualifies for a grace period free trial, who gets a discount, and what the terms are. Make those terms visible in the checkout flow so users see exactly what they're getting. Send out an email campaign with checkout links to encourage onboarding, and track user IDs in your pricing table views to show tiers available only to existing users. Honour your grandfathering commitments. Deliver the grace period in full. Apply the discounts you offered. Your users trusted your word, and following through on that trust keeps it intact. ## Get Your Billing Right from Day One You've earned your users' trust through honest communication and generous grandfathering. Don't lose it to a broken checkout or a billing bug. Salable handles the billing infrastructure behind those promises. Define your products and plans in the dashboard, set up grandfathered pricing tiers for existing users, then drop the checkout links straight into those migration emails. Entitlements let you gate features with a single API call, no billing logic scattered through your codebase. When a grandfathered user's grace period expires, Salable handles the transition. When they upgrade or downgrade, Salable manages the proration. You handled the hard part, telling your users the product they love now costs money. [Salable handles what comes next](https://salable.app). --- ### The Most Expensive Mistake Startups Make With Stripe Source: https://salable.app/blog/saas-startup-guides/build-vs-buy-billing # The Most Expensive Mistake Startups Make With Stripe "We'll just use Stripe directly" is one of the most expensive decisions a startup can make. Not because Stripe is hard to integrate, but because billing is an iceberg: the checkout flow is the visible 10%, while subscription management, failed payment handling, usage metering, entitlement enforcement, and tax compliance lurk beneath the surface. Teams that start with DIY billing inevitably discover these hidden requirements, usually after shipping a fragile v1 that becomes increasingly expensive to maintain. Understanding when build makes sense requires honest accounting of both immediate and ongoing costs. ## The Seductive Simplicity of "Just Stripe" Stripe's API is genuinely well-designed. A developer can create a checkout session, redirect a customer, and process a payment in an afternoon. The documentation is excellent, the test mode is comprehensive, and the code samples actually work. It feels like billing is solved. But checkout is the easiest part of billing. The real complexity surfaces over subsequent months. Your checkout worked, but now you need to know whether the customer's subscription is still active. Stripe tracks this, but your application needs to track it too. You need webhooks to stay synchronised—and webhooks can fail, arrive out of order, or deliver duplicates. Suddenly you're building event infrastructure. Your customer wants to upgrade to a higher tier. Stripe supports proration, but you need to calculate what they owe, present it clearly, and update their entitlements. Your upgrade flow needs to handle mid-cycle changes, annual to monthly switches, and customers who upgrade and downgrade repeatedly. A credit card expires. Stripe retries automatically, but you need to communicate with the customer, enforce grace periods, and eventually suspend access. When the payment finally goes through, you need to reinstate access. These flows need testing, monitoring, and customer support tooling. Each requirement is manageable in isolation. Together, they constitute a billing system that takes months to build properly and ongoing engineering effort to maintain. ## The Real Cost Evaluating build versus buy requires honest accounting. Most teams underestimate because they count only the initial integration. The checkout flow—creating products, configuring prices, handling success redirects—might take a week. That's the visible iceberg tip. Subscription state management with webhooks, idempotency, and reconciliation takes another few weeks. Plan changes with proration take more. Failed payment recovery takes more still. Usage metering, if you need it, is architecturally significant. Entitlement enforcement weaves through your entire application. Customer-facing billing portals require substantial frontend work. Tax compliance creates legal liability if you get it wrong. Add it up honestly: DIY billing represents months of engineering time for a solid foundation, plus ongoing maintenance that might consume one engineer's attention indefinitely. ## The Costs That Compound The initial build is just the beginning. DIY billing carries ongoing expenses that grow over time. Payment processor APIs change. New payment methods emerge. Your pricing model evolves. Each change requires engineering attention. A billing platform spreads this maintenance across all its customers; DIY billing concentrates it on you. When billing bugs hit production, they affect real money. Remediation includes refunds, customer communication, and reputation damage. And the engineers who built your billing system hold irreplaceable knowledge—when they leave, that knowledge leaves with them. Then there's opportunity cost—the least visible but often the largest. Every week maintaining billing infrastructure is a week not building features that differentiate you in the market. ## When Building Makes Sense Despite these costs, building in-house sometimes makes sense. If billing itself is your competitive advantage—you're building a billing platform, payment processor, or accounting system—then you're not building infrastructure, you're building product. Build. If you're processing millions of transactions with genuinely simple pricing, the economics shift. Per-transaction fees add up, and simplicity reduces implementation cost. But be honest about whether your pricing will stay simple as you grow. If your pricing model is genuinely novel and no platform supports it, you may need custom work. But verify this is actually true. Most "unusual" models turn out to be variations on patterns that platforms already handle. If regulatory requirements mandate specific data handling or processing flows that platforms can't accommodate, building isn't optional. For most SaaS products, none of these conditions apply. The pricing model is standard. Volume doesn't justify fixed costs. Billing isn't the differentiator. In these cases, building is an expensive distraction from actual product development. ## This Is Why We Built Salable The question isn't whether you _can_ build billing—you can. The question is whether you _should_. We've done the hard work: checkout, subscription management, entitlements, usage metering, team billing, failed payment recovery. All the iceberg beneath the waterline. You pay us instead of building it yourself, and your engineers stay focused on features that differentiate your product. Build if billing is core to your value proposition. For everyone else, there's [Salable](https://salable.app/docs/quick-start). --- ### The Webhook Mistakes That Cost Companies Real Money Source: https://salable.app/blog/saas-startup-guides/webhooks-sync-billing # The Webhook Mistakes That Cost Companies Real Money Your application needs to know when subscriptions change, but polling the billing API every minute is wasteful and slow. Webhooks deliver events in real-time, but they come with their own challenges. Events can arrive out of order. Your server might be down when a critical event fires. The same event might be delivered twice. Building reliable webhook handling means accounting for these realities rather than assuming perfect delivery. The patterns aren't complicated, but they're non-obvious to developers who haven't been burned by production failures. ## The Webhook Mental Model Think of webhooks as push notifications for your server. Instead of your application constantly asking "did anything change?" the billing system sends a message when something happens. This approach is more efficient and delivers faster updates, but it requires your application to be ready to receive messages at any time. When a billing event occurs—a successful payment, a subscription cancellation, or a plan change—the billing system generates a payload describing the event and sends an HTTP request to an endpoint you've configured. Your application receives this request, processes the event, and responds to acknowledge receipt. That simple flow, however, masks several challenges. Your endpoint must be publicly accessible, which means you need to validate that requests actually come from the billing system rather than from attackers. Your processing must handle the same event arriving multiple times. Your application must remain functional even when events arrive in unexpected orders. Each of these concerns requires explicit engineering attention. The alternative to webhooks is polling, where your application periodically requests the current state of subscriptions and compares it to what you've stored. Polling works but has significant drawbacks. The interval between polls creates latency; a customer who upgrades won't see their new features until the next poll completes. Frequent polling wastes resources and may hit rate limits. Infrequent polling delays important changes. Webhooks solve these problems by delivering updates immediately, but they shift complexity from timing to reliability. ## Validating Webhook Authenticity Anyone who discovers your webhook endpoint can send requests to it. Without validation, an attacker could fabricate events to manipulate your application's billing state. Signature validation prevents this by cryptographically proving that requests originated from the billing system. Each webhook request includes a signature, typically in a header. The signature is computed using a secret key shared between you and the billing system. Your handler computes the expected signature from the request body and shared secret, then compares it to the provided signature. If they match, the request is authentic. If they don't, reject the request. The signature computation typically involves a cryptographic hash function like HMAC-SHA256. Your billing provider's documentation specifies the exact algorithm. Most webhook libraries and SDKs include signature validation functions; use them rather than implementing your own. Cryptographic code is easy to get subtly wrong. Store your webhook secret securely. It should be treated like a password: not committed to source control, not logged, and rotated if potentially compromised. If an attacker obtains your webhook secret, they can forge valid webhook requests. Some billing systems timestamp their signatures to prevent replay attacks, where an attacker captures a valid webhook and resends it later. Your validation should check the timestamp and reject requests older than a reasonable threshold, typically a few minutes. ## The Idempotency Imperative The billing system may deliver the same webhook multiple times. This isn't a bug; it's a feature of reliable delivery. When acknowledgment of delivery fails to arrive—perhaps your server responded slowly, or a network issue dropped the response—the system retries. Your handler must produce the same outcome whether it processes an event once or ten times. This property is called idempotency, and it's the single most important principle in webhook handling. An idempotent handler can be safely called multiple times without changing the result beyond the first call. Designing for idempotency from the start is vastly easier than retrofitting it later—adding idempotency to an existing system often means migrating your entire event history. The standard approach uses an event identifier. Each webhook includes a unique ID for the event it represents. Before processing, your handler checks whether you've already processed this event ID. If you have, skip processing and return success. If you haven't, process the event and record the ID. This check-then-process pattern has a race condition: two concurrent deliveries of the same event might both pass the check before either records the ID. Use database constraints or transactions to ensure atomicity. A unique constraint on the event ID column converts the race condition into a constraint violation, which you handle by returning success. Idempotency extends beyond duplicate detection. Your processing logic itself should be idempotent. If a webhook grants a user entitlements, granting them again should be safe (they already have them). If a webhook updates subscription status, updating to the same status should be a no-op. Design your handlers so that the entire operation, not just the delivery, is idempotent. ## Handling Out-of-Order Delivery Webhooks don't necessarily arrive in the order events occurred. A subscription update might arrive before the subscription created event. A payment succeeded notification might arrive after a payment failed notification for an earlier retry. Your handlers must be robust to these ordering variations. The root cause is that webhooks operate in a distributed system. Different events might be processed by different servers, routed through different network paths, and delivered on different retry schedules. Even if event A happened before event B, the webhook for B might arrive first. The safest pattern is to fetch current state rather than relying on the webhook content. When you receive a webhook indicating that subscription status changed, don't trust the status in the payload. Instead, use the webhook as a signal to fetch the subscription's current state from the billing API. The API always returns current truth; the webhook payload might be stale. This pattern trades API calls for correctness. Each webhook triggers a fetch, which adds latency and API usage. For most applications, the tradeoff is worthwhile. If API costs or latency become significant concerns, you can optimise by trusting webhook data for create events (where there's no previous state to conflict with) while fetching for updates. Some systems include timestamps or version numbers in webhook payloads. You can compare the webhook's timestamp against your stored timestamp, only applying updates if the webhook is newer. This approach works but requires careful handling of clock skew and initial state. ## Failure Handling and Retries Your webhook endpoint might be unavailable when an event fires. Your server might crash, your network might hiccup, or your handler might throw an exception. The billing system will retry delivery, but your application needs to cooperate with that retry mechanism. Return appropriate HTTP status codes. A 200-level response signals that you've received and processed the event; no retry follows. A 500-level response signals that something went wrong; the billing system will retry later. A 400-level response typically indicates a malformed request; whether a retry occurs depends on the specific code and billing system. Process webhooks quickly. Most billing systems enforce timeout thresholds; if your handler takes too long, the delivery is treated as a failure and retried. If your processing is inherently slow, acknowledge receipt immediately and process asynchronously. Asynchronous processing requires its own reliability layer. If your server crashes after acknowledging but before processing, the billing system considers delivery complete, but you've lost the event. You need a queue or similar mechanism to ensure processing completes. The added complexity is manageable but non-trivial. ## Monitoring and Alerting Webhooks fail silently from your perspective. If your endpoint is down, you won't receive notifications about the missed notifications. Monitoring must be proactive rather than reactive. Track webhook receipt rate. You should receive webhooks at a relatively consistent rate, proportional to your activity. A sudden drop might indicate endpoint problems, misconfiguration, or issues on the billing provider's side. A sudden spike might indicate test data accidentally routed to production, or an attack. Monitor handler success rate. Track how many webhooks process successfully versus fail with exceptions. A rising error rate indicates bugs in your handling code or unexpected payload variations. Set up alerts for missing expected webhooks. If you process a checkout event but never receive the corresponding subscription created event, something is wrong. Reconciliation alerts like these catch issues that receipt monitoring misses. Log webhook payloads for debugging. When handling fails, you need to understand what the payload contained. Logging should be detailed enough to reproduce issues but careful about sensitive data. Redact payment method details and personally identifiable information from logs. Periodically reconcile your state against the billing system's state. Even with perfect webhook handling, you might have historical bugs or missed events from before monitoring was in place. A weekly job that fetches all subscriptions and compares them against your database catches drift before it causes customer impact. ## Scaling Webhook Processing As your customer base grows, webhook volume increases. A system that handled ten webhooks per minute might suddenly need to handle hundreds. Your architecture needs to scale with demand. Queue-based processing decouples receipt from handling. Your webhook endpoint validates the signature, enqueues the event, and returns immediately. Separate workers pull from the queue and process events. This architecture handles traffic spikes gracefully: the queue absorbs bursts while workers process at sustainable rates. Horizontal scaling adds more workers as volume increases. If one worker can process fifty webhooks per second, ten workers can process five hundred. Queue-based architectures make scaling straightforward: you add workers without changing the receipt endpoint. Database write patterns may become bottlenecks before processing speed does. Every webhook that updates subscription state writes to your database. If many webhooks arrive simultaneously for the same subscription, you'll either serialise writes (slowing processing) or risk consistency issues from concurrent updates. Batching writes or using optimistic concurrency control helps manage this contention. Consider geographic distribution for latency. If webhooks are delivered from a specific region and your servers are far away, network latency adds to processing time. Multi-region deployment or edge processing can reduce this latency for customers sensitive to synchronisation speed. --- Getting webhooks right is harder than it looks. Many devs patch together something "good enough" and move on, only to discover missed events and silent failures months later when customers complain. Building infrastructure that never misses a webhook takes serious effort—and maintaining it takes more. Salable handles all subscription lifecycle webhooks internally. Payments, cancellations, upgrades, renewals—we process them so you don't have to. Your entitlements stay in sync without you writing a single webhook handler. If you want to respond to events for your own purposes—notifications, alerts, setting up account data—you can use our [webhook events](https://salable.app/docs/webhooks). But for most apps, it's entirely optional. --- ### Avoid Double Charges, Chargebacks, and Angry Customers Source: https://salable.app/blog/saas-startup-guides/testing-billing-integration # Avoid Double Charges, Chargebacks, and Angry Customers Your billing code runs exactly once per customer per event. There's no retry, no rollback, no "let's deploy a fix and re-run." If the webhook handler fails to provision access, customers wait on support. If the upgrade flow double-charges, you're issuing refunds and apologies. The usual development instincts—deploy fast and iterate—don't apply when money is involved. Testing billing integrations requires different strategies: isolated test environments, synthetic customer lifecycles, and explicit coverage of edge cases that production will inevitably surface. ## The Stakes Are Different When a bug appears in your product's core features, you fix it and deploy. Users who encountered the bug might be annoyed, but they refresh and continue. Billing bugs carry different consequences. Double-charging a customer isn't just a bad experience—it's their money incorrectly taken. Even if you refund immediately, the trust damage is real. Their bank might charge overdraft fees. They'll wonder what else might go wrong. The support interaction required to resolve the situation costs you time and goodwill. Failing to provision access after successful payment is equally damaging. The customer paid for something and didn't receive it. They'll contact support confused or angry. If support takes more than a few minutes to respond, they might dispute the charge, creating a chargeback that costs you money and affects your processor standing. Under-charging seems like a smaller problem because customers don't complain, but it accumulates. A proration bug that saves customers five dollars per transaction loses you thousands over time. These bugs are harder to detect precisely because no one reports them. Billing code demands a higher standard of testing than typical application code. You can't rely on production feedback to catch issues—that feedback arrives as angry customers and financial corrections. ## Test Mode: Your Parallel Universe Payment processors provide test modes that simulate real transactions without moving real money. Test mode is your primary tool for billing integration testing. If you're not using it extensively, every production release is another roll of the dice. Test mode uses separate credentials from production. Your API calls go to the same endpoints but operate on test data rather than real accounts. Test credit cards produce predictable outcomes: certain numbers always succeed, others always fail with specific error codes. The key benefit of test mode is reproducibility. You can create the same scenario repeatedly without accumulating real charges. When testing a webhook handler, you can trigger the same event type multiple times until you're confident your code handles it correctly. Test mode also provides tools production doesn't. You can manually trigger events that would be difficult to produce organically. You can adjust timestamps to simulate delayed webhooks. You can create scenarios that take months to occur naturally, like subscription anniversaries or annual renewal processing. Configure your local development and staging environments to use test mode credentials exclusively. Keep production credentials out of development entirely—an accidental charge against a real account while debugging is easily avoided by never having production credentials accessible during development. ## The Customer Lifecycle Test The most important billing test simulates a complete customer lifecycle from signup through cancellation. This test catches integration issues that unit tests miss because it exercises the entire flow as a real customer would experience it. Start by creating a new customer with a test card. Walk through your checkout flow as a user would, verifying each step. Confirm that successful payment creates the expected records in your database. Verify that the customer immediately has access to paid features. Next, trigger a billing cycle. In test mode, you can advance time or manually generate invoices. Confirm that renewal charges process correctly and that access continues without interruption. Test an upgrade flow. Move the customer from one plan to another, verifying proration is calculated correctly and that entitlements change appropriately. Check both the immediate effect and the impact on the next billing cycle. Test a downgrade flow. Move the customer to a cheaper plan and verify the same concerns: correct proration, appropriate entitlement changes, and accurate future billing. Simulate a payment failure. Use a test card number that declines to trigger failure handling. Verify that your application enters the appropriate state and that any grace period logic activates. Then "update" the payment method to a working test card and confirm recovery works. Finally, cancel the subscription. Verify that cancellation processes correctly, that access is revoked at the appropriate time (immediately or at period end, depending on your policy), and that no further charges occur. This complete lifecycle test should run automatically as part of your deployment pipeline. If any step fails, deployment should stop. Billing bugs are too expensive to catch in production. ## Edge Cases You'll Inevitably Encounter Beyond the happy path, specific edge cases deserve explicit testing because they're guaranteed to occur in production. Concurrent operations cause race conditions that sequential testing won't catch. What happens if a customer hits "upgrade" in two browser tabs simultaneously? What if a webhook arrives while your application is still processing a related event? Test these scenarios explicitly by deliberately introducing delays and parallel requests. Currency edge cases emerge when you support international customers. Rounding errors that seem insignificant in dollars become visible in currencies with different decimal conventions. Some currencies don't support cents at all. If you support multiple currencies, test the full lifecycle in each. Timezone boundaries affect billing dates. A customer in Sydney who signed up at 11pm experiences a different "month" than your server running in UTC. Test subscription creation and renewal at timezone boundary times to ensure billing dates behave consistently. Refunds bring their own complications. Full refunds should be straightforward, but partial refunds interact with proration in complex ways. What if a customer upgrades, then requests a refund for the original charge? Each scenario needs defined behaviour and explicit testing. Expired cards during trial conversion catch many teams by surprise. A customer signs up for a free trial, their card expires during the trial, and the conversion charge fails. Your test suite should verify that trial conversion handles payment failure gracefully. ## Testing Webhooks Webhooks form the nervous system of billing integration, and they deserve dedicated testing attention. A webhook handler that mostly works will cause invisible problems when it mishandles certain event types. First, verify webhook signature validation. Your handler should reject requests that lack valid signatures. Accepting unsigned webhooks is a security vulnerability that allows attackers to manipulate your application's billing state. Test each webhook event type your application handles. Don't assume that handling `invoice.paid` correctly means `invoice.payment_failed` works too. The payload structures differ, and your handler logic differs. Every event type needs explicit verification. Test out-of-order delivery. Webhooks can arrive in unexpected sequences—a subscription update event might arrive before the subscription created event. Write handlers that tolerate ordering variations, typically by fetching current state rather than assuming webhook order reflects temporal order. Test duplicate delivery. Your payment processor might deliver the same webhook multiple times as a retry mechanism. Handlers must be idempotent: processing the same event twice should produce the same outcome as processing it once. Verify this explicitly by sending the same webhook payload twice. Test delayed delivery. What happens if a webhook arrives hours or days late? If your handler assumes webhooks are recent, it might make incorrect decisions about current state. Use the event's embedded timestamp, not current time, when timing matters. ## Staging Environment Best Practices A staging environment that mirrors production catches issues that local testing misses. But testing billing in staging requires care to avoid cross-contamination between environments. Use completely separate test mode credentials for staging versus development. This prevents developers from accidentally interfering with staging test data and keeps test data isolated between environments. Populate staging with realistic test data. A single test customer doesn't exercise your billing integration the way hundreds of customers in various states will. Create customers across different plans, different lifecycle stages, and different edge case conditions. Reset staging data periodically. Test data accumulates and becomes unrealistic over time. A weekly reset to a known baseline keeps staging useful. Automate this reset so it actually happens. Run the full lifecycle test suite against staging before every production deployment. If it works in staging, you have reasonable confidence it'll work in production. If it fails in staging, you've caught a problem cheaply. ## Monitoring Production Billing Testing reduces risk, but monitoring catches what testing missed. Billing systems need specific monitoring beyond standard application metrics. Track payment success rate. A sudden drop indicates a problem—whether in your integration, your payment processor, or payment methods expiring across your customer base. Set alerts for when success rate falls below historical norms. Monitor webhook processing. Track receipt of expected webhooks and handler success rates. Missing webhooks or handler failures can signal integration problems that affect customer experience without generating obvious errors. Reconcile subscription state. Periodically compare your application's understanding of subscription state against your payment processor's records. Discrepancies indicate synchronisation bugs that need investigation. Watch for anomalies in billing amounts. Unexpected charges, unusual proration calculations, or pricing that doesn't match current plans can reveal bugs in your billing logic—bugs that cost you or your customers money. These monitors should feed into alerts that reach people who can act on them. Billing anomalies discovered days later are far harder to resolve than those caught in real-time. --- _Salable provides a complete [test mode environment](https://salable.app/docs/quick-start#test-mode) that mirrors production, making it straightforward to test your entire billing integration before going live. Run your test card through the full subscription lifecycle and verify everything works before you charge a real customer._ --- ### What Stripe Won't Tell You About Subscription Billing Source: https://salable.app/blog/insights/what-stripe-wont-tell-you # What Stripe Won't Tell You About Subscription Billing Stripe's documentation makes subscription billing look solved. Create a product, attach a price, generate a checkout session, done. The code samples work. The webhooks fire. It feels like the hard part's over. Then the cracks start showing. A customer asks for per-seat pricing, and your flat-rate setup can't handle it. Sales closes a deal with custom terms, and now you need a bespoke plan with entitlements that exist for exactly one customer. You raise prices and discover existing subscribers need grandfathering. Payments start failing—expired cards, insufficient funds, SCA challenges abandoned mid-flow—and each failure mode needs its own handling. Stripe handles payments brilliantly—fraud detection, international cards, tax calculation, compliance across dozens of jurisdictions. They've earned their reputation. But payment processing and subscription management are different problems. Stripe solves the first and leaves you to figure out the second. ## The Entitlement Gap Stripe stores the fact that a customer subscribes to "Professional Plan" at \$99/month. Your application needs to translate that into capabilities: this customer can access advanced analytics, create unlimited projects, and invite up to 25 team members. Stripe doesn't maintain this mapping—your features aren't part of its domain. The entitlement logic, the rules that translate subscription status into application capabilities, lives entirely in your code. Most developers start by hard-coding entitlement checks. You look up the customer's subscription, check if the product ID matches your Pro plan, and enable the feature. It works. You ship it and move on. The problem surfaces later. You rename the Pro plan to Professional. You introduce a new tier between Basic and Pro. You need to give a customer access to one Pro feature without upgrading their whole plan. Every hard-coded check now needs revisiting—except they're scattered across your codebase, written by different people at different times, and nobody documented where they all are. Some get updated. Some don't. Customers start seeing inconsistent access, and debugging means auditing every feature gate in your application. Then sales closes an enterprise deal with custom terms: Pro features, plus one capability from Enterprise, minus a feature they'll never use, at a price that matches no tier. Now you need a plan that exists for exactly one customer—with its own entitlement set, its own price, and its own renewal terms. Your entitlement system wasn't built for this. You've been checking "is this customer on Pro?" to gate features. Now you need "is this customer on Pro OR on the Acme Corp custom plan OR on any plan that includes the analytics entitlement?" The conditional logic sprawls, special cases multiply, and the next custom deal makes it worse. ## Seat Management Complexity Per-seat pricing seems simple: charge \$10 per user, count users, multiply. Stripe even supports per-seat subscriptions through quantity-based pricing. Create a subscription with quantity 5, charge \$50/month. Done. The complexity emerges when seats change. A customer adds their sixth team member on day 15 of the billing cycle. What happens? Do you prorate—charging half of \$10 for the half-month remaining? Bill the full amount immediately? Wait until the next cycle? Stripe can calculate proration, but your application decides when to trigger it, detects the new seat, calls the API to update quantity, and handles the resulting invoice. Can customers add seats through self-service, or must they contact sales? If they add seats and remove them the same day, do they get credit? Every customer eventually asks these questions. Stripe provides no answers. Most SaaS companies underestimate this complexity because per-seat pricing looks like simple multiplication. In practice, it's a whole category of product features: seat allocation, seat management, seat transfer, utilization reporting, overage handling, and grace periods for temporary overages. Stripe handles none of this—a "seat" is an abstraction in your application, not something Stripe represents in its data model. ## Price Changes and the Grandfathering Problem Raising prices sounds simple—update the number, existing customers keep paying the old rate, new customers pay more. Stripe lets you create a new price object easily enough. The problems start when you try to track who should pay what. Stripe doesn't know which customers are grandfathered. It stores subscriptions with price IDs, but nothing about why a customer qualifies for a particular rate—when they signed up, what promotion they used, whether sales negotiated terms. That logic lives in your code. Now multiply this across several price changes over the years. You have customers on the 2022 rate, the 2023 rate, the current rate, and a handful on custom rates that sales negotiated. Each cohort needs tracking. Each needs to migrate cleanly if they change plans. Some expect to keep legacy pricing on upgrades; others don't. The permutations grow faster than you'd expect, and every edge case is a support ticket waiting to happen. ## Usage-Based Billing: A Different Animal Flat-rate and per-seat billing charge for what customers have. Usage-based billing charges for what customers do. That distinction changes everything. With a flat subscription, you know the invoice amount before the billing cycle starts. With usage-based pricing, the invoice doesn't exist until the cycle ends and you've tallied consumption. You're not just tracking subscriptions—you're tracking events, aggregating them accurately, and generating invoices from data that accumulates in real time. Stripe supports metered billing through usage records, but the tracking infrastructure is yours to build. Every API call, every GB stored, every message sent needs to be counted, attributed to the right customer, and reported to Stripe before the invoice finalizes. Miss events and you underbill. Double-count and you'll have angry customers disputing invoices. Delay reporting and the invoice goes out wrong. Sooner or later, customers will dispute charges, and you'll need logs detailed enough to prove them. Tracking systems fail, forcing a choice between blocking usage, reconstructing usage from application logs, or accepting lost revenue. Customers will want real-time visibility, which means building dashboards on top of your metering infrastructure. ## The Webhook Maze Stripe communicates through webhooks that notify your application when events occur. Payment succeeds, subscription renews, invoice paid, dispute opened. The documentation makes it look simple: listen for events, update your records, move on. Reality is messier. Webhooks can arrive out of order, arrive twice, or not arrive at all. An `invoice.paid` event might land before the `invoice.created` that should precede it. Network issues or deployment downtime can cause missed deliveries. Your handlers need to be idempotent, order-independent, and backed by reconciliation logic that periodically compares your state against Stripe's source of truth. Volume compounds the challenge. A single customer action can trigger half a dozen events—subscription updated, invoice created, invoice finalized, payment intent created, payment intent succeeded, invoice paid. Your handlers need to process this efficiently and avoid redundant work when multiple events describe the same underlying change. ## When Payments Fail A subscription isn't just "active" or "canceled"—it can be stuck in payment limbo, and how you handle that limbo defines your customer experience and your revenue. Cards expire. Banks decline for insufficient funds. European customers hit SCA requirements and abandon the authentication challenge. The causes vary, as do the chances of recovery and the response each demands. Stripe will retry failed payments on a schedule, but the schedule might not match your business needs. More importantly, Stripe doesn't decide what happens to the customer's access while payment is failing. Do you cut them off immediately? Give them a three-day grace period? A week? Do you email them once or start a sequence? Do you show a banner in your app or quietly retry in the background? These decisions have real consequences. Too aggressive, and you'll churn customers who would have paid after a card update. Too lenient, and you'll carry non-paying users while they decide whether your product is worth the trouble. Stripe offers basic notification emails, but the dunning process is best handled outside Stripe for more control and better retention rates. SCA adds another layer. Strong Customer Authentication means European customers may need to manually approve payments, and if they miss the approval request, the payment fails even with valid funds. You need to detect SCA-triggered failures, notify customers differently than you would for a declined card, and provide a path back to authentication. Stripe handles the authentication flow itself, but knowing when and how to re-engage the customer is on you. ## Billing Operations Beyond Payment Running a subscription business requires capabilities that sit outside payment processing entirely. Customers expect self-service: viewing invoices, updating payment methods, changing plans, canceling without emailing support. Stripe provides a customer portal, but customization is limited—if your billing experience needs to match your product's design language, you're building it yourself. Behind the scenes, finance needs revenue recognition and churn metrics; support needs tools to investigate why a customer was charged a specific amount; operations needs automation for dunning, renewal notifications, and anomaly detection. Stripe provides raw payment data. Turning that into actionable insight requires additional tooling and integration work. ## The Integration Tax Every capability Stripe doesn't provide requires custom integration. Checkout is straightforward, but then you need webhook handlers, entitlement logic, seat management, a customer portal, reporting, and operational workflows. Each piece seems manageable in isolation. Together, they represent weeks or months better spent building your core product. And billing doesn't stay contained. It touches signup flows, feature access, user management, financial reporting. The integration tax compounds over time—every pricing change, every new plan, every new feature that needs entitlement gating requires updates. Companies that built quick integrations early find themselves constrained by those decisions later, facing painful migrations or workarounds that add more complexity. ## What Comes Next The gap between Stripe and a working subscription business isn't going away. You can build the missing layer yourself—entitlements, seat management, dunning, customer portals, usage tracking—but you're essentially building billing software as a side project. The companies that pull this off usually have dedicated billing teams or founders with deep domain expertise. The alternative is infrastructure that provides the subscription management layer so you don't have to build it. That's why we built Salable. Stripe stays your payment processor; Salable handles everything between Stripe and your application—entitlements, seats, metered usage, self-service billing, the operational logic that payment processing doesn't address. Either way, the worst outcome is not choosing deliberately. Assuming checkout means billing is solved leads to tech debt, customer-facing bugs, and engineering time that should have gone to your product. Understand the scope, make a deliberate choice, and plan accordingly. --- ### The Subscription Graveyard Starts With a Failed Payment Source: https://salable.app/blog/saas-startup-guides/handling-failed-payments # The Subscription Graveyard Starts With a Failed Payment Somewhere in your customer base, a credit card is about to expire. Another customer's payment will decline because they hit their limit buying holiday gifts. A third will fail because their bank's fraud detection flagged an unfamiliar charge. These aren't edge cases; according to [Recurly's analysis of 1,200 subscription businesses](https://www.marketingcharts.com/customer-centric-83474), 7.2% of subscribers are at risk each month due to failed payments. The difference between recovering that revenue and losing those customers comes down to how you handle the failure. Aggressive dunning annoys customers, while passive approaches let subscriptions lapse silently. The right strategy balances persistence with respect. ## Why Payments Fail Understanding the causes of payment failure helps you respond appropriately. Not all failures are equal, and treating them the same leads to suboptimal recovery. Expired cards are a leading cause of failures. Credit cards have fixed expiration dates, and customers forget to update their payment details before the old card expires. These failures are entirely recoverable once the customer updates their card. Insufficient funds cause another significant portion of failures. The customer's card is valid but declined because they've reached their credit limit or overdraft limit. These failures often resolve on their own when the customer pays down their balance. Retrying a few days later frequently succeeds. Fraud prevention triggers account for many unexplained declines. Banks flag charges that deviate from a customer's typical spending pattern, and a legitimate subscription renewal sometimes gets caught in the filter. This is especially common for international transactions, first-time charges from a new vendor, or any change in price—switching from monthly to annual billing often triggers fraud checks because the charge amount jumps significantly. Customer verification often clears these blocks. Lost or stolen cards require full payment method replacement. The customer may not even realise their card was compromised until they see the failed charge notification. These take longer to resolve but are still recoverable with clear communication. Genuine account problems, where customers have left your service without canceling, or where the underlying account is closed, represent a small portion of failures. These are rarely recoverable through technical means. ## The Dunning Sequence Dunning is the process of attempting to collect overdue payments. The term sounds aggressive, but effective dunning is actually about customer communication rather than debt collection. You're reminding customers about a payment they likely intended to make. A typical dunning sequence combines automatic payment retries with customer notifications. The payment processor retries the charge on a schedule, while your system sends emails that explain the situation and guide customers to resolution. The retry schedule matters. Retrying immediately after a failure rarely works; whatever caused the decline probably hasn't changed in the last few seconds. Retrying the next day is better. Retrying in three to five days is often optimal for insufficient funds cases, giving customers time to pay down their balance. Most payment processors offer "smart retries" that schedule attempts based on historical success patterns for similar decline codes. The communication cadence runs parallel to retries. A first email immediately after failure alerts the customer to the problem. A second email a few days later reminds them if the issue persists. A final warning before service interruption gives urgency without being premature. Each email should be clear about what happened, why it matters, and how to fix it. ## Crafting Effective Recovery Emails The emails you send during dunning determine whether customers fix the problem or ignore it until their access is revoked. Effective recovery emails share several characteristics. Lead with what happened, not with blame. "Your payment didn't go through" is better than "Your card was declined." The first framing treats the failure as a glitch to resolve; the second implies the customer did something wrong. Explain the consequence clearly but without alarm. "If we can't process payment by [date], your access to [product] will be paused" is informative. "YOUR ACCOUNT WILL BE SUSPENDED" is aggressive and off-putting. Make resolution easy. Include a direct link to update payment methods. Don't make customers log in and navigate to find billing settings. The fewer steps between reading the email and fixing the problem, the higher your recovery rate. Provide context customers might need. If the charge amount is included, they can verify it matches their expectations. If your company name shows differently on statements than in the product, mention that so customers don't mistake your charge for fraud. Consider timing and frequency carefully. Sending three emails in three days feels like harassment. Sending one email and then cutting off access feels abrupt. Space communications appropriately and increase urgency gradually. ## Grace Periods and Service Continuity What happens to a customer's access while payment is failing? The answer involves tradeoffs between revenue protection and customer experience. Immediate suspension is the strictest approach: the moment payment fails, access is revoked. This protects against extended free usage but creates a harsh experience for customers who simply forgot to update an expired card. A legitimate customer locked out of your product at a critical moment will be frustrated, regardless of whose fault the payment failure was. Grace periods provide time for recovery without service interruption. During the grace period, the customer retains access while retries and dunning proceed. A typical grace period is seven to fourteen days, long enough for most failures to resolve but short enough to limit exposure. Some products implement degraded access during grace periods rather than full access. Core functionality works, but premium features are restricted. This reminds customers something is wrong without completely blocking their work. The right approach depends on your product and customer base. Products with high switching costs can be stricter; customers will fix payment issues to maintain access to irreplaceable data or workflows. Products with easy alternatives need gentler handling; frustrated customers will simply leave. ## Revenue Recovery Metrics Tracking payment recovery helps you understand how your dunning process performs and where to focus improvements. **Initial failure rate** measures how many payment attempts fail on first try. This metric is largely outside your control, driven by your customer base's payment method characteristics. But watching trends can reveal problems: a sudden spike might indicate an issue with your payment processor configuration. **Recovery rate** measures what percentage of failed payments eventually succeed. [Recurly's research](https://www.marketingcharts.com/customer-centric-83474) found that automated decline management saved 69.4% of subscribers at risk of involuntary churn. If your recovery rate falls significantly below this benchmark, your dunning process needs attention. **Time to recovery** tracks how long it takes to resolve payment failures. Faster is better, both for cash flow and customer experience. If most recoveries happen in the first three days, extending your retry period to three weeks isn't adding value. **Final churn rate from payment failure** shows how many customers you ultimately lose due to payment issues rather than intentional cancellation. This is the metric dunning aims to minimise. Some payment churn is inevitable, but high rates suggest process problems. ## Proactive Prevention The best dunning strategy is preventing failures in the first place. Several practices reduce the volume of failures you need to handle. Card account updater services automatically refresh stored card details when customers receive new cards. [Nearly 30% of payment cards in the U.S. are reissued each year](https://hostmerchantservices.com/2026/01/involuntary-churn/) due to expiration, loss, or fraud—account updaters capture these changes in the background, preventing failures before they happen. Expiration warnings alert customers before their card expires. A simple email a month before expiration, prompting them to update their payment method, prevents many failures before they happen. Retry timing based on card type can improve recovery rates. Corporate cards are more likely to succeed on weekdays when finance teams are active. Consumer cards may do better after typical paydays. Smart retry configurations apply these patterns automatically, scheduling attempts when similar cards historically succeed. Clear charge descriptions prevent fraud flags. If your statement descriptor is "CORP12345" instead of your company name, customers and their banks are more likely to flag charges as suspicious. Use descriptors that customers will recognise. ## Building the Recovery Stack Implementing payment recovery involves coordination between your payment processor, your application, and your communication systems. Your payment processor handles retries and provides webhook notifications when payments fail or succeed. Configure retry schedules appropriately and ensure you're receiving failure webhooks reliably. Most processors offer detailed decline codes that help you understand why payments failed. Your application needs to track subscription status and respond to payment events. When a payment fails, the subscription enters a grace period. When retries succeed, it returns to normal. When the grace period expires without recovery, access is suspended. Your communication system sends emails based on payment events. This might be built into your application, handled by your billing platform, or managed through a dedicated email service. Whichever approach, ensure emails are triggered reliably and track engagement metrics. The integration between these components needs to be robust. A missed webhook or failed email can mean lost revenue. Test failure scenarios explicitly, not just happy paths. --- When you're using Salable with Stripe, payment recovery runs automatically. Stripe's smart retry logic handles retry scheduling, while Salable manages grace periods and subscription state transitions. You configure the rules; the system executes them. To see how failed payments flow through the stack, check out the [subscription management documentation](https://salable.app/docs/subscriptions-and-billing). --- ### Landing Your First Whale Source: https://salable.app/blog/saas-startup-guides/landing-your-first-whale # Landing Your First Whale You can go from idea to deployed product in a weekend. AI writes the code, Vercel deploys it, Stripe takes the first payment. A task that took a team of engineers three months now fits between Friday evening and Sunday night. Your first users are individuals. Then small teams sign up. You start seeing the same company email domain appearing across multiple accounts—people from the same organisation finding you independently. This is bottom-up adoption, and it's the first signal that enterprise interest is forming. Then the questions change. "Can we get an invoice instead of paying by credit card?" "Can one person manage billing for our whole team?" "Do you have a security policy we can review?" The first enterprise signals are mundane, and you've probably already seen some of them. The gap between a product people love and one their procurement team can sign off on is real, but it's smaller than it looks. Even before enterprise is on your radar, you should be thinking about the basics: least privilege access, whether data is encrypted in transit, whether you could hand someone a written security policy if they asked. These aren't enterprise requirements. They're good practice at any stage. If you've been building with those foundations in place, the enterprise conversation starts from a position of strength rather than scrambling to catch up. Founders who close that gap are setting themselves up to tap a different tier of revenue. Enterprise deals average \$100K–\$500K in annual contract value, with net dollar retention rates between 120% and 140%. A single enterprise customer can outweigh hundreds of self-serve signups. ## The Product Was the Easy Part Slack launched in February 2014 with per-seat pricing: free for small teams, paid per user when you outgrew the free tier. Their S-1 filing describes what happened next as "organizational virality"—one team would adopt Slack, colleagues in other departments would notice, and usage spread organically across the company. By the time IT and procurement got involved, Slack was already embedded in daily workflows. The problem was that none of this organic adoption came with the infrastructure enterprises needed to formalise it. IT wanted centralised provisioning, security wanted audit trails, and finance wanted consolidated billing across divisions. Slack had built a product enterprises were already using. Now they needed to build the wrapper that let enterprises pay for it properly. Three years after launch, Slack shipped Enterprise Grid: centralised administration, SSO, compliance controls, and consolidated billing across departments. Capital One and IBM were among the launch customers. Their S-1 reported 575 customers paying more than \$100K a year, with IBM running 360,000 seats. That enterprise packaging turned a chat tool into a \$27.7 billion acquisition. Dropbox, GitHub, and Zoom followed a similar pattern. Usage spread organically, executives noticed, and when those companies added enterprise infrastructure, large customers formalised what was already happening. None of them rewrote the core product. They added the compliance and billing layer that procurement teams require. Some companies reject this path and do well. Basecamp built a \$25–30 million-a-year business with fifty employees by refusing enterprise complexity: flat-rate pricing, no sales team. David Heinemeier Hansson called per-seat pricing "a tax on growth." Mailchimp skipped enterprise sales too, and Intuit bought them for \$12 billion. But if an enterprise buyer has found your product and wants to write a six-figure cheque, the question is whether you're ready when they show up. ## The Compliance Conversation Enterprise compliance sounds intimidating, but the requirements are well-documented and the path is incremental. The conversation usually starts with a security questionnaire—a standard form asking how you handle data, who has access, and what controls you have in place. Knowing what to expect makes it easier to prepare. SSO comes first. Enterprise organisations manage employee access through centralised identity providers like Okta and Azure AD. SAML or OIDC support lets employees log in through systems their security team trusts, and it unblocks the rest of the compliance conversation. Libraries like WorkOS and Auth0 have solved the hard parts. You're looking at days of integration work. SOC 2 comes next, and it's more accessible than its reputation. Platforms like Vanta and Drata have turned what used to be a six-figure consulting engagement into a guided, automated process. A SOC 2 Type I report is a point-in-time assessment. An auditor confirms your security controls are designed well. Most mid-market security reviews accept Type I, and you can complete it in weeks. SOC 2 Type II evaluates whether those controls worked over a sustained period, six to twelve months. Larger enterprises require it, but by the time you need Type II, Type I revenue is helping fund the process. Beyond SOC 2, enterprise buyers expect encryption at rest and in transit as a baseline, plus documented security policies and data processing agreements for EU customers. If you're selling internationally, ISO 27001 may come up. You build each requirement on the last. The security practices you implement for SOC 2 form the foundation for what follows. Compliance is incremental. Each certification opens new market segments that help justify the investment in the next. Slack built its compliance portfolio over years—SOC 2 first, then [Enterprise Key Management](https://slack.com/blog/transformation/introduction-enterprise-key-management-for-security) and [HIPAA support](https://slack.com/blog/transformation/health-care-hipaa-compliance-slack) in 2019, [FedRAMP Moderate](https://slack.com/blog/transformation/introducing-new-layers-of-enterprise-grade-security) in 2020, and [GovSlack with FedRAMP High](https://slack.com/blog/news/govslack-secure-compliant-government-work) in 2022. ## Pricing That Doesn't Break at Scale ### When Flat-Rate Falls Apart Your \$29/month plan works for individual users. Then a 500-person company asks about pricing, and you realise the plan wasn't designed for this conversation. Charge \$29 per seat and the enterprise buyer sees a \$174,000 annual commitment, more than they've validated in their pilot. Offer a flat \$29/month regardless of team size and you've left six figures on the table while absorbing the infrastructure costs of five hundred users. Neither option works because neither accounts for the reality that more users means more load on your systems, more support, more storage—costs that scale with headcount even if your pricing doesn't. This is where volume discounts become attractive. Offering a lower per-seat price at higher quantities gives enterprise buyers a reason to consolidate and commit, while keeping your revenue closer to the actual cost of serving them. Volume pricing sets a single per-unit rate based on quantity—the more seats, the lower the price per seat. Graduated pricing takes a different approach, charging each tier at its own rate so the first fifty seats cost more than the next hundred. Most SaaS products selling to enterprises do better with graduated pricing because it rewards growth without punishing early commitment, and it keeps your revenue aligned with the infrastructure costs each tier of usage creates. Slack solved a different pricing problem with "fair billing," charging only for users active in the last twenty-eight days. IBM was considering a 360,000-seat rollout, and paying for thousands of inactive accounts would have killed the deal. With fair billing, IBM could roll out across the company and pay only for seats people used. ### Designing for Land-and-Expand Enterprise deals start small: a team of eight on your free tier, a department of forty on a paid plan, then a VP asking procurement to formalise what three hundred employees are using. Consolidation is a priority for large organisations where possible—five teams paying full price on separate accounts costs more than one negotiated contract. The trade-off is dead seats: bulk deals inevitably include people who rarely log in, which is why models like Slack's fair billing work well at this stage. Your pricing needs to accommodate that growth, from a free signup to a formal enterprise contract, without forcing a migration along the way. Mailchimp proved this. After introducing a generous free tier, their user base jumped from 85,000 to 450,000 in a year. Most free users didn't upgrade right away, but they planted Mailchimp inside organisations. A marketing team that needed email automation at scale upgraded the tool they knew rather than evaluating competitors. Remove the entry barrier and your existing users sell the product for you. You also compound revenue from customers you've already landed. Slack's net dollar retention of 143% came from more teams within each organisation adopting the product and upgrading to higher tiers, year after year. Price for the pilot and design for the expansion. ## Start with Pricing Volume discounts, graduated tiers, per-seat proration, annual contracts—you can set all of this up today with existing tooling. Enterprise pricing on your page tells buyers the door is open, and when one of them bites, you're talking numbers rather than scrambling to figure out what to charge. SSO, security audits, certifications are a longer road. They don't need to be finished before your pricing says you're serious. Enterprise billing introduces two wrinkles that catch founders off guard. A 500-person company buying your product splits into two relationships: the person who signs the contract and the people who use it. The CTO or finance team manages billing; 500 employees need access. Your billing system needs to separate the payment relationship from the access grants, or you end up building multi-tenant billing logic from scratch. Salable's owner/grantee model handles this by design. The owner holds the subscription, grantees get access through groups under that owner, and you control the mapping. The second wrinkle is feature access. Enterprises expect different capabilities at different price points, and your pricing will evolve as you learn what customers need. If you've hardcoded checks like `if (plan === 'enterprise')` throughout your codebase, changing a plan means redeploying code. Salable's entitlement system lets you define named capabilities and assign them to plans in a dashboard. Your code checks for `can_export_data` or `seats_over_100`. You adjust which plans grant those capabilities in Salable without touching your codebase. You configure all of this in a dashboard, and your engineering team stays on the product and compliance. That investment matters most during growth. Zoom went from 344 customers paying more than \$100K to 1,999 in two years. If you're adding enterprise customers that fast, your billing infrastructure needs to handle the volume. ## From First Whale to Enterprise Motion Your first enterprise customer teaches you what your market requires: the security questionnaire, the procurement sticking points. That knowledge turns one deal into a repeatable process. And the revenue from that first deal, \$100K or more in annual contract value, funds the compliance and billing infrastructure that makes the second deal faster to close. Slack built the infrastructure to land Capital One. That same infrastructure later supported IBM. Slack used the same playbook to reach 1,183 customers paying more than \$100K a year, accounting for 49% of their revenue. Each deal funded the infrastructure for the next. If enterprise is your path—if someone's asking for invoices, team billing, or a security policy—the gap between your product and their procurement process is smaller than it looks. The playbook is documented, each step funds the next, and you don't have to build the billing layer yourself. You've built something enterprises want. The rest is execution. --- _Salable handles [tiered pricing](https://salable.app/docs/products-and-pricing) and [per-seat billing](https://salable.app/docs/grantee-groups) with [entitlement enforcement](https://salable.app/docs/understanding-entitlements) so you can focus engineering time on your product and compliance._ --- ### Why Per-Seat Pricing Is Way Harder Than It Looks Source: https://salable.app/blog/saas-startup-guides/team-subscriptions # Why Per-Seat Pricing Is Way Harder Than It Looks Your first customers were individuals, and per-user billing was straightforward. But now a company wants to buy seats for their whole team, and suddenly simple questions get complicated. Who receives the invoice: the person who signed up or their finance department? How do team members get access without sharing credentials? What happens when someone leaves the team mid-billing-cycle? Team subscriptions aren't just per-seat pricing multiplied out. They're a different model with distinct concepts: billing owners versus users, seat allocation and limits, and organisational access control. ## The Fundamental Split: Who Pays vs. Who Uses Individual subscriptions conflate two roles that team subscriptions must separate. When a solo user buys a subscription, the same person handles billing and uses the product. When a company buys seats for a team, the person responsible for payment may never log into the product at all. The billing owner is the person or entity responsible for payment. They receive invoices, manage payment methods, and handle subscription changes. In team scenarios, this is often a finance administrator or procurement team rather than an end user. Grantees are the people who actually use the product. They log in, access features, and derive value from the subscription. They might not know or care what the subscription costs; they just need access to do their work. This distinction is fundamental to getting team billing right. As you design each feature and flow, ask yourself: is this for the billing owner or the grantee? The billing portal, where customers update payment methods and view invoices, is for owners. The product itself is for grantees. The seat management interface, where administrators add and remove team members, sits in between and might serve both audiences depending on your organisational model. ## Modelling Team Structure Once you've separated billing from access, you need to model how teams actually work. The simplest approach is a flat list of users attached to a subscription. The owner pays, and every grantee in the list has access. This works for small teams with straightforward needs. More sophisticated products need hierarchical structures. A company might have multiple departments, each needing separate seat pools while sharing a single billing relationship. Or they might need to assign different permission levels within the team: admins who can manage seats, editors who can modify content, and viewers who can only read. The temptation is to build for the complex case immediately, but this adds significant engineering overhead. Start with the flat model unless you have concrete evidence that customers need hierarchy. Most early team customers will be small enough that a simple list of seats suffices. Whatever structure you choose, you'll need a way to identify which grantee group a user belongs to. This could be an explicit team ID, an email domain, or a reference to an external identity provider. That identifier connects users to their subscription and determines what they can access. ## Seat Allocation and Limits Per-seat pricing means tracking how many seats are used and enforcing limits. This sounds simple, but the details matter for both customer experience and revenue. The first decision is whether seats are allocated or consumed. In an allocation model, the owner explicitly assigns seats to specific users: Alice gets seat one, Bob gets seat two. The seat remains allocated even if the user doesn't log in regularly. This model is straightforward to understand and administer. In a consumption model, seats are claimed on a first-come, first-served basis up to the limit. Any user with the right invitation or email domain can claim a seat. This model is more flexible but can lead to conflicts when more people want access than seats allow. The second decision is what happens when limits are reached. The strict approach blocks new users from joining until a seat is freed or more seats are purchased. This protects revenue but creates friction when a team needs to add someone urgently. The flexible approach allows temporary overage, charging for the additional seat on the next billing cycle or flagging the account for upgrade. This prioritises user experience but requires careful communication to avoid billing surprises. Most SaaS products adopt a hybrid: hard limits on the subscription quantity, but a self-serve upgrade path that's fast enough that hitting limits doesn't block work. The team lead can add more seats in seconds, making the limit a speed bump rather than a wall. ## Managing Seat Changes Mid-Cycle Team membership changes constantly. People join companies, leave companies, change roles, and switch teams. Your billing logic must accommodate these changes without creating accounting nightmares or support tickets. When someone joins a team mid-cycle, you have options. You could charge nothing until the next billing period, effectively giving away free access. You could charge a prorated amount for the remainder of the current period. Or you could charge the full period price regardless of timing. Proration is the most common approach. If a customer adds a seat halfway through the month, they pay half the monthly seat price. This feels fair and matches customer expectations. The tricky part is presentation: customers should see clear line items that explain the prorated charges. When someone leaves a team, the question is whether to issue credit. Some products reduce the seat count immediately but don't credit the unused portion. Others prorate a credit that applies to the next invoice. The right answer depends on your pricing and customer expectations. The simplest implementation is to handle seat changes at the billing cycle boundary. Seats can only be added or removed at renewal, and changes made mid-cycle take effect at the next renewal. This eliminates proration complexity entirely. It works for products where seat changes are infrequent, but frustrates customers who need to add users urgently. ## Invitations and Onboarding Team members need a way to claim their seats and start using the product. The invitation flow bridges billing and product access, and getting it right shapes your customers' first impression of working with you. A typical flow works like this: the billing owner or team administrator initiates an invitation by entering email addresses. Your system sends invitation emails with unique links. Recipients click the link, create or connect an account, and join the team. The seat is consumed when they complete onboarding. Tokens in the invitation link handle most edge cases. The recipient's sign-in email doesn't need to match the invited address—the token validates the invitation, not the email. You'll still want clear handling for expired invitations and users who already belong to another team, but the token-based approach keeps the common path simple. Some companies want to enforce that team members use their corporate email domain. This is a separate concern from invitations—it's about identity policy, not access flow. If your customer is acme.com, they might require all team members to sign in with @acme.com addresses regardless of how they were invited. Single sign-on adds another dimension. Enterprise customers often require SSO integration, where their corporate directory manages identity. In these setups, seat allocation can happen automatically based on directory group membership, with no manual invitation required. ## Owners, Grantees, and Multi-Tenancy In Salable, the hierarchy is simple: an owner can have many groups, and groups can have many grantees. The owner is the top-level tenant—the organisation or account that holds subscriptions. Groups let you organise grantees within that owner, whether that's departments, teams, or any structure that fits your product. This matters because a single grantee can exist across multiple owners. Someone might have a personal account on your service and also belong to an enterprise organisation with a different subscription tier. When you check entitlements, you pass the granteeId (typically the user ID) and filter by owner to get the capabilities for their current context. Switch tenants, filter by a different owner, and the same user sees different entitlements. Salable doesn't dictate who can manage subscriptions—that's your RBAC implementation. By making the owner the overarching group, you decide which members should have access to billing, seat management, and admin functions. Some teams want only the original purchaser to manage billing. Others delegate to multiple administrators. Your access control, your rules. The administrative experience you build depends on your customers. Early customers with five-person teams need something simple. Enterprise customers managing hundreds of users need delegation, bulk operations, and audit logs. Start simple and expand as your customer base demands it. ## How Salable Avoids Common Mistakes Developers building team subscriptions from scratch hit the same problems repeatedly. Salable's owner/grantee model sidesteps them by design. Coupling user identity to subscription identity creates problems when subscriptions change hands—the original purchaser's account becomes inseparable from the billing. Salable keeps these separate. The owner holds the subscription; grantees get access through groups under that owner. Transfer ownership, and grantees keep their access without disruption. The solo-to-team transition trips up many implementations. A solo user upgrades, and suddenly their account needs to become a team with them as a member. With Salable, a solo user is just an owner with one group and one grantee—themselves. Adding team members means adding grantees to a group. No migration, no restructuring. Hardcoding seat limits in your application makes plan changes painful. Salable treats limits as configuration. Change a plan's seat count in the dashboard, and existing subscriptions reflect it automatically. Your code asks Salable what the limits are; it never stores them locally. When someone loses access—removed from a team, subscription cancelled—your app still needs to handle that gracefully. Salable makes this simple: check their entitlements, and if they have none, show them why and what they can do about it. ## Building for Growth Team subscriptions are often the first step toward enterprise features. As your customers grow, they'll request capabilities that go beyond basic seat management. Hierarchical organisation structures let companies model departments, teams, and sub-teams within a single billing relationship, enabling delegated administration and budget allocation. Role-based access control restricts what different team members can do within the product. Not everyone needs full access; some users should be viewers, others editors, others admins. Usage allocation lets teams distribute limits across organisational units. If the subscription includes 100,000 API calls, different departments might have separate quotas. Audit logging tracks who did what and when, satisfying compliance requirements and enabling security reviews. You don't need these features at launch. But designing your team model with an eye toward future requirements helps you avoid architectural dead ends. The separation of billing owner from grantee, and the explicit modelling of team membership, creates the foundation that enterprise features build on. --- _Salable's [owner/grantee model](https://salable.app/docs/grantee-groups) handles the complexity of team subscriptions out of the box. You focus on your product; we'll handle who gets access to it._ --- ### Going from Conceptualisation to Monetization Source: https://salable.app/blog/saas-startup-guides/going-from-conceptualisation-to-monetization # Going from Conceptualisation to Monetization Monetizing your app from concept through to obtaining your first customer is a tricky thing, nobody will deny that — It’s a struggle I’ve faced, myself, fairly recently with the development of a product in my own personal time, Kiwiki. ## Every Great Idea has an Origin Last March I was busy contributing to various video game communities in my spare time, and in one of them, I had decided to build a community wiki website for an ad-free, clean experience that was filled with bespoke features; website and wiki builders in the market lacked the level of customization I required, were too taxing on battery life, too riddled with intrusive ads, and were almost too simplified for my use case; these wikis were complex systems built off of the back of over 30,000 lines of JSON code for movesets, data values, and descriptions — Once the website launched, having been custom-developed by myself, it was a roaring success within the community hitting over 100,000 unique impressions in the space of three months, with over 20,000 active monthly users; unfortunately the game petered off and became unsupported within a year, making the website unfeasible to continue hosting as the community for the game diminished. Saying this, it gave inspiration towards a unique product — A website generator focused on wikis that could be extensively customized, branded, and shipped using plain JSON data to build automatic APIs for large amounts of data, and to promote component reuse and dynamic experiences. I had other communities asking me to build wikis for their games — some of which were far, far larger than I could possibly record, with hundreds of thousands of lines of text and values — however I was only one person. What if I built this tool so that they could self-serve? ## Going from Concept to Payment From this, Kiwiki was born — But I had one major problem; how do I monetize an application like this? First, I theorized my pricing models — Would I charge per page? Per wiki? Per user? Per team? Would I have tiers? Would I support upgrading and downgrading for tiers? All of these questions were critical to my implementation, and Salable was — as a platform — more than simple enough to integrate and experiment with in order to find a solution that best worked for my product. By establishing an authentication layer on my application via Clerk, and then creating Entitlements in Salable, I was able to meter the amount of pages a plan could have, and how many users could edit a wiki instance in Kiwiki from within my code — Simply sign up to Salable, create a Product with Plans that mirrored my tiers (Free, Paid, Pro and Enterprise Plans), and create Entitlements for my unique selling points like the number of pages, number of team members per-wiki, etcetera. Each wiki instance would be a single subscription on the main Product, subscribing to one of the Plan tiers — If they wanted to upgrade or downgrade their plan, I would simply enact the change on the date of the next bill, and in our end of the code, hide the pages that exceeded the number of pages they were entitled to, or require them to cut team members if they had downgraded, for instance. By hooking onto the Entitlement name and providing it a value depending on the current user’s plan, this enabled me to be both extremely flexible, and also scalable with my plans and paywalling features. If I wanted to suddenly make a new feature tier-dependent, then I could just create a new Entitlement, add it to my plans, and my code would automatically have access to that flag to start building new paywalled experiences and limits as I needed. ## Changing my Mind Let’s also theorize if I woke up one day and decided to charge per-page, or allow users to purchase extra page slots beyond their plan — I could create a new plan as a one-off purchase with a new Entitlement flag for adding a new page to the tier’s total, and simply trigger it on a successful payment; this way I’m not locked into having a tiered pricing application, but instead can transition into a dynamic pricing application with just a few clicks, and a little bit of code. Building upgrade and downgrade flows too was an effortless task within Salable — Again, I would simply swap the user’s subscription from one Plan to another, offering the choice for the user to upgrade or downgrade at the end of their current billing cycle, or immediately via cancelling their current subscription and establishing a new subscription — This way, users had the option of both waiting out a change in tier, or triggering it immediately. ## Conclusion What would have taken me weeks — if not months — to develop both of these flows using just Stripe has taken me only minutes in Salable. Even less if you were to utilise an AI model like Claude to automatically implement Salable’s integration into your application. Whether you’re an AI evangelist or an AI purist, Salable’s docs are simple to understand and follow, and the results do honestly speak for themselves. Hopefully this blog has inspired you, whether you have an existing product or have just started conceptualizing one — Irrespective of the state of your product, or the structure of your planned pricing, Salable can handle it quickly, quietly and dynamically. --- ### Your Pricing Will Change, Your Code Shouldn’t Source: https://salable.app/blog/saas-startup-guides/entitlements-pattern # Your Pricing Will Change, Your Code Shouldn’t You've shipped your app with tier-based access controls, and it works. Users on the Pro plan get Pro features, Enterprise users get everything. Then your pricing changes. You add a new tier, rename a plan, and close a deal with a customer requesting a bespoke plan that doesn't fit your standard packages. Suddenly, you're hunting through your codebase, updating hardcoded plan names, hoping you haven't locked a paying customer out of something they bought. Every pricing model change or customer deal now requires a code change and a deployment. The entitlements pattern prevents this entirely. Instead of checking which plan a user is on, your code checks what they're allowed to do. Plans grant entitlements, entitlements gate features, and the mapping lives in the configuration. Your pricing models can evolve without your codebase knowing or caring. ## The Problem with Tier-Based Checks Development often begins with tier-based access control because it feels intuitive. You have plans called "Starter," "Pro," and "Enterprise," so your code checks which plan a user is on. The logic seems straightforward: Pro users get advanced features, Enterprise users get everything. The problems emerge slowly, then all at once. First, the checks multiply. Feature-gating code starts in one or two files, then spreads throughout your codebase as you add more premium features. Your advanced analytics component checks the tier. Your export functionality checks the tier. Your API rate limiter checks the tier. Your admin panel checks the tier. Each check is trivial in isolation, but together they form a scattered, implicit definition of what each plan includes. Second, the tiers change. You decide that "Pro" is too expensive and split it into "Pro" and "Pro Plus." Or you rename "Starter" to "Essential" for marketing reasons. Or you add a "Growth" tier between existing tiers. Each change requires finding and updating every tier check in your codebase. Miss one, and you've either given away features for free or locked paying customers out of functionality they purchased. Third, customers don't fit your tiers. A startup wants the analytics from Pro but only needs the user limits from Starter—and they want a price that reflects the mix. An enterprise customer needs one specific feature from your top tier, but nothing else that justifies the cost. When your code assumes every customer maps to exactly one tier, these deals become engineering problems. You end up hardcoding exceptions by customer ID or creating fake tiers that exist only to satisfy one contract. The tier-based approach implicitly assumes your pricing structure is stable and your feature sets map cleanly to tiers. Neither assumption holds for long. ## Entitlements Over Tiers The entitlements pattern inverts the relationship between plans and features. Instead of asking "what plan is this user on?" your code asks "does this user have this capability?" The plan determines which capabilities a user has, but plan names never appear in your feature code. Consider an export feature. With tier-based checks, your code might look like this: ```javascript if (user.plan === 'pro' || user.plan === 'enterprise') { showExportButton(); } ``` With entitlements, it becomes: ```javascript if (user.hasEntitlement('export_data')) { showExportButton(); } ``` The difference seems cosmetic, but it's architecturally significant. The first approach embeds your pricing structure into your codebase. The second decouples them entirely. Plans grant entitlements, and entitlements control features, but your feature code only references entitlements. This separation lets you restructure pricing without touching feature code. Want to split Pro into two tiers? Update the entitlement mappings. Want to offer a custom bundle? Create a plan with the right entitlements. Want to run a promotion that gives Starter users temporary access to Pro features? Grant the entitlements temporarily. Your feature code stays exactly as it was. ## Designing Your Entitlement Vocabulary The power of entitlements depends on choosing the right granularity. Too coarse, and you lose flexibility; too fine, and you're back to scattering checks everywhere, just with different names. Start by listing everything you might want to gate. This includes features, usage limits, support tiers, and integrations. Don't worry about which plan gets what yet; just identify the decision points in your product. Next, look for natural groupings. Some capabilities always go together. If every plan that gets "export_csv" also gets "export_json," you might combine them into "export_data." If "advanced_charts" and "custom_dashboards" always travel together, consider "advanced_analytics." Name entitlements for what they enable, not which plan includes them. "export_data" is better than "pro_export" because the name survives pricing changes. "unlimited_projects" is better than "enterprise_tier" because it describes a capability, not a position in your pricing hierarchy. Boolean entitlements are straightforward. The user either has the capability or doesn't. Your code checks once and proceeds accordingly. ## Implementing Entitlements with Salable Salable handles the complexity of access control so you don't have to build it yourself. You define your plans and their entitlements in the Salable dashboard, then check entitlements with a single API call. When you need to know what someone can do, you ask Salable for that grantee's entitlements. If the user belongs to multiple organisations (say, a personal account and an enterprise organisation), you filter by owner to get the entitlements for the tenant they're currently operating in. The response collates everything for that context—base plan, add-ons, and custom deals into one list of capabilities. This means your application never needs to understand your pricing structure. It doesn't know what plans exist, how much they cost, or which features belong to which tier. It just knows capabilities. When you restructure pricing, add plans, or close custom deals, your application keeps working without changes. ## Handling Edge Cases Gracefully Real-world entitlements go beyond straightforward plan checks. Trials, add-ons, and custom arrangements all add complexity, but the entitlements pattern accommodates them cleanly. Trials work by granting entitlements temporarily. A user on a free plan trial gets Pro entitlements for fourteen days. Your entitlement resolver combines the plan's standard entitlements with any temporary grants. Whether the user is on trial or a full subscription, your feature code sees the same entitlements. Add-ons grant additional entitlements without changing the base plan. A Starter customer who purchases the "Advanced Analytics" add-on gets those entitlements layered on top of their standard Starter entitlements. The feature checks remain unchanged. Custom arrangements are simply plans with tailored entitlement mappings. An enterprise customer needs SSO but wants to stay on the Pro price? Create a custom plan that grants Pro entitlements plus SSO. Your sales team can close the deal without waiting for engineering. ## Feature Flags vs. Entitlements If you're already using feature flags for gradual rollouts and A/B testing, entitlements might feel redundant. Both control what users can access. But they serve different purposes and work best together. Feature flags control the rollout of functionality across your user base. They answer questions like "has this feature been released?" and "is this user in the experiment cohort?" Feature flags are typically boolean and managed by engineering. Entitlements control access to functionality based on commercial relationships. They answer "has this user paid for this feature?" and "does this plan include this capability?" Entitlements are managed by product and business teams. The two systems intersect at a clear boundary. A feature must pass both checks to be accessible: the feature flag must be enabled (the feature has shipped), and the entitlement must be present (the user has paid). During development, you might enable a feature flag for internal testing while no plans grant the entitlement yet. At release, you enable the feature flag broadly and add the entitlement to appropriate plans. ```javascript async function canAccessFeature(user, featureKey, entitlementKey) { const flagEnabled = featureFlags.isEnabled(featureKey, user); const hasAccess = user.entitlements.includes(entitlementKey); return flagEnabled && hasAccess; } ``` This separation keeps responsibilities clear. Engineers manage feature flags for technical rollout; product and business teams manage entitlements for commercial access. ## Migrating from Tier Checks If your codebase already has tier checks scattered throughout, migrating to entitlements requires methodical effort. The good news is that you can migrate incrementally, wrapping existing checks without requiring a full rewrite. Start by cataloguing existing tier checks. Search your codebase for plan comparisons, tier references, and premium feature gates. This inventory shows you the scope of the migration and reveals patterns you can address systematically. Create entitlements that match your current tier structure. If Pro includes features A, B, and C, create entitlements for each and grant them all to Pro. This maintains current behaviour while introducing the entitlement abstraction. Replace tier checks one at a time. Each change should be behaviour-preserving; users should see exactly the same access before and after. This lets you verify the migration incrementally rather than relying on a single large change. Once you've replaced all tier checks with entitlement checks, you have the freedom to restructure. Tier names no longer appear in your codebase; only entitlement names remain. You can rename plans, split tiers, create custom bundles, and run promotions without touching the code. ## Building for the Future The entitlements pattern isn't just about surviving pricing changes. It's about building a foundation that supports the complexity your billing will eventually require. Early-stage products have simple pricing: one or two tiers, clear feature boundaries, and few exceptions. Tier checks work fine at this stage. But success creates complexity. More plans, more features, more exceptions, more customisation requests from sales. Products that built tier checks into their foundation pay that debt forever. Products that built entitlements adapt without rewrites. The investment in entitlements pays off over time. Each pricing change that doesn't require engineering involvement is time saved. Each custom deal that's just a configuration rather than code is velocity gained. Each feature addition only affects its own section. Your first pricing structure won't be your last. Build the abstraction that makes change cheap. --- _Salable's [entitlements system](https://salable.app/docs/understanding-entitlements) provides the mapping and resolution layer out of the box, so you can adopt the entitlements pattern without building the infrastructure yourself. Define entitlements in the dashboard and check them in your code with a single API call._ --- ### When to Optimise with Hybrid Pricing Source: https://salable.app/blog/insights/when-to-optimise-with-hybrid-pricing # When to Optimise with Hybrid Pricing SaaS products often deliver value in more than one way—through access, through usage, through the scale of what's being managed. Single-model pricing captures one of these dimensions while ignoring the others. Hybrid pricing combines models to match how products actually deliver value: a base fee for platform access, per-seat charges for team growth, usage rates for consumption. Each component captures a distinct dimension. ## The Limits of Single-Model Pricing An observability platform delivers value through engineer access and through the volume of data being monitored—but if it only charges per seat, the team ingesting 500GB pays the same as the team ingesting 50GB. A marketing platform delivers value through the toolset, the team using it, and the size of the contact database—but a flat monthly fee means the enterprise with a million contacts pays the same as the startup with a thousand. Only pricing one metric means you're giving away the others for free. Hybrid pricing addresses this. The observability platform could add a rate per gigabyte ingested. The marketing platform could add a per-seat charge and a fee per thousand contacts stored. If your product delivers value in more than one way, a single model can only charge for one of them. The industry has recognised this. [OpenView's 2022 State of Usage-Based Pricing report](https://openviewpartners.com/blog/state-of-usage-based-pricing/) found that 46% of SaaS companies now combine models in some form. Only 15% use pure pay-as-you-go. The rest moved toward combinations because single models couldn't capture what they were actually selling. ## When Hybrid Pricing Makes Sense Sometimes hybrid pricing isn't a strategic choice—it's a structural requirement driven by how your product creates and delivers value. Products with significant variable costs often require a usage component regardless of preference. If each API call incurs infrastructure costs, pure flat-rate pricing forces you to either limit usage (creating friction) or accept that heavy users consume your margin. A base fee plus usage charges aligns your pricing with your cost structure while still providing a predictable foundation. Products that deliver value through multiple distinct dimensions often need pricing components for each. A data warehouse delivers value through storage capacity, compute power for queries, and data transfer. A marketing platform delivers value through the toolset, team access, and the size of your contact database. Pricing only one dimension leaves the others uncaptured. Setup and configuration effort can justify one-time fees layered onto recurring charges. If onboarding a customer requires substantial work—custom integrations, data migration, training—that effort represents real value delivered. Absorbing it into recurring fees underprices the initial engagement; separating it as a one-time charge reflects the actual value exchange. Role-based access patterns suggest differentiated pricing for different user types. Not everyone in an organisation needs full capabilities. Separating pricing for editors versus viewers, administrators versus end users, or creators versus consumers lets you expand seat counts without proportionally increasing prices—capturing more users at appropriate price points for their actual usage. ## The Complexity Trade-Off Hybrid pricing's power comes with a cost: it's harder for customers to understand what they'll pay. This isn't a minor concern. [McKinsey's research on software pricing](https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/the-art-of-software-pricing-unleashing-growth-with-data-driven-insights) found that simpler pricing correlates with higher growth—customers who understand what they're buying decide faster. Complexity doesn't just confuse customers; it creates operational challenges. Sales teams struggle to quote accurately. Finance teams struggle to forecast. Support teams field questions about bills that customers don't understand. Each additional pricing component multiplies these difficulties. The test for hybrid pricing isn't whether it captures value more accurately—it probably does. The test is whether customers can still predict their costs. Predictability matters enormously to buyers, especially in enterprise contexts where budget certainty is non-negotiable. A base fee plus metered overage can work because customers understand the floor and can estimate their usage. A base fee plus per-seat plus three different usage meters plus setup fees plus add-ons becomes genuinely difficult to reason about. The first structure adds one component to the familiar subscription model; the second creates a pricing conundrum. ## Common Hybrid Patterns Hybrid structures tend to follow a few recognisable patterns. **Platform access plus usage** works well for infrastructure products and API services. A base fee covers the platform and provides revenue predictability; usage charges scale with consumption. Heavy users pay more, but everyone starts from an accessible entry point. **Platform access plus per-seat** suits collaboration tools where both the platform and team size represent distinct value. The base fee covers the platform regardless of team size; per-seat charges scale as organisations grow. **Tiered commitments plus overage** provides cost predictability up to a threshold while capturing value from exceptional usage. The base tier includes a certain amount of usage; going beyond triggers additional fees. This works when customers cluster around predictable levels but some need more. **Role-based differentiation** lets you expand seat counts without proportionally increasing prices. Full users pay full price; limited users—viewers, readers, occasional contributors—pay less or nothing. This captures value from power users while allowing broad deployment. ## Making Hybrid Pricing Work Hybrid pricing succeeds when each component maps to a distinct value dimension and customers can still forecast their costs. A few principles help. **Keep it to two or three components.** Two components remain comprehensible—a base fee plus usage, or a base fee plus per-seat. Three pushes the boundary. Four or more creates pricing that slows purchase decisions and generates billing disputes. **Map each component to value customers recognise.** If you charge per seat, customers should understand why seats matter. If you charge for usage, the metric should correlate with value received. Pricing components that feel arbitrary erode trust. **Build in predictability.** Usage floors guarantee minimum spend for you; usage ceilings guarantee maximum spend for customers. Committed-use discounts reward predictability. Alert thresholds notify customers before they hit unexpected charges. These mechanisms contain variability within bounds both parties can accept. **Test comprehension.** Before launching, walk prospective customers through your model and ask them to estimate their costs. If they can't do it with reasonable accuracy, the model is too complex. Simplify until customers can predict their spending without a spreadsheet. ## The Infrastructure Question Hybrid pricing is conceptually straightforward but operationally demanding. Each pricing component requires tracking, metering, calculation, and display. A base fee plus per-seat plus metered usage means maintaining seat counts, recording usage events, calculating prorated charges, and presenting coherent invoices—all while handling the edge cases that inevitably arise. This complexity explains why many companies default to simpler models even when hybrid would capture value more accurately. Building hybrid billing from scratch is substantial engineering work, and the maintenance burden doesn't disappear after launch. Salable's Line Items solve this. Instead of building custom logic for each pricing component, you compose plans from four building blocks: flat-rate for predictable charges, per-seat for team-based pricing, metered for usage-based billing, and one-off for setup fees or implementation packages. A plan can combine any of these—a \$99/month base fee, \$15 per seat, and \$0.02 per API call, all in one subscription. The billing system handles the composition, proration, and invoicing automatically. This matters because pricing should evolve. The hybrid structure that works at launch may need adjustment as you learn how customers actually use your product. When pricing is configuration rather than code, you can experiment and adapt without engineering sprints. ## Conclusion Hybrid pricing captures more value by matching your pricing structure to how your product actually delivers value. But only if you can implement it without drowning in engineering complexity. Salable was built with first-class support for hybrid pricing. Compose plans from flat-rate, per-seat, metered, and one-off components through configuration, not code. The pricing structure that matches your product becomes a product decision, not an engineering project. --- ### When Seats Still Make Sense (and When They Don't) Source: https://salable.app/blog/insights/when-seats-still-make-sense # When Seats Still Make Sense (and When They Don't) There's a lot of talk recently about seat-based pricing being dead. As AI tools automate tasks once done by people, the idea is that charging per seat becomes as absurd as charging per desk in a remote-first company. Small teams with AI agents will generate enterprise-level output, and pricing models tied to headcount will collapse under the contradiction. There's truth in this, but only partialy. The rush to abandon seats misses something important: seats were never arbitrary. For certain products, they captured real value. The question isn't whether to abandon seat-based pricing; it's understanding when seats reflect genuine value and when consumption tells a better story. ## When Seats Create Value Selling access to a team is fundamentally different from selling value to an individual user or AI Agent. For example, Slack delivers value very differently from chatGPT. Slack relies on mutliple cross collaboration between a team, the more users in that tool, the more value each person finds being able to collaborate. The value of a collaboration tool isn't just the features it offers each user. It's the interaction between all the members on the platform. This distinction matters for pricing. Products with genuine network effects derive value from the connections between users, not just the capabilities available to each. A messaging platform is worthless with one user, marginal with five, and transformative with an entire organisation. The more people on the platform, the more valuable it becomes for everyone. Seat-based pricing captures this dynamic directly: as the group grows, so does the value, and so does the price. Even without network effects, seat counts often correlate with value in ways that make pricing straightforward. The size of an organisation's team typically reflects its overall budget and the scale of problems it's solving. A project management tool used by three people serves a different scale of operation than one used by three hundred. The hundred-person company isn't just using more of the product; it's deriving more value from the coordination and visibility the tool provides. Seat-based pricing performs several useful functions. It ties revenue to a value signal that customers can feel: they understand why adding users costs more because they can see the additional value those users receive. It enables viral, word-of-mouth growth as individual users bring the tool into their teams. And it gives organisations clear control over usage and cost through a single lever: the number of people with access. ## When Consumption Creates Value Tying value to revenue works differently when value comes from actions rather than access. Consumption-based pricing shines when high-cost features deliver value in proportion to how much they're used, and when that usage varies unpredictably. The most visible example is AI. Every API call to a language model consumes compute resources and generates tokens. The cost structure is variable, and so is the value delivered. A customer making a thousand API calls derives different value than one making a million. Charging per token aligns the price with both the cost incurred and the value delivered. Consumption billing tends to work when three conditions hold: the features being consumed are genuinely high-value, that consumption varies significantly between customers or over time, and users can control their consumption. If everyone consumes roughly the same amount, a subscription makes more sense. If users have no control over their usage, variable pricing feels punitive. In B2B contexts, consumption pricing offers something enterprises crave: predictability through control. If a company can predict that using a service will drive a certain percentage of growth, they can scale usage and revenue together. The cost becomes a known input to a growth equation rather than a fixed overhead. This predictability extends to cost optimisation. When businesses have choices between services at different price points, like "thinking" models versus their faster, cheaper counterparts, they can match the tool to the task and manage their margins accordingly. ## Why AI Changes the Equation The introduction of AI features into SaaS products has made per-user costs variable in ways they never were before. A user who triggers a hundred AI-assisted actions costs dramatically more to serve than one who triggers ten. The seat that was once a reasonable proxy for value now conceals wildly different consumption patterns. This creates pressure from multiple directions. Some features are becoming significantly more valuable with AI augmentation. A single button that summarises a document or drafts a response can save hours of work. Others are becoming commoditised as AI makes basic functionality trivial. The value landscape is shifting beneath existing pricing structures. Customers notice this shift and want more control. In a seat-only model, the only lever available is adding or removing users. Changing the price per seat requires demonstrating increased value to every single user, a high bar when different users derive different value from AI-enhanced features. Customers want more levers to pull: the ability to dial up AI usage when it's valuable and dial it down when it's not, without the blunt instrument of removing access entirely. The result is that seat-based pricing alone is no longer sufficient for products with significant AI components. It's not that seats have become meaningless; they haven't. It's that they can no longer carry the full weight of pricing when usage patterns vary so dramatically. ### The Interface is changing The way users interact with SaaS products is changing just as quickly as the cost structures behind them. For years, SaaS has been built around the assumption of a human user logging into the tool. Pricing followed naturally from that model. A “user” was someone with a login, a seat, a defined place in the interface. That assumption is breaking down. Increasingly, products are being consumed programmatically. Developers are working directly from the terminal. Teams are interacting with tools through chat interfaces. Agents are orchestrating workflows across multiple services without anyone opening a browser. The interface is no longer the product. The outcome is. In this world, the concept of a “seat” starts to feel artificial. You might have a single engineer wiring together APIs to generate reports, trigger workflows, or enrich data. That work could deliver value across an entire organisation, but it maps to exactly one user in a seat-based model. There is no natural moment where you would add more seats, because the value isn’t tied to more people logging in. It’s tied to more being done. AI accelerates this shift. When agents can plan, execute, and iterate on tasks independently, they become the primary consumers of SaaS products. Humans move up a level, supervising and directing rather than directly interacting. The “user” becomes harder to define. Is it the person who set up the agent, or the agent itself? Projects like [x402](https://www.x402.org/) are early signals of where this is heading. We can explore a world where services are consumed directly by machines, with authentication, payment, and access handled programmatically. In that model, there is no concept of logging in, no dashboard, and no seat. Just capability, accessed on demand. This creates a fundamental mismatch with seat-based pricing. If your most valuable customers are interacting via APIs, agents, or automated workflows, then pricing per human user becomes increasingly disconnected from how value is actually created and consumed. Seats don’t disappear overnight, but they become less central. As interfaces fade and agents take over more of the interaction layer, pricing needs to follow usage, outcomes, or capabilities rather than the number of people who happen to have an account. ## The Path Forward The answer isn't abandoning seats for pure consumption pricing. That would mean giving up everything seats do well: the revenue predictability, the alignment with team growth, the familiar model that customers understand. The answer is combining models to match how your product actually delivers value. Flexibility in your pricing model is now crucial. A collaboration tool might charge per seat for platform access (because the value of collaboration genuinely scales with team size) while adding consumption pricing for AI features that vary dramatically in usage. A data platform might combine a base subscription for access with metered pricing for compute and storage. The goal is matching each pricing component to the type of value it captures. This isn't just about finding the right balance between seats and consumption. It's about recognising that different parts of your product may deliver value in fundamentally different ways, and pricing each accordingly. The platform itself might justify a flat fee. Team growth might justify per-seat charges. AI-powered features might justify usage-based metering. Combining these creates a pricing structure that reflects the actual value exchange rather than forcing all value into a single metric. The competitive advantage goes to companies that find the right match, whether monetising the number of people, the activity they produce, or both. Rigid single-model pricing leaves value on the table. Flexible pricing that can adapt as products evolve and as customers' needs change captures more of the value being created. ## Building Flexible Pricing Without Engineering Complexity The challenge with hybrid pricing is usually the implementation. Tracking seats is straightforward. Metering consumption is more complex. Combining them in a single subscription, with proper proration and coherent invoicing, is substantial engineering work. Many companies default to simpler models because it's easier even when hybrid pricing would be more sensible or give the customer greater value. Salable's Line Items solve this problem. Instead of building custom logic for each pricing component, you compose plans from building blocks: flat-rate for predictable charges, per-seat for team-based pricing, metered for consumption-based billing. A single subscription can combine a base platform fee, per-seat charges for team growth, and per-token pricing for AI usage, all managed through configuration rather than code. When pricing is configuration rather than engineering, you can adapt as you learn. The hybrid structure that works at launch may need adjustment as AI capabilities evolve and customer usage patterns become clearer. Building that flexibility into your infrastructure means pricing can evolve with your product rather than constraining it. Seats aren't obsolete. Consumption isn't the only answer. The products that thrive in the AI era will be those that match their pricing to how they actually create value and have the infrastructure to adapt as that value evolves. --- ### Market-Leading SaaS Pricing Strategies You Should Steal Source: https://salable.app/blog/insights/subscription-pricing-playbook # Market-Leading SaaS Pricing Strategies You Should Steal Every SaaS founder thinks their product is unique—and in many ways they're right. But when it comes to pricing, the differences matter less than the similarities. Studying how successful subscription businesses price reveals consistent patterns: how they choose value metrics, structure tiers, set anchor prices, and evolve pricing as they grow. These patterns aren't accidents. They emerge from the fundamental psychology of how customers perceive value and make purchasing decisions. Understanding them lets you skip the expensive experimentation phase and start with pricing that's at least directionally correct. None of this promises perfection. Your initial pricing will be wrong in ways you can't predict. But there's a difference between being wrong in a data-informed way and being wrong because you guessed. The patterns in this playbook put you in the first category, where iteration leads to improvement rather than thrashing. ## The Universal Challenge of Value Metrics Every subscription business faces the same foundational question: what should customers pay for? This is the value metric—the unit that increases as customers derive more value from your product. Seats, usage, storage, projects, API calls, revenue processed—these are all value metrics that different companies have chosen to align price with value. The choice matters enormously. A good value metric scales naturally with the value customers receive, so paying more as you use more feels fair. A poor value metric creates misalignment: customers who derive tremendous value pay the same as those who barely use the product, or they get charged based on something unrelated to their actual benefit. Consider how Slack chose active users rather than total users. A company might invite 500 employees but only see 50 actively messaging—under active-user pricing, they pay for 50. This feels fair because the company only pays for value they're actually receiving. It also reduces friction during adoption because organisations don't face a massive upfront commitment to roll out company-wide. Contrast this with per-seat pricing that charges for every invited user, regardless of activity. The organisation with 500 invites and 50 active users pays ten times more for the same actual usage. That pricing model creates adoption friction and encourages administrators to be stingy with invitations, limiting the very network effects that make collaboration tools valuable. The best value metrics share three characteristics. First, customers intuitively understand why they should pay more as the metric increases—more users means more value from collaboration, more API calls means more transactions processed, more storage means more assets managed. Second, the metric correlates with actual value received, not just with product usage. Third, customers can measure and predict the metric well enough to forecast their costs. Testing your value metric against these criteria reveals whether you've chosen wisely. If customers push back not on the price level but on the unit of measurement itself, you probably have a value metric problem. ## The Three-Tier Standard and Why It Works Look at enough successful SaaS companies and a striking pattern emerges: the overwhelming majority settle on three tiers, even when they started with more or fewer. This convergence isn't coincidental. Three tiers represent a cognitive sweet spot that balances choice with simplicity. Two tiers create a binary choice that lacks nuance. Customers who don't fit neatly into either category feel forced into an awkward compromise. Two tiers also leave money on the table—you're probably undercharging your most valuable customers and overcharging your most price-sensitive ones because you haven't created options that speak to each segment. Four or more tiers introduce complexity that confuses rather than helps. Each additional tier requires customers to make finer distinctions about their needs, distinctions they may not be equipped to make. Analysis paralysis kicks in, and customers defer decisions rather than commit. Worse, closely-spaced tiers cannibalize each other—customers who would have paid for the premium tier notice that the tier just below has almost everything they need. Three tiers work because they map naturally to three intuitive customer segments: individual users or small teams, growing teams with more sophisticated needs, and enterprises with scale and compliance requirements. Most SaaS products genuinely serve these distinct segments, making three tiers a natural fit. The three tiers also enable psychological positioning. The higher tier serves as the anchor—the price point that makes lower options seem reasonable by comparison. The middle tier becomes the target—the option most customers should consider, positioned to look like the best value against the high anchor. The lower tier provides an entry point for price-sensitive customers and serves as a stepping stone to upgrades. ## Anchor Pricing and the Psychology of Comparison Customers don't evaluate prices in isolation—they evaluate them relative to alternatives. This comparison instinct is the foundation of anchor pricing, one of the most powerful psychological levers in pricing design. The principle is straightforward: presenting a high-priced option creates a reference point that makes lower prices feel more reasonable by comparison. A $299/month enterprise tier on the page makes a $99/month professional tier feel accessible, even if $99/month would feel expensive in isolation. Without that high anchor, customers evaluate $99/month against their own mental reference points—which may be based on cheaper competitors or different product categories entirely. Successful SaaS companies use anchoring deliberately. Including a visibly higher-priced tier—even one that few customers choose—shapes how customers perceive the other options. The expensive tier establishes a reference point that makes mid-tier pricing feel reasonable. The expensive tier might only convert a small percentage of visitors, but it shapes perception of the tiers that follow. Anchoring extends beyond the pricing page. Enterprise pricing quotes that start high and negotiate down feel like better deals than quotes that start low and hold firm, even when the final price is identical. Free trials that clearly display what the paid price will be anchor customers to expect payment, making conversion feel natural rather than like a bait-and-switch. The anchoring effect also explains why "contact us" pricing on enterprise tiers can backfire. Without a visible anchor, customers create their own mental anchor—often based on smaller-company pricing they've seen elsewhere. When the sales quote comes in higher than their mental anchor, sticker shock ensues, even if the price is entirely reasonable for the value delivered. ## The Entry Point Decision: Freemium Versus Free Trial How customers first experience your product is one of the most consequential pricing decisions you'll make. The freemium versus free trial debate has generated enormous controversy, but patterns from successful companies suggest the answer depends on specific characteristics of your product and market. Freemium works when your product has viral or network characteristics that benefit from maximum adoption. Slack's free tier isn't charity—it's a growth engine. Teams adopt Slack for free, establish it as their communication hub, and then convert to paid when they need message history or integrations. The free users aren't costs; they're the network effects that make Slack valuable for paying customers. Freemium also works when your product requires behaviour change that takes time to develop. A productivity tool might take weeks or months of use before its value becomes apparent. A 14-day free trial isn't enough time for users to develop the habits that make the product indispensable. Indefinite free access lets the product's value compound until conversion becomes natural. Free trials work when your product delivers immediate value that's evident within the trial period. A design tool that lets you create professional graphics in minutes can demonstrate its worth in a 14-day trial. A project management platform that improves team coordination can show results within weeks. If your product's value is quickly apparent, a time-limited trial creates urgency that freemium lacks. Free trials also work better when your product has high marginal costs or limited viral potential. Storing user data, processing transactions, or providing compute resources all cost money—at scale, freemium users can become a significant expense. If there's no network effect to justify those costs, time-limited trials make more sense. The most successful companies often combine approaches: a free tier with meaningful but limited functionality, plus a free trial of the full premium experience. This captures the broad adoption benefits of freemium while letting serious customers experience the full product before committing. ## Pricing Evolution: The Stages of Growth Pricing isn't something you set once and forget. Successful companies evolve their pricing as they grow, and this evolution follows recognizable stages. In the earliest stage, pricing is exploratory. You're learning who your customers are, how they use your product, and what value they derive. Your pricing should be simple—probably a single tier or very basic tiered structure—because you don't yet have the customer understanding to design sophisticated pricing. The goal is learning, not optimization. As you develop customer understanding, pricing becomes segmented. You recognise that different customers derive different value, and you create tiers that capture those differences. This is when most companies move to the three-tier structure, with clear distinctions between individual, team, and enterprise users. With scale comes optimization. You have enough data to experiment systematically—testing price points, tier boundaries, feature allocation, and packaging. Successful companies in this stage treat pricing as a continuous improvement project, not a fixed decision. They run experiments, analyse results, and iterate. At maturity, pricing becomes strategic. Market position, competitive dynamics, and customer expectations all factor into pricing decisions. You might hold prices stable even when costs decrease, investing the margin in product improvement. Or you might cut prices to capture market share before a competitor gains traction. Pricing becomes a tool for market strategy, not just value capture. The mistake many founders make is trying to skip stages. They design sophisticated pricing before understanding their customers, or they set prices and forget them while competitors iterate. The companies that win at pricing match their pricing sophistication to their stage of growth. ## The Iteration Imperative Perhaps the most consistent pattern among successful subscription companies is continuous pricing iteration. They don't agonize over getting the initial price perfect; they establish a baseline and improve from there. This matters because pricing is inherently uncertain. You can't know in advance exactly how customers will respond to different price points, tier structures, or packaging decisions. The only way to learn is to experiment, and experimentation requires infrastructure that supports change. The companies that iterate effectively on pricing share several characteristics. They track granular data on conversion rates by tier, upgrade and downgrade patterns, price sensitivity across segments, and lifetime value by acquisition channel. This data reveals where pricing works and where it creates friction. They also create the technical ability to change pricing quickly. When pricing changes require engineering effort—new tiers, different feature gates, updated billing logic—iteration slows to a crawl. The companies that iterate fastest have decoupled pricing configuration from code deployment, letting product and business teams adjust pricing without waiting for development cycles. Finally, they embrace the idea that pricing is never finished. Even successful pricing can become less effective as markets change, competitors adjust, and customer expectations evolve. The company that sets pricing and moves on will eventually be outcompeted by companies that continuously improve. ## Common Patterns Worth Stealing Beyond these structural principles, specific tactical patterns recur across successful subscription businesses. None of these are universal rules—context always matters—but they represent proven approaches worth considering. Annual pricing discounts create predictable revenue and reduce churn. The standard pattern is roughly 15-20% discount for annual commitment—enough to motivate behaviour change without sacrificing too much revenue. Presenting annual pricing as the default, with monthly as the alternative, nudges customers toward longer commitments. Graduated onboarding prices ease customers into higher payment. Some companies offer reduced rates for the first few months, letting customers experience value before paying full price. This reduces the barrier to initial conversion while still capturing full revenue from established customers. Usage-based floors and ceilings address the uncertainty customers feel about variable pricing. A minimum charge ensures revenue predictability for you; a maximum charge ensures cost predictability for customers. The specific numbers depend on your unit economics and customer expectations. Deliberate feature allocation creates clear upgrade triggers. When customers hit limits on their current tier—running out of seats, reaching project caps, needing blocked integrations—the path to upgrade becomes obvious. The best upgrade triggers are capabilities customers discover they need through use, not benefits you have to explain upfront. Transparent pricing signals confidence. Companies that require "contact sales" for all pricing often do so because they lack confidence in their pricing, not because their product is too complex to price publicly. Transparent pricing builds trust and lets customers self-qualify, reducing sales friction. ## The Learning Mindset The most important pattern isn't structural—it's about mindset. Successful subscription companies approach pricing with a learning mindset rather than a perfection mindset. They accept that initial pricing will be wrong, but wrong in ways that yield data for improvement. This mindset shift is liberating. Instead of agonizing over whether $49 or $59 is the right price point, you pick one, measure results, and adjust. Instead of debating endlessly about tier structure, you launch something reasonable and learn from how customers actually behave. The learning mindset also prevents paralysis when competitors change pricing. Instead of reactive panic, you evaluate whether the competitive change affects your value proposition and customer segments. Sometimes it requires response; often it doesn't. The company that has been continuously learning about their own pricing can evaluate competitive changes with data rather than fear. Your pricing will change—probably multiple times. The goal isn't getting it right the first time; it's building the infrastructure and mindset for continuous improvement. The company that iterates quickly on pricing beats the company that agonizes over the initial decision, every time. ## Conclusion: Patterns as Starting Points The patterns in this playbook—three-tier structures, value-based metrics, anchoring effects, entry point strategies, evolutionary staging—aren't rules to follow blindly. They're starting points that let you benefit from collective learning without repeating everyone else's experiments. Your product and market will have characteristics that make some patterns more relevant than others. A developer tool might need usage-based pricing that a marketing platform doesn't. An enterprise-focused product might skip the free tier entirely. A marketplace might need entirely different structures. But the meta-pattern holds universally: successful subscription pricing comes from iteration, not revelation. Start with directionally correct pricing based on proven patterns. Measure how customers respond. Adjust based on what you learn. Repeat indefinitely. The company that iterates on pricing fastest wins. Build the infrastructure—the data tracking, the configuration flexibility, the experimental mindset—that makes rapid iteration possible. Your first pricing will be wrong. Your tenth will be much better. And your competitors who set prices and forget them will wonder why you're growing faster. --- ### Anonymous to Authenticated: Frictionless Checkout Flows Source: https://salable.app/blog/features/anonymous-checkout-frictionless-flows # How SaaS Companies Sabotage Their Own Sales Your potential customer found your pricing page, selected a plan, and clicked "Subscribe." Then you asked them to create an account. Password requirements, email verification, maybe phone number for good measure. Half of them left. Anonymous checkout removes this friction. Customers complete payment with just an email address, receiving immediate access via a session token. When they create an account later, the subscription transfers automatically. The payment is captured when intent is highest, account creation happens when it's convenient. The psychology is straightforward. Clicking "Subscribe" represents peak purchase intent. The customer has decided your product is worth paying for. Every additional step between that decision and completed payment gives them time to reconsider, get distracted, or encounter friction that tips the balance toward abandonment. Account creation is substantial friction. Customers must choose a password that meets requirements. They might wonder if you'll spam them. They consider whether they want yet another account to manage. Each consideration is a chance to lose them. Baymard Institute research shows that forced account creation is among the top reasons for cart abandonment in e-commerce, and there's no reason to think SaaS checkout is different. Anonymous checkout defers this friction until after you have the customer's money. The purchase is complete. They're paying customers. Now you can ask them to set a password, and they're far more likely to comply because they've already committed. ## How Anonymous Checkout Works In an anonymous checkout flow, the customer provides payment information without creating an account first. The minimum required information is an email address for receipts and a payment method. No password, no phone number, no company name unless your business requires it for tax or compliance. At checkout, you provide an anonymous identifier—a temporary ID your application generates for the not-yet-registered customer. Salable creates the subscription associated with this identifier. Your application handles authentication however you choose: a magic link, a temporary session, or immediate access based on the checkout completion redirect. Later, typically during their first session in your product, you prompt the customer to complete account setup. "Set a password to secure your account" or "Finish setting up your profile" feels like housekeeping rather than a barrier. They're already invested; they're using the product; completing registration is just tying up loose ends. When the customer creates their account, you update the subscription's identifier from the anonymous one to their permanent user ID. The subscription, entitlements, and billing relationship transfer to their new account. No orphaned subscriptions, no manual reconnection—just a single API call to swap the identifier. ## Designing the Minimal Checkout The goal of anonymous checkout is minimizing fields while capturing necessary information. What's truly required depends on your business, but for most SaaS subscriptions it's just email and payment. Email is essential for receipts, failed payment notifications, and communication. It also becomes the customer's identifier for account linking later. Ask for email first, prominently, with clear indication that you won't spam them. Payment information is obviously required to charge them. Stripe's checkout handles this with optimised forms for card details. The fewer clicks and fields, the better. That's it for many products. Name, company, phone number, and other fields can all be captured later during account setup or not at all. Every field you add increases abandonment. Add fields only when you genuinely need the information before completing the purchase. Some businesses have legitimate requirements for additional checkout fields. B2B SaaS might need company name for invoicing. Products with regulatory requirements might need location for tax compliance. Services with shipping components need addresses. But even these should be reduced to the minimum necessary for purchase completion. ## Managing Anonymous Identifiers The anonymous identifier bridges the gap between purchase and account creation. How you generate and manage these identifiers is up to your application. A common approach is generating a UUID at checkout time. This UUID becomes the owner ID for the subscription. Store it in a cookie or your session storage so you can identify the customer when they return. When they complete account registration, replace the UUID with their permanent user ID. Your authentication strategy remains entirely in your control. Some applications grant immediate access after checkout based on the redirect parameters. Others send a magic link to the customer's email. Still others require a lightweight sign-in step that's simpler than full registration. Choose whatever fits your product's security requirements and user experience goals. The key constraint is ensuring you can map between the anonymous identifier and the eventual account. Whether that's a database lookup, a signed token, or session storage depends on your architecture. The important thing is that when account creation happens, you can tell Salable which anonymous subscription belongs to the new user. ## Prompting for Account Completion The timing and framing of account completion prompts affects conversion rates. You've captured the payment; now you need to convert anonymous buyers into fully registered users. First-run prompts work well for many products. The customer arrives via checkout redirect or magic link, sees a brief "Finish setting up your account" modal, and sets a password. This happens before they use the product substantively, so it doesn't interrupt a task. The ask is small: "Enter a password to secure your account." Triggered prompts appear when the customer tries to use a feature that requires a full account. Maybe saving settings, adding team members, or accessing a dashboard. "Create your account to save this configuration" ties the ask to immediate value. Time-delayed prompts appear after a period of use. The customer has been using the product for a day or a week, and you prompt them to secure their account. The familiarity with the product reduces resistance; they've already integrated it into their workflow. Don't make the prompt dismissible indefinitely. If customers can permanently dismiss the account creation prompt, some will, and you'll have anonymous customers you can't fully support. Balance user autonomy with operational needs. Perhaps the prompt appears once per session until they complete setup, or access degrades slightly after a set period. ## Handling Edge Cases The checkout email is your safety net. As long as customers know the email address they purchased with, you can connect them to their subscription later. If a customer loses their session before creating an account, prompt them for their checkout email. Look up the subscription by email, verify ownership (via a confirmation link or code), and let them complete account setup. This works the same whether they lost a browser session, switched devices, or waited weeks before returning. If a customer accidentally purchases twice, cancel the duplicate and refund it. This is simpler than trying to merge subscriptions or credit future billing. ## Conversion Rate Impact The business case for anonymous checkout is well-documented. According to [Baymard Institute research](https://baymard.com/lists/cart-abandonment-rate), 19% of online shoppers abandon checkout when forced to create an account. [Shopify's data](https://www.shopify.com/enterprise/blog/guest-checkout) puts the figure at 24%. Either way, roughly one in five potential customers is lost to a friction point that anonymous checkout eliminates. The impact can be substantial. The famous ["$300 Million Button" case study](https://articles.uie.com/three_hund_million_button/) by Jared Spool documented a 45% increase in purchases after a major retailer replaced forced registration with guest checkout, translating to \$300 million in additional revenue over the first year. The improvement varies by product and audience. B2C products often see larger gains because consumer tolerance for friction is lower. B2B products see meaningful but smaller improvements because business buyers expect some account setup as part of purchasing. Test with your actual audience to measure your specific uplift. Customers who have already paid are motivated to complete account setup—the friction that seemed prohibitive before purchase feels trivial after. The key is capturing the sale when intent is highest, then handling account creation when the customer is already committed. ## Implementation with Salable Anonymous checkout uses the same checkout flow as regular purchases—you simply provide an anonymous identifier as the owner ID instead of a real user ID. Generate the identifier in your application, pass it to the checkout link, and Salable creates the subscription associated with it. The checkout collects email and payment. Upon completion, it redirects to your success URL. Your application handles the post-checkout experience: storing the anonymous identifier, granting access, and eventually prompting for account creation. When the customer creates their account, call Salable's API to update the owner ID from the anonymous identifier to their permanent user ID. The subscription transfers to the new identifier, and all entitlement checks going forward use the real user ID. ## Balancing Conversion and Experience Anonymous checkout optimises for purchase completion, but it creates temporary uncertainty in the customer experience. Customers might not know their password (because they don't have one yet). They might be confused about how to log in later. They might worry about losing access. Magic links sidestep this problem entirely. Since checkout already captured the customer's email and linked it to their subscription, you can authenticate them by sending a login link to that address. No password to create, remember, or reset. The customer clicks the link in their inbox and they're in. This approach works particularly well for anonymous checkout because the email-to-subscription relationship already exists. If you do want customers to set passwords, clear communication mitigates concerns. The checkout completion page should explain what happens next: "You're in! We sent a receipt to your email. You can use the product now, and we'll help you set up your password shortly." The in-app experience should reinforce that everything is fine while gently nudging toward completion. "You're currently logged in with a temporary session. Set a password to secure your access." The tone is helpful, not urgent. If your product genuinely requires account information upfront, consider whether anonymous checkout is right for you. Team collaboration products might need names from the start. Multi-user subscriptions might need to know who the admin is. Anonymous checkout works best for products where a single user can get immediate value without extensive setup. The fundamental trade-off is between optimising the purchase moment and optimising the ongoing relationship. Anonymous checkout maximises purchase completion; account completion maximises long-term engagement. By separating these concerns into two steps, you can optimise each without compromising the other. The payment captures when intent is highest. The profile builds when commitment is established. Your conversion rate improves, and your customers still end up with proper accounts ready for long-term use. --- ### Why Monthly Subscriptions Are a Bad Default Source: https://salable.app/blog/features/flexible-billing-intervals # Why Monthly Subscriptions Are a Bad Default Monthly billing became the SaaS default because it was easy, not because it was optimal. Some customers want annual contracts for budget predictability. Others need weekly billing aligned with their pay cycles. High-velocity products might bill daily. Forcing everyone into monthly subscriptions leaves money on the table: annual prepay improves cash flow, while weekly billing reduces churn in price-sensitive segments. Flexible billing intervals let you meet customers where they are instead of where your billing system allows. The monthly assumption is embedded so deeply in SaaS thinking that many founders never question it. Pricing pages show monthly rates. Metrics assume monthly cohorts. Churn calculations divide by monthly active users. When a customer asks "can I pay annually?" the answer is often "we'll figure something out," followed by manual invoicing outside your subscription system. But customer needs genuinely vary. Enterprise buyers often have annual budgeting processes that make monthly payments administratively burdensome. Freelancers and contractors might prefer weekly billing that matches their cash flow. Seasonal businesses want quarterly billing aligned with their busy periods. Products with daily value delivery, like job boards or classified listings, might charge per day rather than per month. Flexible billing intervals aren't just a customer convenience feature. They're a revenue optimization lever that affects cash flow, churn, and customer lifetime value in measurable ways. ## The Case for Annual Billing Annual billing deserves special attention because of its impact on unit economics. When a customer pays upfront for a year, several good things happen simultaneously. Cash flow improves immediately. Instead of waiting twelve months to collect twelve months of revenue, you receive it all at once. This cash can fund growth, reduce the need for financing, or simply provide a buffer against revenue fluctuations. Churn decreases because the commitment period is longer. A customer who pays monthly has twelve opportunities per year to cancel. A customer who pays annually has one. Even if they become dissatisfied at month three, the sunk cost of their annual payment often keeps them engaged long enough to work through issues. Studies consistently show that annual subscribers have lower churn rates than monthly subscribers, typically by 15-20%. Customer acquisition costs amortize faster. The marketing spend to acquire a customer is fixed regardless of billing frequency. An annual subscriber who pays \$1,200 upfront delivers more revenue relative to acquisition cost than a monthly subscriber who might churn after four \$100 payments. The trade-off is discount expectations. Customers paying annually typically expect a discount for their commitment, usually 10-20% compared to monthly rates. This discount is economically rational because the improved unit economics from cash flow and retention more than compensate for the lower total price. ## Implementing Billing Intervals in Salable Salable supports flexible billing intervals natively, configured at the line item level. Each line item specifies an interval (day, week, month, or year) and an interval count. This combination lets you create standard intervals like monthly or annual, as well as custom intervals like quarterly (every three months) or biannual (every six months). The interval configuration is straightforward. A monthly subscription sets the interval to "month" and interval count to 1. An annual subscription sets interval to "year" and interval count to 1. A quarterly subscription sets interval to "month" and interval count to 3. A daily subscription sets interval to "day" and interval count to 1. When you offer multiple billing intervals for the same product, you typically create separate plans or line items for each. A Professional plan might exist in monthly and annual variants, with the annual variant priced at a discount. Customers choose their preferred billing frequency at checkout, and the subscription bills on that schedule going forward. Renewal and proration calculations adjust automatically for the configured interval. An annual subscription that starts on March 15 renews on March 15 the following year. If the customer upgrades mid-term, proration divides the remaining period appropriately. A customer six months into an annual subscription who upgrades receives credit for half their original payment against the new plan price. ## Weekly and Daily Billing At the other end of the spectrum from annual contracts, some products benefit from billing cycles shorter than monthly. Weekly billing suits customers with weekly cash flow patterns. Freelancers and hourly workers often get paid weekly or biweekly. Products targeting these segments reduce friction by aligning billing with income. The subscription renews on Friday; payday is Friday; the charge goes through smoothly. Weekly billing also reduces the initial commitment barrier. A \$25 weekly subscription feels more accessible than a \$100 monthly subscription, even though the annual cost is higher. For price-sensitive segments, the lower weekly amount may convert better than the equivalent monthly rate. Daily billing applies to products with highly variable daily value. Job posting sites might charge per day a listing is active. Classified advertising services might bill daily. Cloud infrastructure famously bills by the hour or second. When usage and value are inherently daily, billing should match. The implementation consideration with short billing intervals is payment processing friction. Each charge incurs processing fees, and very small daily charges may have poor unit economics after fees. Additionally, frequent charges increase the surface area for payment failures. A customer with a weekly subscription experiences payment failure opportunities four times as often as monthly. For very short intervals, consider separating how you express pricing from how you collect payment. You might advertise a daily rate but charge weekly, or quote a weekly price but bill monthly. Customers see an accessible per-day or per-week figure while you avoid the overhead of frequent small charges. ## Custom Interval Patterns Standard intervals cover most cases, but some businesses need billing cycles that don't map to days, weeks, months, or years. Quarterly billing aligns with business planning cycles. Many companies budget and review spending quarterly, making a quarterly subscription a natural fit for their internal processes. Configure this with interval "month" and interval count 3. Biannual billing (every six months) appears in contracts where annual feels too long but quarterly feels too short. Configure with interval "month" and interval count 6. Multi-year contracts for enterprise customers might bill annually but commit for two or three years. The billing interval is annual, but the minimum commitment spans multiple billing periods. This requires contract-level configuration beyond just the billing interval. Academic calendars often don't align with calendar months. A semester-based product might bill for four-month periods that match fall and spring semesters. Configure with interval "month" and interval count 4, with renewal dates set to semester start dates. ## Communicating Billing Options to Customers When offering multiple billing intervals, clear communication prevents confusion and reduces support requests. Your pricing page should show options prominently. The conventional UI is a toggle between monthly and annual, showing how prices differ. Expand this pattern if you offer more intervals: a selector showing weekly, monthly, quarterly, and annual with corresponding prices and savings. Show the math explicitly. "Annual billing saves you \$238 per year" is more compelling than just showing two prices. Customers should understand both what they'll pay and what they'll save by choosing different intervals. Consider defaulting to annual on the pricing page. If annual billing benefits your unit economics, make it the default selection. Customers who want monthly can switch, but the nudge toward annual captures customers who would accept either. In checkout, confirm the billing interval before payment. "You'll be charged \$1,188 today and again on [date next year]" eliminates surprises. For monthly, "You'll be charged \$99 today and monthly on the [day]th" sets clear expectations. ## Changing Billing Intervals Customers sometimes want to switch billing intervals mid-subscription. A monthly subscriber might want to convert to annual to lock in a rate or capture a discount. An annual subscriber might want to switch to monthly due to budget constraints. These conversions require careful handling of timing and money. Converting from monthly to annual typically means charging for a full year minus any remaining monthly credit. Converting from annual to monthly might mean prorating the unused annual term into monthly credits. Salable handles interval changes through subscription modification. When a customer changes their interval, the system calculates appropriate credits and charges, generates the necessary invoice adjustments, and updates the renewal schedule. Your application receives webhooks for the change and any associated billing events. Set policies for interval changes and communicate them clearly. Can customers switch from annual to monthly, or only monthly to annual? Is switching mid-term allowed, or only at renewal? What happens to discounts if a customer downgrades from annual? These policies should be documented and consistently applied. ## Billing Intervals and Revenue Recognition Different billing intervals affect when you recognise revenue under accrual accounting. An annual subscription paid upfront represents twelve months of unearned revenue that you recognise monthly as you deliver service. This distinction matters for financial reporting and taxes. Cash basis accounting shows annual payments as revenue when received. Accrual basis accounting spreads that revenue over the subscription period. If you're operating under GAAP or IFRS standards, your auditors will want to see proper deferral of prepaid subscriptions. Salable's reporting supports both views. You can see cash collected by period and revenue recognised by period. The deferred revenue balance shows how much you've collected but not yet earned. This data feeds into your financial systems for accurate reporting. ## The Flexibility Advantage Every billing interval decision trades off between customer preferences, unit economics, and operational complexity. The optimal mix depends on your specific business, but having options lets you experiment and optimise. Start with the intervals that obviously serve your market. If you're selling to enterprises, annual billing is likely essential. If you're targeting freelancers, consider weekly. If you're purely consumption-based, daily might make sense. Add intervals based on customer demand. If multiple customers ask for quarterly billing, the request is probably worth serving. If no one asks for weekly, don't build it just for theoretical flexibility. Monitor the impact of different intervals on your key metrics. Do annual subscribers retain better? Do weekly subscribers convert more easily? Let data guide which intervals you promote versus which you merely make available. The monthly default persists because it's familiar, not because it's universal. Your customers have diverse needs, cash flows, and planning cycles. Flexible billing intervals let you serve them all without fragmenting your product or complicating your operations. The subscription bills when it makes sense for the customer, and your business captures the value either way. --- ### Why Your Pricing Page Is Losing You Customers Source: https://salable.app/blog/insights/pricing-page-losing-customers # Why Your Pricing Page Is Losing You Customers Pricing pages are high-stakes real estate. Visitors who reach your pricing page have significantly higher conversion intent than average—they're actively evaluating whether to buy. Yet many pricing pages squander this opportunity. The typical SaaS pricing page commits the same sins: feature comparison tables that read like spec sheets, tier names that mean nothing, and a sea of checkmarks that blur together. Customers land on pricing pages with a question: "Is this right for me?" Feature matrices don't answer that question. They answer "What do I get?"—which is a different, less important inquiry. Fixing pricing pages requires shifting from features to outcomes, from comparison to recommendation. This shift isn't cosmetic. It reflects a fundamental change in how you think about the job your pricing page needs to do. Most pricing pages are designed as information repositories; they should be designed as decision-making tools. ## The Feature Matrix Trap Visit any SaaS pricing page and you'll likely find a comparison table dominating the layout. Three or four columns representing tiers, with rows of features stretching down the page. Checkmarks and crosses indicate what's included at each level. The logic seems sound: give customers all the information they need to make an informed decision. Comparison tables aren't the problem. The problem is how most tables are designed: optimising for comprehensiveness, not comprehension. A table with forty features tells customers everything and nothing simultaneously. They can see that the Pro tier includes "Advanced Analytics" while Basic doesn't, but they don't know whether Advanced Analytics matters for their use case. They see that Enterprise includes "Custom Integrations" but have no framework for evaluating whether they'll ever need custom integrations. The format assumes customers arrive with perfect knowledge of what they need. In reality, most visitors are still figuring out whether your product solves their problem at all. They're not ready to parse the differences between "10 projects" and "Unlimited projects"—they're wondering if this is the right tool for someone in their situation. This mismatch explains why bounce rates on pricing pages are often surprisingly high. Customers come seeking clarity and find complexity. They wanted guidance and got a spec sheet. ## What Customers Actually Want to Know When someone visits your pricing page, they're in one of three mental states. Some are just browsing, getting a sense of what you charge to decide if you're even worth evaluating. Others are seriously considering your product and need to determine which tier fits. A few are ready to buy and just need to confirm the details before committing. For all three groups, the central question is the same: "Is this right for someone like me?" Notice that this isn't a question about features. It's a question about fit. Features matter eventually, but only in service of fit. A customer doesn't care about "Advanced Analytics" in the abstract—they care whether it will help them accomplish what they're trying to accomplish. The product manager evaluating your tool wants to know if it will make their team's weekly planning easier. The startup founder wants to know if it can scale with their growth. The enterprise buyer wants to know if it meets their security requirements. Effective pricing pages answer the fit question first and the feature question second. They help customers see themselves in one of the tiers before diving into what that tier includes. "For growing teams shipping weekly" tells customers more at a glance than "50 projects, 10 integrations, advanced reporting." ## Tier Names That Mean Nothing Among the most common pricing page failures is tier naming that provides zero information. Basic, Pro, Enterprise. These names persist because they're familiar, not because they're useful. Consider what these names actually communicate. "Basic" says "this is our stripped-down version"—hardly an aspirational choice. "Pro" suggests this is for professionals, but which professionals? The freelance designer and the Fortune 500 IT department are both professionals. "Enterprise" has become shorthand for "expensive and complicated," which may not be the message you want to send. Compare this to tier names that describe who the tier is for. "For Individuals" immediately tells solo users where to look. "For Teams" signals collaboration features. "For Organizations" suggests scale and administration capabilities. These aren't clever, but they're clear. Customers can self-select without decoding marketing jargon. The test for tier naming is simple: can a customer new to your product look at the names alone and make a reasonable guess about which tier fits them? If your names require reading the fine print to differentiate, they're not doing their job. ## The Checkmark Problem Checkmarks have become the default visual language for feature comparison, but they often create more confusion than clarity. A page full of checkmarks becomes visual noise—your eye can't distinguish what's important from what's filler. Everything looks equally weighted, even when features vary enormously in significance. Worse, checkmarks encourage a style of comparison that serves nobody. Customers start counting checkmarks as if more is always better, even when many of those features are irrelevant to them. They might choose a higher tier because it has more checkmarks, paying for capabilities they'll never use. Or they might choose a lower tier because it seems "enough" based on checkmark count, missing a critical feature buried in the comparison. The alternative isn't eliminating feature comparison entirely—sometimes customers genuinely need to compare specific capabilities. But lead with outcomes and value first, then make detailed feature comparison available for those who want to dig deeper. A "Compare all features" link below your primary pricing presentation lets detail-oriented customers find what they need without overwhelming everyone else. ## Anchoring and the Architecture of Choice Pricing page design isn't just about information—it's about psychology. How you present options influences which options customers choose. This is the realm of choice architecture, and most pricing pages get it wrong. The most common mistake is presenting tiers as equals and letting customers figure out the right choice. Three cards of identical size, equal visual weight, no recommendation. This feels neutral but actually creates friction. Customers have to do all the work of deciding, with no guidance from you. Contrast this with pricing pages that recommend a tier. Highlighting one option as "Most Popular" or "Best Value" reduces decision fatigue and anchors customers to a specific choice. They can still pick a different tier, but they start from a recommendation rather than a blank slate. Highlighted tiers convert better—not because customers blindly follow suggestions, but because recommendations provide useful information about what most people need. Anchor pricing also shapes perception through contrast. When you present a $29 tier next to a $149 tier, the $29 option feels affordable by comparison. The $149 tier might only convert a small percentage of visitors, but its presence makes the middle tier look reasonable. This isn't manipulation; it's recognition that humans evaluate prices relative to available alternatives, not in absolute terms. The order of tiers matters too. Research from CXL found users showed preference for higher-priced options when expensive tiers were presented first—though lab preferences don't always match real purchasing behaviour. When customers see $299/month first, $99/month feels like a deal. When they see $29/month first, $99/month feels like a big step up. Neither ordering is inherently better—the right choice depends on whether you want to anchor high or lead with accessibility. ## Outcome-First Pricing Page Design Fixing a feature-focused pricing page isn't complicated, but it requires rethinking what the page is for. Instead of answering "What do I get?" first and hoping customers figure out fit, answer "Is this right for me?" first and let features support that answer. Start each tier with a clear statement of who it's for or what outcome it delivers. Not "10 users included" but "For small teams collaborating on projects." Not "Advanced security" but "For organisations with compliance requirements." These outcome statements help customers see themselves in the tier before evaluating features. Below the outcome statement, include a brief value proposition—what makes this tier worth the price. "Everything you need to ship your first project" works better than a feature list because it speaks to the customer's goal, not the product's capabilities. Features should support the outcome and value proposition, not replace them. Include the key differentiators that matter for the target segment, but resist the urge to list everything. Three to five bullet points are usually enough. Save the comprehensive feature list for a secondary comparison view. Social proof reinforces fit. Testimonials from customers in the target segment ("Perfect for our three-person design team") help visitors see themselves in your existing customer base. The specific customer type matters more than generic praise. ## The Call-to-Action Gap Even pricing pages that get the content right often stumble at the final step: the call to action. Vague CTAs like "Get Started" or "Learn More" create uncertainty about what happens next. Customers wonder: Will I be charged immediately? Do I need to talk to sales? Is there a free trial? Effective CTAs are specific about the next step. "Start your 14-day free trial" tells customers they can try before buying. "Talk to sales" sets expectations for enterprise tiers that require custom pricing. "Buy now" is appropriate when customers can complete the purchase immediately. The CTA should also resolve potential objections. "No credit card required" eliminates a common friction point for free trials. "Cancel anytime" addresses fear of commitment. "See a demo first" offers an alternative for customers not ready to commit. Different tiers often warrant different CTAs. Self-serve tiers might lead directly to signup, while enterprise tiers route to a sales conversation. Forcing all customers through the same funnel ignores that they're at different stages of readiness. ## Testing Beyond the Obvious Most pricing page optimization focuses on the obvious: button colors, tier names, feature lists. But the highest-impact changes often come from testing structural assumptions. Test whether customers actually want to see all features, or whether a simplified presentation converts better. Test whether recommended tiers help or feel pushy for your specific audience. Test whether annual pricing prominently displayed increases commitment or creates sticker shock. Consider testing entirely different page structures. Does a single landing page for all tiers work better than separate pages per tier? Does a calculator that recommends tiers based on inputs outperform static comparison? Does embedding pricing in the main navigation versus hiding it behind "Contact" affect perception? The meta-lesson: best practices are starting points, not destinations. Your pricing page exists in the context of your specific product, audience, and market. What works for another SaaS might not work for you. ## From Comparison to Recommendation Your pricing page's job isn't to provide information—it's to help customers make a confident decision. Feature matrices provide information but don't guide decisions. They leave customers to figure out fit on their own, armed with details that may or may not matter to their situation. The shift from features to outcomes transforms your pricing page from a spec sheet into a recommendation engine. Lead with who each tier is for. Frame value in terms of customer outcomes. Use features to support the value proposition, not replace it. Recommend a tier rather than presenting options as equals. People don't want to compare features. They want to know which option is right for someone like them. "For teams shipping weekly" tells them more than "Unlimited deployments" ever could. Answer the fit question first, and the feature question answers itself. Your pricing page is too important to be a passive repository of information. Make it an active guide that helps visitors see themselves as customers. --- ### Real-Time Billing Events for Your Application Source: https://salable.app/blog/features/webhooks-real-time-billing-events # Real-Time Billing Events for Your Application Your customer just upgraded their plan, but your application still shows them the old features. Maybe your cron job will catch it in an hour. Maybe tomorrow. Polling for subscription changes works until it doesn't, and when it fails, customers notice. Webhooks flip the model. Instead of asking "has anything changed?" every few minutes, your application receives a notification the moment a change occurs. Subscription created, payment failed, usage recorded: your system knows immediately. The customer upgrades at 2:47 PM, and by 2:47 PM their new features are live. The polling approach seemed reasonable when you built it. Every fifteen minutes, your background job queries the billing API for subscriptions modified since the last check. It processes any changes, updates your local database, and goes back to sleep. The simplicity was appealing, and for low volumes it worked fine. Then you scaled. The fifteen-minute window started feeling long when customers complained about delayed access. You shortened it to five minutes, then one minute. Now your job runs constantly, making API calls that usually return empty results. Your rate limits are stressed. Your billing API costs increased. And the fundamental problem remained: customers still waited up to a minute for changes to propagate. Webhooks eliminate this latency entirely. The billing system pushes events to your application as they occur. The upgrade happens, the event fires, your handler runs, and the features unlock. The delay drops from minutes to milliseconds. ## The Event-Driven Model Understanding webhooks requires shifting from pull to push thinking. In the polling model, your application is the active party, asking the billing system for updates on a schedule you control. In the webhook model, the billing system is the active party, pushing updates to an endpoint you've registered. This inversion has profound implications for how you architect your application. Polling code typically lives in a scheduled job that reads data and updates state. Webhook code lives in an HTTP handler that receives data and updates state. The logic is similar, but the trigger is different. Webhooks arrive as HTTP POST requests to an endpoint you specify. The request body contains a JSON payload describing what happened: the event type, the relevant objects (subscription, invoice, customer), and timestamps. Your handler parses the payload, validates that it's legitimate, and processes the event. The key mental model is that webhooks are facts about things that happened. "Subscription SUB-123 was upgraded to Professional plan at 14:47:23 UTC." Your handler's job is to update your system's state to reflect this fact. The subscription in your database should now show Professional plan. The customer's entitlements should reflect Professional features. ## Event Types That Matter Salable sends webhooks for all significant billing events. Knowing which events to handle lets you build responsive applications that stay synchronised with billing state. **Subscription lifecycle events** tell you when subscriptions are created, updated, or cancelled. A new subscription means a new customer to provision. An updated subscription might mean changed entitlements or seat counts. A cancelled subscription means access should end at the appropriate time. **Payment events** inform you about billing success and failure. A successful payment confirms continued access. A failed payment might trigger a grace period or dunning flow. A dispute or refund might require special handling depending on your policies. **Invoice events** track the billing document lifecycle. An invoice created event lets you add custom line items before finalization. An invoice paid event confirms payment processing. An invoice past due event might trigger account restrictions. **Usage events** for metered billing confirm that usage records were received and will be included in the next invoice. These events provide an audit trail and let you verify that your usage reporting is being processed correctly. Each event type has a defined payload structure documented in Salable's webhook reference. The structure includes the event type identifier, a timestamp, and the relevant objects with their current state. ## Building a Robust Webhook Handler A production webhook handler needs more than a simple HTTP endpoint. Several concerns require careful implementation to ensure reliability. **Signature verification** ensures that webhooks actually came from Salable and weren't forged by attackers. Each webhook includes a signature header computed from the payload and a secret key. Your handler must verify this signature before processing. Salable's SDKs include signature verification functions, so you don't need to implement the cryptography yourself. ```javascript const isValid = salable.webhooks.verify(payload, signatureHeader, webhookSecret); if (!isValid) { return res.status(401).send('Invalid signature'); } ``` **Idempotent processing** handles the case where the same event arrives multiple times. Network issues, retries, and edge cases can cause duplicate delivery. Your handler should process each event exactly once, even if it's received multiple times. Typically this means checking whether you've already processed an event ID before taking action. **Response timing** matters because webhook delivery systems expect quick responses. Your handler should acknowledge receipt by returning a 200 status code promptly, ideally within a few seconds. If your processing takes longer, acknowledge first and process asynchronously. A handler that times out will cause retries and potential duplicate processing. **Error handling** determines what happens when processing fails. Return a 5xx status code to signal that Salable should retry the webhook. Return a 4xx status code if the webhook itself is malformed and retries won't help. Log errors with enough context to debug later. ## Handling Common Event Patterns Certain event sequences appear repeatedly across applications. Understanding these patterns helps you implement correct handling. **Subscription creation** typically triggers user provisioning. When you receive a `subscription.created` event, you might create a workspace for the customer, initialize their settings, send a welcome email, and update your customer database. The order of these operations might matter; ensure the workspace exists before sending an email with a link to it. **Subscription upgrade** means entitlement changes. The customer's old features should continue working while new features become available. Check the `previousAttributes` field in the event payload to see what changed, and update your entitlement cache accordingly. **Payment failure** starts a grace period in most applications. The customer's access continues temporarily while you attempt to collect payment. Send notifications encouraging them to update their payment method. After a configured number of retries or days, a follow-up event indicates whether payment succeeded or the subscription should be suspended. **Subscription cancellation** has immediate and scheduled variants. An immediate cancellation means access ends now. A scheduled cancellation (at period end) means access continues until the paid period expires. Your handler should check the cancellation timing and act appropriately. ## Testing Webhook Handlers Webhook handlers are notoriously difficult to test because they receive external events that are hard to simulate. Several strategies make testing tractable. **Local testing** with webhook forwarding lets you receive real webhooks on your development machine. Tools like ngrok or Cloudflare Tunnel expose your localhost to the internet, giving you a public URL to configure as your webhook endpoint. This lets you trigger real events and see how your handler responds without deploying to a server. **Payload capture** during local development gives you test fixtures. Log the raw payloads your handler receives, then save representative examples as JSON files. Replay these in unit tests to verify your parsing, validation, and processing logic without network dependencies. **Integration tests** in staging environments verify the full flow. Create a test subscription, observe the webhook arrive, verify your handler processed it correctly. Automate these tests to run on deployment. **Monitoring in production** catches issues that testing misses. Track webhook reception rates, processing times, and error rates. Alert on anomalies like sudden drops in events or spikes in errors. The sooner you notice a problem, the sooner you can fix it. ## Webhook Reliability and Retries Network failures happen. Servers go down. Bugs cause handlers to crash. A robust webhook system handles these failures gracefully. Salable's webhook delivery includes automatic retries with exponential backoff. If your handler returns an error or times out, the webhook is retried after a delay. The delay increases with each retry, preventing retry storms from overwhelming a struggling server. The retry schedule follows a pattern: immediate retry, then one minute, five minutes, thirty minutes, two hours, and so on up to a configurable maximum. Most transient failures resolve within the first few retries. Persistent failures eventually stop retrying, and the event moves to a dead letter queue for manual investigation. Your handler should be idempotent to handle retries gracefully. If the first attempt partially succeeded before failing, the retry shouldn't double-count the action. Check whether the event was already processed, or design your operations to be naturally idempotent (setting state rather than incrementing counters). ## Monitoring and Debugging Webhook-driven architectures require visibility into the event flow. When something goes wrong, you need to understand what events arrived, how they were processed, and what state resulted. **Logging every event** provides an audit trail. Log the event type, relevant IDs, timestamp, and processing outcome. When investigating an issue, you can trace from a customer complaint to the events that should have fired to how your handler processed them. **Monitoring delivery health** catches systemic issues. Track the volume of webhooks received over time. A sudden drop might indicate a configuration problem or Salable infrastructure issue. A sudden spike might indicate unusual activity worth investigating. **Dashboard visibility** through Salable's interface shows recent webhooks, their delivery status, and response codes from your handler. When debugging, you can see exactly what was sent and whether your endpoint acknowledged receipt. **Alert on failures** to catch problems before customers report them. If your handler starts returning errors, you want to know immediately. Configure alerts for error rate thresholds so you can investigate and fix issues proactively. ## Moving Beyond Polling Migrating from polling to webhooks typically happens incrementally. You add webhook handlers alongside existing polling, verify they work correctly, then remove the polling once you trust the new system. During the transition, reconciliation jobs help catch discrepancies. Run a periodic job that compares your local state against the billing API. Any differences indicate either a missed webhook or a handler bug. As your webhook handling matures, these reconciliation runs should find fewer issues until you're confident enough to remove them. The operational benefits of webhooks compound over time. Lower API usage reduces costs. Faster propagation improves customer experience. Event-driven architecture enables real-time features that polling can't support. The initial investment in building robust handlers pays dividends in everything built on top of them. ## Real-Time Applications Webhooks enable application patterns that aren't possible with polling. **Instant feature unlocks** let customers use new features the moment they pay. No more "your access will be updated within the hour" messages. The upgrade event fires, your handler runs, and the feature is live. **Payment failure responses** can be immediate and contextual. Instead of a batch email hours later, you can show an in-app notification the moment payment fails. "We couldn't charge your card, click here to update" appears while the customer is actively using your product. **Usage dashboards** can update in real-time as metered events are recorded. The customer watches their usage count increment with each API call, building confidence that billing will be accurate. **Automated workflows** trigger on billing events without delay. A new subscription could kick off an onboarding sequence. A cancellation could trigger a feedback survey. A payment failure could assign a task to your success team. The speed of webhooks makes these workflows feel automatic rather than batched. Your billing system becomes a real-time data source rather than a database you periodically sync. Events flow through your application as they happen, keeping every system aligned without manual intervention or scheduled jobs. The customer upgraded at 2:47 PM, and by 2:47 PM everything in your application reflects that upgrade. That's the power of webhooks. --- ### SaaS Billing Doesn’t Need More Plans, It Needs a Cart Source: https://salable.app/blog/features/cart-system-addons # SaaS Billing Doesn’t Need More Plans, It Needs a Cart Your customer wants your Professional plan plus the Analytics add-on, the API Access module, and maybe the White Label extension. Traditional billing systems would force your customers to choose between individual subscriptions for each add-on or a monolithic plan that bundles more than they want. Neither of these options is convenient or ideal for customers. Salable's cart system solves this by letting customers purchase multiple plans as a single subscription. The core product and add-ons live, are billed, and are managed together. And when needs change, customers can add new capabilities, remove what they don't use, or replace one add-on with another, all without creating subscription chaos. Managing individual subscriptions can fester a plethora of inconveniences for customers. Each subscription operates on its own lifecycle, which may result in differing renewal dates. Cancellation of a product's subscription may not automatically cancel its add-ons, and managing entitlements across multiple subscriptions and products can become cumbersome. Monolithic bundles avoid these problems but create new ones. If the Analytics add-on and the API Access module are bundled into your Professional plan, customers who want only one must pay for both. Those who need neither but want the core Professional features are subsidising capabilities they don't use. Unbundling creates more plans, leading to a more complex pricing page and harder purchasing decisions for customers. The cart model lets you combine products and add-ons without treating them as a single packaged offer. Products and add-ons remain separate for pricing and entitlement purposes, but they combine into a single subscription for billing and lifecycle management. Customers build their own bundle from your menu of options. ## How Carts Create Composable Subscriptions A cart in Salable works like a shopping cart in e-commerce, but for subscription products. Customers add plans to their cart, and when they check out, all the plans become line items in a single subscription. The resulting subscription has a unified billing cycle. All line items renew together. The customer sees one charge on their credit card statement, not separate charges for each component. If they view their subscription in your customer portal, they see everything they've purchased as parts of a whole. Your customers will gain all entitlements from all plans in the subscription. If the Professional plan includes project management features and the Analytics add-on includes dashboard features, the customer has access to both. Your entitlement checks are agnostic of the contents of the cart and will simply check if your customer has the specified entitlement in their subscription. Customers have the flexibility to compose their cart; after purchasing a subscription, all contents of their cart behave as a single unit. ## Designing for Cart-Based Purchasing The cart system may influence your pricing structure. Instead of creating comprehensive plans, you should create a core plan for your product along with a catalogue of add-ons. The core plan should provide standalone value. It's what most customers need and what defines your product's primary use case. Everything else becomes optional add-ons that extend, enhance, or specialise the core functionality. Add-ons typically fall into a few categories. Feature extensions add capabilities beyond the core product, like advanced analytics, API access, or white-label options. Capacity extensions increase limits, like additional storage, more users, or higher API rate limits. Professional services like priority support, dedicated account management, or custom training can also be add-ons rather than bundled into premium tiers. The key principle is that add-ons should provide independent value. A customer who buys the Analytics add-on should receive clear benefit from it regardless of whether they also buy the API Access module. If two add-ons only make sense together, consider combining them into a single add-on or making one dependent on the other. ## The Checkout Experience Cart-based checkout needs UI that supports browsing and selection. Unlike single-product checkout, where the customer clicks a plan and enters payment details, cart checkout involves building a bundle before payment. Your pricing page becomes more of a product catalogue. Each option shows what it provides and what it costs. Customers can select their core plan and then browse available add-ons, adding them to their cart as they shop around The cart summary should be visible throughout the selection process. Customers need to see their running total and the list of what they've selected. Changes to the cart should update the summary immediately to ensure the total price is clear prior to checkout. For simple product catalogues, an accordion or toggle interface works well. Core plans show prominently, add-ons appear as checkboxes or toggles beneath them, and the selected items stack in a sidebar cart. For more complex catalogues, a dedicated cart page where customers review and adjust their selections before proceeding to payment might be clearer. The checkout should collect payment and process the final cart. Salable will handle the cart-to-subscription conversion and generate a single subscription for your customer. The customer's payment method is charged for the combined total. ## Modifying Subscriptions After Purchase One advantage of the cart model is that subscriptions can be modified without wholesale replacement. Customers can add new add-ons to an existing subscription, remove add-ons they no longer need, or replace one add-on with another. Adding an add-on to an existing subscription happens immediately—no cart required. The customer selects the new add-on, and the subscription updates to include it. Their payment method is already on file, so billing prorates automatically, charging for the remainder of the current billing period without requiring another checkout flow. Removing an add-on follows a similar flow but in reverse. The customer indicates they want to remove something, and the subscription adjusts. Depending on your policies, the removal might be immediate or take effect at the next renewal. Credits for unused time can apply or not based on how you've configured the add-on. Replacing one add-on with another, such as swapping a standard support add-on for a premium support add-on, combines removal and addition in a single operation. The customer sees this as an upgrade or change rather than two separate transactions. ## Managing Complex Bundles Some products have interdependencies between add-ons. For example, the API Access module may require the Professional plan or the White Label extension may be incompatible with another add-on. You should ensure your checkout flow handles the constraints that you have established. The White Label extension might be incompatible with certain integrations. Your checkout flow needs to handle these constraints. Implement availability logic in your pricing page and checkout UI. When a customer selects a core plan, show only the add-ons compatible with that plan. Hide or disable options that don't apply, with explanations where helpful. This filtering happens in your application before the cart reaches Salable, ensuring customers only see valid combinations. For complex dependency trees, document the relationships clearly for customers. A diagram showing which add-ons work with which plans, or a guided selection flow that filters options based on prior selections, helps customers navigate without frustration. ## Billing and Revenue Recognition Cart-based subscriptions consolidate billing but still provide line-item visibility for accounting and analysis. Invoices show each plan in the subscription as a separate line item. The Professional plan is \$99, the Analytics add-on is \$29, the API Access module is \$49; the total charge is \$177. This breakdown helps customers understand their charges and simplifies expense allocation for businesses that track costs by category. For your revenue reporting, you can see performance at the line item level. Which add-ons are most popular? What's the average add-on revenue per subscription? Which combinations drive the highest lifetime value? This granularity helps you optimise your product catalogue and pricing. Revenue recognition treats the subscription as a unit but can segment by line item if needed. The total monthly recurring revenue from a cart-purchased subscription attributes appropriately across the components that comprise it. ## The Strategic Advantage Cart-based purchasing isn't just operational convenience; it's a strategic advantage in how you monetise and grow. Modularity lets you serve more customer segments without proliferating plans. Instead of Starter, Professional, Professional Plus, Professional Plus with Analytics, and Professional Enterprise bundles, you have a core product and a menu. Customers build what they need; you don't need to anticipate every combination. Expansion revenue becomes natural. When a customer needs more capability, they add an add-on. The upsell conversation is "would you like this specific thing?" rather than "would you like to pay more for a bigger package?" Churn reduction comes from precise fit. Customers who have exactly what they need are less likely to leave than customers paying for capabilities they don't use. The subscription matches their requirements rather than approximating them. Testing new products becomes simpler. Launch a new add-on, make it available in the cart, see who buys it. If it succeeds, keep it. If it doesn't, remove it. The cart architecture supports experimentation without restructuring your core offering. ## Building for Carts Implementing cart-based purchasing requires changes across your pricing page, checkout flow, customer portal, and entitlement logic. The scope is significant but the components are well-defined. Your pricing page needs to support selection and cart building. This is primarily a frontend concern: displaying options, tracking selections, showing the running total, and passing the cart to checkout. Your checkout flow needs to accept a cart and create a composed subscription. Salable's checkout handles this, receiving the cart items and processing them into a single subscription. You pass the cart contents; the system handles the rest. Your customer portal needs to show the subscription composition and support modifications. Customers should see what's in their subscription, what add-ons are available, and have clear paths to add or remove items. Your entitlement logic doesn't need to change if it's already checking subscription entitlements. The entitlements from all cart items combine in the subscription, so standard entitlement checks work without modification. ## Conclusion The cart system transforms your billing from a constraint into an enabler. Customers get exactly what they need. You capture revenue from every capability they value. And the subscription relationship remains unified, simple to manage, easy to understand, and straightforward to modify as needs evolve. Your Professional plan plus Analytics plus API Access plus White Label becomes one subscription, one invoice, one relationship. That's what add-ons done right looks like. --- ### Why Letting Customers Pay Less Makes More Money Source: https://salable.app/blog/insights/letting-customers-pay-less # Why Letting Customers Pay Less Makes More Money Every customer extracts different value from your product. Usage patterns, team sizes, and priorities vary enormously—and a handful of predefined tiers can only approximate that reality. The approximation works for most customers most of the time, but when it doesn't, the mismatch pushes them toward cancellation. Customers who'd happily keep paying are forced out if you're unwilling to compromise and offer them a middle ground. ## The Binary Choice Tiers bundle features together, and that bundling creates friction the moment a customer's needs don't align with what's on offer. A customer who relies on three features from your Starter plan and one from Professional is stuck. The full tier jump costs more than the single feature is worth to them, so they go without it—or worse, they start wondering whether a competitor offers a better fit. Customers who do make the leap to the higher tier are reminded they're overpaying for features they'll never use every time they receive an invoice. Both situations push toward cancellation, and the underlying cause is the same. Rigid tiers turn pricing into an all-or-nothing proposition. The customer would happily pay _something_ in between, but "in between" doesn't exist on your pricing page. So they leave. ## The Cost of Leaving Losing a customer costs more than merely the loss in revenue. Acquiring a new customer costs [somewhere between five and twenty-five times more](https://hbr.org/2014/10/the-value-of-keeping-the-right-customers) than keeping an existing one, with [SaaS acquisition costs ranging from a few hundred to several thousand dollars per customer](https://firstpagesage.com/reports/b2b-saas-customer-acquisition-cost-2024-report/) depending on your industry and segment. Every cancellation writes off that investment entirely. Run the numbers on a specific scenario. A customer on your \$149/month Professional plan isn't using half the features in their tier. They cancel. That's \$1,788 in annual revenue gone, plus the full acquisition cost sunk. Dropping them to your \$49/month Starter tier might not work either—if the features they do use aren't on that plan, there's nowhere to step down. But a tailored plan at \$79/month keeps \$948 coming in—revenue that continues paying back your acquisition investment rather than evaporating. And beyond the revenue, you lose the relationship. Once a customer has cancelled, they're back on the open market evaluating alternatives. Winning them back means re-engagement campaigns and win-back offers that eat into your margins—assuming they come back at all. A customer who stays, even at a lower price, keeps your product embedded in their workflow. ## Tailored Plans You could add a fourth or fifth tier, and it might fit some customers better—but there will always be edge cases. More plans also means a more confusing product, making checkout decisions harder and leading to failed conversions. The solution is tailored plans—arrangements built around what a customer actually uses. Instead of rigid bundles, you treat each feature as a granular entitlement. Your standard tiers are just predefined combinations of those entitlements, and you can recombine them to accommodate whatever deal you need to make. Someone overpaying for features they don't need gets a plan with entitlements trimmed to their actual usage at a lower price. Someone on a lower tier who needs a single entitlement from the tier above gets exactly that, without paying for everything else bundled alongside it. The result is a plan that accurately reflects the value the customer receives. Less revenue than a full tier upgrade, more revenue than a cancellation. Your standard tiers stay clean for self-service customers while tailored arrangements happen behind the scenes for those who don't fit the mould. ## The Maths A single month's invoice tells you almost nothing about a customer's value. Revenue across the entire relationship is what matters. A customer on a tailored plan at \$79/month for three years contributes \$2,844. A customer who pays \$149/month for six months before cancelling contributes \$894. The monthly revenue is lower, but the lifetime revenue is often higher. And the Starter customer on \$49/month who adds a single feature at \$30/month generates \$360/year you'd never capture if the only option was a full tier jump to \$149. Multiply that across your customer base. If ten percent of your customers would cancel over a pricing mismatch, and you retain even half of them through flexible arrangements, the numbers compound quickly. Each retained customer is revenue you keep without spending a penny on acquisition. Each one is a customer who stays in your product, benefits from every improvement you ship, and grows with you instead of evaluating your competitors. Rigid tiers optimise for simplicity at the point of sale. Flexible pricing optimises for the months and years that follow. ## The Upgrade Potential Retained customers on tailored plans are future revenue without further acquisition costs. You've already done the hard work of getting them into your product. They know how it works, their data is there, and switching to a competitor means starting over. Every one of them costs less to keep than to win back or replace. A tailored plan at a lower price isn't a ceiling—it's a starting point. There's a gap between what the customer pays now and what they could pay as they grow, and that gap is your upside. As their team expands or their needs change, each new requirement is an opportunity to sell them more entitlements. ## Tailored Plans, Built into Salable Salable supports this out of the box. Entitlements are a core primitive — every feature in your product can be packaged, combined, and recombined into whatever plan a customer needs. Create your standard tiers for self-service, then build tailored plans for the customers who don't fit. No custom billing logic, no workarounds. The flexibility is built into the platform. Customers who can pay less tend to stay longer, and customers who stay longer generate more revenue over time. --- ### Stop Trying to Hack Team Subscriptions into Stripe Source: https://salable.app/blog/features/grantee-groups-team-subscriptions # Stop Trying to Hack Team Subscriptions into Stripe Per-seat pricing sounds simple until you implement it. Who pays and who uses aren't the same question. A company administrator buys 50 seats, but the individual team members need access. Adding someone to the subscription shouldn't require the billing owner to click buttons in your UI. And what happens when someone leaves the team? Grantee groups model these relationships explicitly. The owner holds the billing relationship, the grantees receive access, and the group manages membership. Changes propagate automatically, seat limits enforce themselves, and your code doesn't need to track who paid for whom. The complexity sneaks up on you. Early customers are individuals who buy subscriptions for themselves, so the payer and the user are the same person. Your authentication checks if the current user has a subscription, and everything works. Then a team signs up. The CTO purchases a subscription for their engineering department. Ten engineers need access, but only the CTO has a billing relationship with you. Your authentication logic breaks because the engineers don't have subscriptions in their names. The quick fix is custom code. You create a mapping table that says "these ten user IDs belong to this subscription." You check that table during authentication. It works until the team grows to twenty, someone leaves, and the CTO asks why they're still paying for departed employees. Now you need admin interfaces for the CTO to manage team membership, and your subscription logic is scattered across multiple systems. Grantee groups solve this properly from the start. The concepts are distinct and the relationships are explicit, which means your code stays simple even as team complexity grows. ## The Three-Part Model Understanding grantee groups requires distinguishing three concepts that traditional billing systems conflate: owners, grantees, and groups. The **owner** identifies who holds the subscription—typically an organisation ID, team ID, or user ID from your system. This is the identifier your application passes when looking up subscription status or recording metered usage. It represents whoever is responsible for the billing relationship: the company that purchased, the team that subscribed, or the individual user. When subscriptions renew, cancel, or upgrade, the owner is the party involved. **Grantees** are the individuals who receive access to your product through the subscription. They don't have their own billing relationship; instead, they derive access from membership in a group that's associated with the subscription. A grantee might be an employee using company-licensed software, a team member added by an administrator, or a collaborator invited to a shared workspace. The **group** connects owners and grantees. It's the container that holds grantees and associates with subscriptions. When you check whether a user has access to a feature, you're really asking whether they're a grantee in a group that has an active subscription with the appropriate entitlements. This separation might seem like unnecessary complexity, but it reflects how teams actually work. The person with the corporate credit card isn't the same as the people using the product. The list of users changes more frequently than the billing relationship. And the same owner might have multiple groups with different subscriptions for different purposes. ## How Access Flows Through the System When a grantee tries to access a feature, the access check follows a clear path: retrieve the groups the grantee belongs to, find the subscriptions associated with those groups, and check whether any of those subscriptions include entitlements for the requested feature. In practice, this means your authentication middleware calls a single function with the grantee's ID and the entitlement they need. Salable handles the traversal from grantee to groups to subscriptions to entitlements and returns a yes or no answer. Your code doesn't need to understand the relationship model; it just asks "can this user do this thing?" and gets a definitive response. This design scales elegantly. A grantee might belong to multiple groups, perhaps their department group and a cross-functional project group. Each group might have different subscriptions with different entitlements. The access check considers all paths and grants access if any valid path exists. The performance implications are handled at the infrastructure level. Salable caches the relationship graph so that access checks are fast even for complex group structures. When group membership changes, the cache updates automatically. You don't need to implement caching logic or worry about stale permissions. ## Managing Team Membership The owner needs to control who belongs to their group, but that control shouldn't require navigating your billing interface for every change. Grantee groups support self-service membership management through APIs and embeddable components. The simplest pattern is an invitation flow. The owner generates an invitation link or enters email addresses. Invited users accept and become grantees in the group. No manual provisioning on your side, no webhook handlers to map users to subscriptions, no administrative overhead. For organisations with existing identity providers, grantee groups can sync with external directories. When an employee joins or leaves in the company's HR system, their group membership updates accordingly. The billing subscription doesn't change; only the list of grantees who derive access from it does. Seat limits enforce themselves through the group. If a subscription includes 50 seats, the group can have at most 50 grantees. Attempting to add more fails with a clear error indicating the seat limit. The owner can either remove existing grantees or upgrade their subscription to add more seats. This enforcement happens at the group level, not in your application code. ## Handling Complex Organizational Structures Real organisations rarely fit a simple hierarchy. Departments, teams, projects, and temporary working groups create overlapping structures that traditional per-seat billing can't model. Grantee groups handle this complexity through multiple groups per owner and multiple group memberships per grantee. A company might have a department-level group for standard access and project-specific groups for specialized features. An employee belongs to their department group and gets added to project groups as needed. Each group can have its own subscription or share entitlements, depending on how you structure your plans. Consider an enterprise with three departments, each needing your product. Rather than one massive subscription, they might prefer three separate subscriptions with independent billing, budgeting, and administration. Three groups, three subscriptions, one owner at the corporate level. Department administrators manage their own grantees without accessing other departments' groups. Alternatively, a company might want unified billing but department-level usage tracking. One subscription, three groups, with reporting segmented by group. The billing administrator handles payments while department managers handle membership. The flexibility is in how you model the relationships, not in custom code you write. ## The Developer Experience For engineering teams implementing team subscriptions, grantee groups dramatically simplify the integration. The Salable SDK provides methods for the common operations: checking access, managing grantees, creating invitations, and querying group membership. Access checks are the most frequent operation and the simplest to implement. A single API call returns whether a grantee has a specific entitlement through any of their group memberships. You don't need to understand the underlying subscription structure; the answer is already resolved. ```javascript const hasAccess = await salable.entitlements.check({ granteeId: currentUser.id, entitlement: 'advanced-analytics' }); ``` For administrative interfaces, the SDK provides methods to list group members, add and remove grantees, and check available seat capacity. These power the team management UI that owners use, whether you build custom interfaces or embed Salable's components. When subscriptions change, webhooks notify your application. A new subscription might mean creating a group. An upgrade might mean increased seat capacity. A cancellation might mean revoking access. These events arrive in real-time, allowing your application to respond immediately rather than polling for changes. ## Migrating Existing Team Implementations If you already have team subscriptions with custom membership logic, migrating to grantee groups means mapping your existing data model to the new concepts. The migration typically involves three steps. First, identify your current owners, those who have billing relationships in your system. Create corresponding owners in Salable with the same identifiers. Second, create groups for each team structure you're currently tracking manually. Third, add your existing team member mappings as grantees in the appropriate groups. Once migrated, you can remove your custom membership tables and the code that queries them. Access checks route through Salable instead of your database. Team management happens through the grantee group interfaces instead of your admin panels. The reduction in custom code translates directly to reduced maintenance burden. For gradual migration, you can run both systems in parallel during a transition period. Check your existing tables first and fall back to grantee groups, or vice versa. This approach lets you validate the new system before fully committing. ## Scaling to Enterprise Grantee groups aren't just for small teams; they're designed to handle enterprise scale. Organisations with thousands of employees and complex hierarchies use the same primitives as ten-person startups. The difference is in configuration, not code. Bulk operations handle large-scale changes efficiently. Importing hundreds of grantees from a CSV or syncing thousands of users from an identity provider happens through optimised bulk APIs rather than individual requests. The system is built for enterprise data volumes. Security controls at the group level let you implement principle of least privilege. Different groups can have different entitlements, and users only receive the entitlements from groups they belong to. Removing someone from a high-privilege group immediately revokes those entitlements even if they remain in other groups. ## The Simplicity of Explicit Relationships The fundamental insight behind grantee groups is that explicit modeling beats implicit inference. When relationships between payers, users, and access are explicit in your data model, everything else gets simpler. Access checks become lookups rather than computations. Team management becomes data updates rather than business logic. Scaling becomes capacity rather than architectural rework. Your application code asks simple questions and gets simple answers. Can this user access this feature? Yes or no. How many seats are available in this group? A number. Who are the members of this group? A list. The complexity of team subscriptions is handled in the infrastructure where it belongs, not scattered throughout your application. Per-seat pricing still sounds simple, because with grantee groups, it actually becomes simple. The billing owner, the access recipients, and the group that connects them are all first-class concepts with explicit relationships. Your implementation reflects reality instead of working around a data model that assumes every payer is also a user. The CTO who buys 50 seats can manage their team without understanding your billing system. Engineers get access without needing subscription records in their names. And when someone leaves, removing them from the group is all it takes. The subscription continues, the seat frees up, and the access revokes automatically. That's team subscriptions done right. --- ### Stripe Webhooks Without the Pain Source: https://salable.app/blog/features/stripe-webhooks-without-pain # Stripe Webhooks Without the Pain The Stripe webhook documentation makes it look easy: receive an event, verify the signature, process the payload. Three steps. What the documentation doesn't mention is what happens when your server is down during a critical event. Or when the same event arrives twice. Or when a subscription update arrives before the subscription created event. Or when your database transaction fails mid-processing and Stripe retries the webhook while you're in an inconsistent state. Production webhook handling is a reliability engineering problem that most teams underestimate until they're debugging lost subscriptions at 2 AM. Salable's infrastructure handles these edge cases with a battle-tested pipeline that doesn't drop events. Every developer who's integrated Stripe has a war story. The customer who was charged but never got access because the webhook handler crashed after charging but before provisioning. The duplicate charge that occurred because the handler didn't check for idempotency. The subscription that shows as active in Stripe but cancelled in the application because events arrived out of order. These bugs are insidious because they're intermittent, they affect paying customers, and they're hard to reproduce. ## The Hidden Complexity Understanding why webhooks are hard requires examining what can go wrong at each stage. **Network reliability** is the first concern. Stripe sends a webhook; your server receives it; the response returns to Stripe. Any of these network hops can fail. Packets get lost. Connections time out. DNS hiccups. Your server might receive the webhook but Stripe might not receive your acknowledgment, causing a retry of an event you already processed. **Server availability** matters because webhooks arrive on Stripe's schedule, not yours. A deployment that restarts your application, a spike in traffic that overwhelms your server, or a cloud provider hiccup can all cause missed webhooks. Stripe will retry, but the retry schedule spans hours, and your customers shouldn't wait hours for their purchase to process. **Processing failures** occur even when the webhook arrives successfully. Your handler might throw an exception mid-processing. The database connection might fail during a write. An external API your handler calls might time out. If processing fails after some side effects but before others, you're left in an inconsistent state. **Duplicate delivery** is explicitly documented by Stripe but often ignored by developers. "Your webhook endpoints might occasionally receive the same event more than once." Occasionally sounds rare, but at scale, occasional adds up. Every handler must be idempotent, treating the second delivery of an event the same as the first. **Event ordering** can't be guaranteed. A subscription might be deleted before the corresponding creation event arrives. An invoice payment event might arrive before the invoice creation event. Events that seem sequential can arrive in any order, and your handler must cope. ## The Idempotency Requirement Every webhook handler must be idempotent, meaning processing the same event twice produces the same result as processing it once. This requirement sounds simple but affects how you design every handler. The naive approach to handling a subscription creation event might be: create a user record, create a subscription record, send a welcome email. If this handler runs twice, you might create duplicate users, duplicate subscriptions, or send duplicate emails. None of these outcomes are acceptable. The idempotent approach tracks event IDs. Every Stripe webhook carries a unique event identifier, and the first thing a reliable handler does is check whether that identifier has been processed before. If it has, the handler returns success immediately—the work is already done. If the event is new, processing proceeds normally, and the event ID is recorded upon completion. This single check at the event level prevents duplicate users, duplicate subscriptions, and duplicate emails regardless of how many times Stripe delivers the same webhook. Implementing idempotency correctly requires careful thought about what "already processed" means. An event might have been partially processed before a failure. Your checks need to verify that all effects were applied, not just that processing began. ## Event Ordering Challenges Stripe sends events in roughly chronological order, but "roughly" isn't a guarantee. Network latency, retry timing, and parallel event generation can all cause events to arrive out of order. Consider subscription creation and immediate update. A customer creates a subscription and immediately changes a setting. Stripe generates `subscription.created` and then `subscription.updated`. Due to network timing, the update arrives first. Your handler tries to update a subscription that doesn't exist yet, fails, and returns an error. Stripe retries the update later; maybe the creation has processed by then, maybe not. The robust solution is to read from Stripe's API when processing each event. Instead of relying on the event payload, fetch the current state of the entity directly. That way you always have the latest data regardless of what order events arrive. ## Retry Logic and Exponential Backoff Stripe retries failed webhooks on an exponential backoff schedule: 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and then hourly for up to 3 days. This schedule means that a server outage lasting an hour might delay event processing for hours, and some events might not be delivered until the next day. For critical events like subscription creation or payment success, this delay is unacceptable. Customers expect immediate access when they pay. Waiting hours because your server happened to be deploying at the wrong moment is a poor experience. ## Database Transaction Hazards Webhook handlers often need to update multiple database records together. A subscription creation might create user, subscription, and entitlement records. If any write fails, the entire operation should roll back. Without transactions, a failure mid-processing leaves your database in a partial state—user created but subscription missing, or subscription created but entitlements not granted. These inconsistencies are difficult to detect and painful to debug. When Stripe retries, your idempotency checks might see existing data and skip steps, leaving the partial state permanently unresolved. With transactions, all your writes succeed or fail together—but this creates new problems. Long-running transactions hold database connections and block subsequent requests. As handlers queue up, failures cascade—each slow transaction makes the next one slower. Eventually, processing takes longer than Stripe's timeout, and Stripe assumes failure and retries while you're still working on the first attempt. ## Queue-Based Processing The solution is to separate receipt from processing entirely. When a webhook arrives, your endpoint does three things: validate the signature, write the event to a durable queue, and return success to Stripe. Nothing else. The entire operation takes milliseconds and never touches your main database. Processing happens separately, driven by workers that pull events from your queue. These workers can take whatever time they need—there's no Stripe timeout looming. If a worker fails mid-processing, the event stays in the queue for another attempt. You control the retry schedule: immediate retries for transient failures, exponential backoff for persistent problems, and configurable limits before giving up. Your transactions can run as long as they need to because they're not blocking Stripe's connection. This architecture is the right answer, but implementing it yourself is substantial work. You need to choose and operate a queue technology—Redis, RabbitMQ, SQS, or similar—and ensure it's configured for durability so events survive restarts and crashes. You need workers that process reliably, handle failures gracefully, and don't duplicate work when retrying. You need visibility into how full the queue is, how long processing takes, and how often it fails so you can spot problems before customers do. You need alerting when things go wrong and tooling to investigate when they do. Most teams underestimate this. What starts as "just add a queue" becomes weeks of infrastructure work, and the system requires ongoing operational attention. It's the right architecture, but it's not simple to build or maintain. ## Dead Letter Queues for Failed Events Some events can't be processed no matter how many times they're retried. An event type your handler doesn't recognise. A payload that fails validation for unknown reasons. These events shouldn't retry forever. After a configured number of attempts, they should route to a dead letter queue for human review. The queue captures the event payload, the error messages from processing attempts, and timestamps. An operator can examine failed events, determine the cause, fix the underlying issue, and reprocess if appropriate. Building a dead letter queue isn't complicated, but building the tooling around it—dashboards, alerting, reprocessing workflows—takes time. ## The Alternative Teams that build their own Stripe integration face a choice: build production-grade webhook handling, or accept the reliability gaps. Building properly takes weeks of engineering time and ongoing maintenance. For most teams, this is a poor use of engineering resources that could go toward product features. Accepting reliability gaps means occasional customers who pay but don't get access, subscriptions that fall out of sync between your application and Stripe, and 2 AM debugging sessions when critical events are lost. The cost shows up in support tickets, churn, and engineering distraction. Salable provides the solution: reliable infrastructure without building it yourself. Your integration stays simple while the hard parts happen behind the scenes. The Stripe webhook docs make it look easy. Three steps: receive, verify, process. In production, those three steps explode into dozens of edge cases, failure modes, and reliability requirements. Salable handles all of it so you can focus on building your product. Your customers subscribe and get access, every time, without you ever having to debug a lost webhook. --- ### ‘Your Customers Want Local Prices, Not Conversions’ Source: https://salable.app/blog/features/multi-currency-support # Your Customers Want Local Prices, Not Conversions Your first international customer just signed up, and they're asking why they have to pay in dollars. Currency conversion fees eat into their budget, and the psychological friction of foreign pricing makes your product feel less accessible. You could calculate exchange rates manually, but they fluctuate daily, and your billing system lacks currency-handling logic anyway. Multi-currency support removes this barrier. Configure prices in the currencies your customers use, and let the checkout flow present the right price automatically. The frustration shows up in support tickets and abandoned checkouts. A prospect in Germany sees \$49/month and has to do mental math to understand what that means in euros. Then they wonder about the conversion fee their bank will charge. Then they question whether a product that can't be priced in their currency is really built for their market. Each friction point reduces the likelihood they'll complete the purchase. This isn't about convenience; it's about credibility. When you display prices in a customer's local currency with round, intentional numbers, you signal that you've thought about their market. You're not an American company grudgingly accepting foreign credit cards. You're a global business that serves customers where they are. ## Why Conversion Isn't Enough The simplest approach to international pricing is letting your payment processor handle currency conversion. The customer pays in their local currency, and Stripe or your processor converts it to your settlement currency at the current exchange rate plus a conversion fee. This approach works technically, but it fails commercially. The customer sees a price like EUR 45.23, which looks accidental rather than intentional. The amount changes with exchange rate fluctuations, so a customer who checked your pricing yesterday might see a different number today. And the conversion fee, typically 1-2%, either eats into your margin or gets passed to the customer as an additional line item. Intentional local pricing looks different. You set EUR 49/month because that's a clean, marketable price in the European market. The price doesn't fluctuate with exchange rates because it's not derived from your USD pricing; it's set independently for that currency. Customers see the same number every time they visit your pricing page, and the number feels deliberate. The psychological research on pricing supports this approach. Customers respond more positively to round numbers and consistent pricing than to mathematically derived amounts. The conversion fee savings are real but secondary to the trust and professionalism that intentional pricing conveys. ## Setting Prices Across Currencies Multi-currency support means configuring distinct prices for each currency you want to support. This isn't a multiplier applied to your base currency; it's independent pricing for each market. The independence matters because markets have different value perceptions. What customers are willing to pay in the United States, Europe, and Southeast Asia varies by local purchasing power, competitive alternatives, and cultural expectations. A \$99/month price might translate to EUR 99, GBP 79, or JPY 9,900, not because of exchange rates but because those are the right prices for those markets. When setting international prices, consider purchasing power parity as a starting point but not a rigid formula. PPP suggests what an equivalent amount of money buys in different economies, but software pricing doesn't follow PPP perfectly. Your costs are mostly fixed regardless of where customers are located, and customers in lower-PPP markets may still pay premium prices for products that deliver premium value. Research your competitive landscape in each market. What do similar products charge in euros? In pounds? In yen? Price relative to local alternatives, not just relative to your own USD pricing. ## Automatic Currency Detection Configuring prices in multiple currencies only helps if customers see the right one. Salable's checkout flow detects the customer's likely currency based on their browser locale and IP geolocation, presenting prices in that currency by default. The detection isn't deterministic. A customer in France using an English-locale browser might be an expat who prefers EUR or an American traveller who prefers USD. The checkout should show the detected currency but let customers change it if needed. A simple currency selector, often displayed near the price, lets customers choose their preferred option without hunting through settings. For products where the customer's location has compliance implications, you might require certain currencies for certain jurisdictions. EU customers paying for services with VAT implications might need to transact in EUR regardless of their preference. Your checkout flow should handle these edge cases without breaking the general experience. ## Managing Exchange Rate Risk When you set independent prices in multiple currencies, you take on exchange rate risk. The EUR 49 you charge today might be worth more or less in USD terms next month, depending on how currencies move. For small volumes, this risk is negligible. For significant international revenue, it's worth understanding. Exchange rate fluctuations average out over time for most businesses. EUR strengthens against USD one quarter; it weakens the next. If your international revenue is diversified across multiple currencies, the fluctuations partially cancel each other out. If you have substantial revenue concentration in a volatile currency, consider periodic price adjustments. You don't need to track exchange rates daily, but an annual review of international pricing against your base currency ensures you're not drifting too far from your intended margins. Some businesses prefer to keep international prices loosely tied to a base currency, adjusting them when exchange rates move beyond a threshold. If EUR moves more than 10% against USD, you might update your EUR pricing accordingly. This hybrid approach captures most of the benefits of local pricing while limiting exchange rate exposure. ## Tax and Compliance Considerations International sales introduce tax complexity. Different jurisdictions have different rules for digital services tax, VAT, GST, and sales tax. Multi-currency support is necessary for serving these markets but doesn't automatically solve tax compliance. The good news is that modern billing infrastructure handles much of this complexity. Salable integrates with Stripe's tax calculation, which determines the appropriate tax rate based on the customer's location and the nature of the service. The customer sees a price including tax where required, and the correct amount is collected and reported. What you need to ensure is that your prices are set appropriately for tax-inclusive or tax-exclusive presentation. In the EU, B2C prices are typically VAT-inclusive, so your EUR 49 price should already include the applicable VAT. In the US, prices typically display without sales tax, which is added at checkout. Configure your prices for each currency with the local presentation convention in mind. ## Rolling Out Multi-Currency You don't need to support every currency from day one. Start with the currencies that serve your existing and near-term target markets. For most SaaS businesses, a pragmatic starting set includes USD for the Americas, EUR for the Eurozone, GBP for the UK, and potentially AUD for Australia and CAD for Canada. These five currencies cover the majority of English-speaking B2B SaaS purchases. Expand from there based on where you see demand. When adding a new currency, audit your entire pricing surface. Every plan, every line item, every add-on needs a price in the new currency. Partial coverage creates confusing experiences where some prices appear localised, and others fall back to your default currency. Test the checkout flow thoroughly for each currency. Verify that the correct price displays, the tax calculation works, and the payment processes without issues. Currency-related bugs often appear only with specific currency/country/tax combinations, so test realistic scenarios rather than just the happy path. ## Communicating International Availability Multi-currency support signals international readiness, so communicate it explicitly. Your pricing page should show that you serve customers globally, not just display a currency selector that visitors might miss. Consider showing prices in the visitor's detected currency by default while making it easy to see other options. Prices shown in EUR. Also available in USD, GBP, and more, this tells European visitors that you've considered their market while indicating broader international support. For enterprise sales, multi-currency support simplifies procurement. Global companies often prefer paying in their headquarters' currency for accounting consistency. Being able to invoice in USD, EUR, or GBP, based on customer preference, removes a friction point in procurement. ## Beyond the Transaction Multi-currency affects more than checkout. Your customer portal should display subscription details in the currency the customer is paying. Invoices should be denominated in the transaction currency. Usage reports and billing histories should be consistent with what the customer actually pays. This consistency extends to your internal reporting. You'll want to track revenue in both local currencies and a normalised base currency for comparison. Understanding that EUR revenue grew 20% while USD revenue grew 15% requires seeing both the local currency figures and their base currency equivalents. Salable's reporting provides both views. You can see revenue in transaction currencies to understand each market's performance, and you can see normalised revenue for overall business health. The conversion happens at consistent rates for reporting purposes, separate from the actual transaction values. ## The Broader Opportunity Multi-currency support isn't just about removing friction for existing international interest. It's about expanding your addressable market. Customers who wouldn't consider a product priced only in a foreign currency might happily purchase when they see local pricing. The SaaS market is global, and geographic constraints are largely artificial. A project management tool is just as useful in Berlin as in Boston. A developer platform serves engineers everywhere. Multi-currency support lets you capture that global opportunity without building separate products for each market. Start with the currencies your current customers need. Expand to the markets you want to grow into. Set intentional prices that reflect local value, not just mathematical conversions. And let the checkout flow handle detection and selection so customers see the right price without effort. Your next customer might be anywhere in the world. Make sure your pricing page is ready to greet them in their language, which, when it comes to money, means their currency. --- ### Choose the Wrong Pricing Model, Pay for It Later Source: https://salable.app/blog/saas-startup-guides/choosing-pricing-model # Choose the Wrong Pricing Model, Pay for It Later Pricing advice is everywhere, but most of it skips the fundamental question: how do your customers measure the value they get from your product? If they value predictability, flat-rate wins. If value scales with team size, per-seat makes sense. If usage varies wildly between customers, metering aligns your revenue with their outcomes. The model you choose shapes your revenue trajectory, your sales motion, and your engineering requirements. Understanding the tradeoffs now prevents painful migrations later. ## The Value Metric Question Before comparing pricing models, you need to answer one question: what makes your product more valuable to one customer than another? The answer isn't always obvious, and getting it wrong leads to pricing that frustrates customers and leaves money on the table. Consider a document collaboration tool. One customer might be a solo consultant who creates a few documents per month. Another might be a fifty-person team generating hundreds of documents daily. The solo consultant and the team both use the same software, but they derive vastly different value from it. Your pricing model should reflect this difference. The value metric is the unit that captures this variation. For collaboration software, it might be team members. For an API, it might be requests. For analytics software, it might be events tracked. The right pricing model follows naturally once you've identified the right value metric. Some products have obvious value metrics. A CRM that stores customer records creates more value as the sales team grows, making per-seat pricing intuitive. An email sending service creates more value as volume increases, making usage-based pricing logical. Sometimes the right choice is simply flat-rate—customers value predictability, and there's power in keeping it simple. Other products have less obvious value metrics, or multiple competing ones. A project management tool could price by users, by projects, by storage, or simply by feature access. What matters is what customers are willing to pay for your service. ## Flat-Rate Pricing: Simplicity as a Feature Flat-rate pricing charges every customer the same amount, regardless of how much they use the product or how many people access it. This model works when usage doesn't correlate meaningfully with value, or when customers prioritise predictability above all else. The appeal starts with simplicity. Customers know exactly what they'll pay, making purchase decisions easier. There's no mental math calculating whether adding a team member is worth the incremental cost, no anxiety about usage spikes triggering unexpected charges. The price is the price. This simplicity extends to your implementation and operations. You don't need to track usage, allocate seats, or calculate prorated charges for plan changes. Invoicing is straightforward. Revenue is predictable. Customer support doesn't field questions about billing details. Flat-rate pricing works particularly well for products that serve as tools rather than platforms. A grammar checking extension, a backup service, or a personal productivity app often fits this model. The product either works or it doesn't; value doesn't scale meaningfully with usage. The downside is that flat-rate pricing compresses your revenue range. Your most valuable customers pay the same as casual users, leaving money on the table. Conversely, users who barely touch the product might feel overcharged, increasing churn among your least engaged segment. The revenue ceiling is also lower. Growing revenue requires acquiring new customers rather than expanding existing accounts. When a customer's needs grow, you don't benefit unless they need a fundamentally different product that justifies a higher tier. Flat-rate pricing suits products where simplicity is a competitive advantage, where the customer base is relatively homogeneous in their usage patterns, and where the sales motion is self-serve rather than sales-assisted. ## Per-Seat Pricing: Revenue That Scales with Teams Per-seat pricing charges based on the number of users who access the product. This model works when your product's value genuinely multiplies with team size, and when the team-based nature of the product makes seat counts a natural unit. The appeal is alignment between your revenue and customer growth. When a company hires employees who need your tool, your revenue grows automatically. Account expansion happens organically, without sales effort. Per-seat pricing also creates natural upgrade pressure. When a company is close to their seat limit, adding one more person triggers a billing change. This creates regular decision points where customers consider their plan level, often leading to discussions about features or tiers they might upgrade to. The implementation complexity is moderate. You need to track which users have active seats and enforce limits. You need to handle the administrative experience of adding and removing seats. You need to decide whether customers pay for a fixed seat commitment or only for the seats they're actively using. These aren't trivial concerns, but they're well-understood problems with established solutions. Per-seat pricing works well for collaboration and communication tools, where more users literally means more value. Think team chat applications, design collaboration platforms, or shared workspace tools. The seat count directly reflects how many people benefit from the product. The downside is that per-seat pricing can discourage adoption. Customers might limit seat allocation to reduce costs, leaving potential users without access. Shadow IT emerges as teams share credentials to avoid charges. Your product might deliver more value with broader adoption, but the pricing model discourages exactly that. Per-seat pricing also doesn't capture value differences between users. A power user who lives in your tool pays the same as someone who logs in once a month. An executive seat might cost the same as an intern's seat, despite vastly different value delivered. Some products address this by creating user tiers with different pricing. Viewer seats might cost less than editor seats; admin seats might include premium features. This refinement captures value differences but adds complexity to your pricing page and purchasing flow. Per-seat pricing suits products where collaboration is central, where more users genuinely means more value, and where seat counts align with how customers think about their investment. ## Usage-Based Pricing: Aligning Cost with Consumption Usage-based pricing charges customers based on how much they consume, whether that's API calls, data processed, messages sent, or any other measurable unit. This model works when usage varies dramatically between customers and correlates with the value they receive. The appeal is fairness and flexibility. Customers who use more pay more; customers who use less pay less. This eliminates the objection that a product costs too much for limited use, because limited use costs proportionally less. New customers can start small and expand as they succeed, lowering the barrier to adoption. Usage-based pricing also creates natural revenue expansion. As customers grow their usage, your revenue grows without sales intervention. A customer who processes ten thousand events might grow to process a million events, and your revenue scales accordingly. The growth isn't capped by a seat count or tier limit; it's limited only by customer success. From a positioning perspective, usage-based pricing signals confidence in your product's value. You're saying that the more customers use your product, the more value they receive, and you're willing to stake your revenue on that relationship. This resonates with customers who want pricing tied to outcomes rather than arbitrary limits. The implementation complexity is significant. You need to measure usage accurately and in real-time, expose that data so customers can monitor their consumption, handle billing cycles and usage aggregation, and communicate pricing clearly despite the inherent unpredictability. Usage-based pricing works well for infrastructure products, APIs, and platforms where consumption varies by orders of magnitude between customers. The customer processing a hundred API calls per day has fundamentally different economics than the customer processing a million. Flat-rate pricing either excludes the small customer or undercharges the large one. The downside is unpredictability, both for you and your customers. Customers may hesitate to adopt a product when they can't predict monthly costs. Finance teams struggle to budget for variable expenses. That uncertainty creates friction in the purchasing process. Customers may also constrain their usage to control costs, even when using more would benefit them. A developer might cache aggressively or batch requests to reduce API charges, potentially degrading their user experience to save money. The pricing model can work against adoption in ways that hurt both parties. Usage-based pricing suits products where consumption genuinely varies by orders of magnitude, where usage correlates strongly with customer value, and where customers accept variable costs as a tradeoff for flexibility. ## Hybrid Models: Combining Approaches Most mature SaaS products eventually adopt hybrid models that combine elements of multiple approaches. Understanding the pure models helps you design hybrids that capture their benefits while mitigating their drawbacks. The most common hybrid is a base fee plus usage. Customers pay a flat monthly charge that includes some baseline access, then pay additional fees for consumption beyond that baseline. This structure provides revenue predictability for you and cost predictability for customers, while still allowing revenue to scale with intensive usage. Another hybrid is per-seat pricing with usage limits. Each seat includes a certain allocation of resources, and exceeding those allocations triggers additional charges. This preserves the simplicity of per-seat billing while capturing additional value from power users. Tiered usage pricing is another variant. Rather than charging linearly per unit, you offer usage brackets with different per-unit costs. A customer might pay \$0.10 per request up to ten thousand requests, then \$0.05 per request beyond that. This creates volume incentives while maintaining usage alignment. The risk with hybrid models is complexity. Every additional variable makes the purchasing decision harder. Customers struggle to predict costs, finance teams struggle to budget, and your sales team struggles to explain the model clearly. Simplicity has real value; don't sacrifice it without good reason. ## Making the Decision The right pricing model for your product depends on answers to several questions. How do customers perceive value from your product? Does value scale with users, with usage, or remain roughly constant? What does your competitive landscape look like? How do customers expect to buy products in your category? If you're genuinely unsure, start with flat-rate pricing. It's the simplest to implement, the easiest to explain, and the fastest to launch. You can add complexity later; removing it is much harder. If your product is inherently collaborative and team-based, per-seat pricing probably makes sense. Customers already think about the product in terms of team members, so pricing by seat feels natural. If your product serves customers of all sizes, and usage varies dramatically between them, usage-based pricing aligns your revenue with customer success. Just be prepared for the implementation complexity and customer education requirements. Whatever you choose, remember that your initial pricing model isn't permanent. Most successful SaaS companies have changed their pricing multiple times as they learned more about their customers and markets. The goal isn't perfection on day one; it's to get something working well enough to generate revenue and learning. Choose the model that matches your value metric today, implement it as simply as possible, and plan to iterate based on what customers teach you. --- _Not sure which model fits your product? Salable supports flat-rate, per-seat, and usage-based pricing out of the box, so you can experiment without re-architecting. Explore the [pricing models documentation](https://salable.app/docs/products-and-pricing) to see how each approach works in practice._ --- ### Your Billing System Is Sabotaging Growth Source: https://salable.app/blog/features/introducing-tiered-pricing # Your Billing System Is Sabotaging Growth Every growing SaaS hits the same wall: your biggest customers want volume discounts, but your billing system only supports one price per unit. You end up with spreadsheets tracking custom deals, manual invoice adjustments, and a pricing page that lies to your best customers. Tiered pricing solves this by encoding volume discounts directly into your pricing model. Buy more, pay less per unit, automatically calculated and invoiced without human intervention. The request usually comes from a sales call. A prospect loves your product and wants to roll it out across their organisation, but they balk at paying the same per-user rate for 500 seats that they'd pay for five. You want to accommodate them because larger deals mean better unit economics for you too. But your billing system doesn't support volume discounts, so you create a custom plan, track it in a spreadsheet, and hope you remember to honour the special pricing when the invoice goes out. This approach doesn't scale. By the time you have a dozen custom deals, you're spending hours each month reconciling billing against your spreadsheet of promises. Worse, you can't publish volume discounts on your pricing page because your system can't calculate them automatically. Potential customers who would self-serve at higher volumes never see that option. Tiered pricing automates what you're already doing manually. You define price brackets based on quantity, and the billing system calculates the correct charge for any order size. A customer buying 10 units pays one rate; a customer buying 100 units pays a lower rate per unit; a customer buying 1,000 units pays lower still. No spreadsheets, no manual adjustments, no special deals that slip through the cracks. ## How Tiered Pricing Works The fundamental concept is straightforward: you divide quantities into brackets, and each bracket has its own unit price. When a customer's usage or purchase quantity falls into a bracket, that bracket's pricing applies. The complexity lies in _how_ the pricing applies, which brings us to the critical distinction between graduated and volume tiers. Understanding this distinction matters because choosing the wrong model can either leave money on the table or create pricing cliffs that confuse customers. The names sound similar, but they produce dramatically different invoices. With **graduated tiers**, each bracket charges its own rate for the units within that bracket. Think of it like income tax brackets in many countries. If your first tier covers units one through 50 at $10 each, and your second tier covers units 51 through 100 at $8 each, a customer buying 75 units pays $10 for the first 50 units plus $8 for the next 25 units, totalling $700. With **volume tiers**, the qualifying bracket applies to _all_ units. Using the same brackets, a customer buying 75 units falls into the second tier, so they pay $8 for all 75 units, totalling $600. The per-unit rate they qualify for applies universally rather than bracket by bracket. The mathematical difference is significant. In the graduated example, the customer pays $700. In the volume example, they pay $600. That's a 14% difference from the same tier structure, just applied differently. ## Choosing Between Graduated and Volume Tiers Graduated tiers suit most SaaS pricing scenarios because they create smooth cost curves. As customers grow, their costs increase proportionally with a gentle downward slope in per-unit pricing. There are no sudden jumps or counterintuitive moments where buying more actually costs less. Volume tiers create discount cliffs, which can be strategically useful but require careful design. Consider what happens at tier boundaries with volume pricing. If units one through 100 cost $10 each and units 101 through 200 cost $8 each, a customer buying 100 units pays $1,000, but a customer buying 101 units pays $808. Buying one more unit saves them $192. This cliff effect can drive behaviour you want, like pushing customers to commit to higher volumes, but it can also create support headaches when customers game the system or feel tricked. The general guidance is to default to graduated tiers unless you have a specific reason to create discount cliffs. Graduated tiers are easier to explain, less prone to edge-case confusion, and still reward volume purchases without the counterintuitive pricing moments. ## Designing Your Tier Structure The number of tiers and where you set the boundaries depends on your customer distribution and business goals. Too few tiers and you're not rewarding volume growth. Too many tiers and your pricing page becomes unreadable. Most successful tiered pricing implementations use three to five brackets. The first tier covers your typical individual or small team customer. The middle tiers capture growing businesses. The top tier, often labelled "Enterprise" or with custom pricing, handles your largest accounts. Setting boundaries requires looking at your actual customer data. Where do customers naturally cluster? If most customers have between one and ten users, but you have a meaningful segment with 50 to 200 users, and occasional accounts with 500 or more, your tier boundaries might fall at 10, 50, 200, and 500+ units. The discount progression matters too. A 5% discount per tier feels minimal; customers might not notice it. A 50% discount per tier might be unsustainable for your margins. Most tiered pricing lands somewhere between 10% and 25% discount per tier, with larger jumps at higher volumes where your marginal costs are genuinely lower. ## Implementation Without the Headaches The promise of tiered pricing is automation. You configure your tiers once, and every invoice calculates correctly regardless of customer size. But achieving this automation requires a billing system that supports tiered pricing natively. In Salable, you configure tiered pricing directly on your line items. You define each tier with its quantity range and unit price, choose between graduated and volume calculation, and the system handles everything else. When a customer's subscription renews or their metered usage gets invoiced, the tier calculation happens automatically. If they grow from 75 seats to 150 seats mid-cycle, the invoice prorates correctly across the applicable tiers. This native support matters because retrofitting tiered pricing onto a system that doesn't support it creates fragile workarounds. You end up with webhook handlers that intercept invoices and recalculate totals, or duplicate products that represent different volume levels. These workarounds break when Stripe changes their API or when you need to handle edge cases like mid-cycle upgrades. ## Communicating Tiered Pricing to Customers Automated calculation only helps if customers understand what they're being charged. Tiered pricing can confuse buyers who expect a single unit price, so your pricing page and checkout flow need to explain the model clearly. The most effective approach shows both the per-unit price at each tier and a calculated example at common quantities. A table showing your tier brackets gives customers the raw information, while calculated examples like "50 users: $400/month" and "200 users: $1,200/month" make the savings tangible. For self-serve checkout, showing the calculated total based on the quantity they've entered removes uncertainty. The customer selects 75 seats, and the checkout shows them exactly what they'll pay, broken down by tier if you're using graduated pricing. No surprises on the invoice. For sales-assisted deals, tiered pricing gives your sales team a framework for volume discounts that doesn't require manager approval for every deal. The customer qualifies for the published tier, period. This consistency builds trust and speeds up the sales cycle. ## When Tiered Pricing Isn't the Answer Tiered pricing works best when you're selling countable units at predictable volumes. Per-seat licensing, API calls, storage gigabytes, and similar metrics fit the model naturally. But not every pricing situation calls for tiers. If your value delivery is genuinely flat regardless of usage, tiers add complexity without benefit. A product that costs the same to serve whether the customer has one user or a hundred might be better priced as a flat monthly fee with an upper limit. If customer value varies dramatically by use case rather than volume, you might need separate products rather than volume tiers. A customer processing financial transactions and a customer tracking inventory might both use your API, but the value you deliver differs enough that volume alone doesn't capture it. And if your largest customers need genuinely custom arrangements involving SLAs, dedicated infrastructure, and bespoke integrations, those belong in enterprise sales conversations rather than self-serve tiered pricing. Tiers work for automated volume discounts; they don't replace relationship-driven enterprise deals. ## Moving Forward with Tiered Pricing The path from manual volume discounts to automated tiered pricing starts with understanding your current customer distribution. Look at how many customers you have at each volume level, what discounts you're already offering informally, and where the natural breakpoints fall. Design your tier structure around that data, defaulting to graduated tiers unless you have a specific reason to create volume discount cliffs. Keep the number of tiers manageable, typically three to five, with meaningful discounts that reward growth without destroying your margins. Implement the tiers in a billing system that supports them natively rather than bolting on workarounds. Test the calculation at boundary conditions, especially at tier edges and with mid-cycle changes. Finally, communicate the pricing clearly to customers. Show the tier structure, provide calculated examples, and display real-time totals at checkout. Tiered pricing should feel like a reward for volume, not a puzzle to solve. The spreadsheets tracking your custom deals can finally go away. Your pricing page can tell the truth to every customer, including your biggest ones. And your billing system can handle the math that you've been doing manually, automatically and correctly, for every invoice going forward. --- ### Build Any Pricing Model from Composable Parts Source: https://salable.app/blog/features/line-items-composable-pricing # Build Any Pricing Model from Composable Parts Most billing systems force you to choose: flat monthly fee or usage-based pricing. Per-seat or metered. Base charge or add-ons. But real products rarely fit these neat categories. Your project management tool might have a monthly platform fee, charge per active user, and bill for storage overages, all in the same subscription. Line items make this natural. Instead of contorting your product to fit your billing system, you compose charges that reflect your actual value delivery. The limitations of single-model pricing become obvious the moment your product evolves. You launched with a simple per-user fee, but now enterprise customers want a predictable base cost. You added API access, but some customers hammer your endpoints while others barely touch them. Your professional services team offers implementation packages, and billing those separately fragments the customer relationship. Every pricing decision becomes a compromise between what makes business sense and what your billing system can actually do. Line items dissolve this tension. Instead of picking one pricing model and shoehorning everything into it, you build plans from composable parts. Each part handles one dimension of value: a flat rate for platform access, per-seat charges for team growth, metered billing for variable usage, one-off fees for setup or implementation. Combined, they create pricing that mirrors how customers actually use and value your product. ## The Four Building Blocks Salable provides four line item types, each designed for a specific kind of charge. Understanding when to use each one lets you construct pricing models that feel intuitive to customers while capturing value accurately. **Flat-rate line items** charge a fixed amount per billing period regardless of usage. They're the foundation of predictable revenue and work well for platform access, minimum commitments, or bundled features. A \$99/month base fee is a flat-rate line item. So is a \$200/month commitment that includes a certain usage threshold before metered charges kick in. The stability of flat-rate charges benefits both sides of the transaction. Customers know exactly what they'll pay each month, which simplifies budgeting and reduces billing surprise. You get predictable recurring revenue that doesn't fluctuate with usage patterns. For products where the core value doesn't scale linearly with usage, flat-rate is often the right foundation. **Per-seat line items** charge based on the number of users, seats, or licenses. The quantity can be fixed at purchase time or dynamic based on active usage. Per-seat pricing aligns revenue with team growth, which makes it popular for collaboration tools, productivity software, and anything where value multiplies as more people use it. The key decision with per-seat pricing is whether seats are committed or active. Committed seats mean the customer pays for a fixed number whether they use them all or not, providing revenue predictability at the cost of potential customer friction when seats go unused. Active seats mean the customer pays only for seats in use during the billing period, which feels fairer to customers but creates revenue variability and potential gaming. **Metered line items** charge based on measured consumption, calculated and invoiced at the end of each billing period. API calls, data transfer, storage, compute time, and transactions are classic metered charges. Metered billing captures value from high-usage customers who would otherwise be subsidised by low-usage customers on flat plans. Metered pricing requires infrastructure to track usage accurately and report it before invoicing. The billing system needs to know that Customer A made 47,293 API calls last month while Customer B made 2,341. This tracking complexity is the cost of usage alignment, but for products with variable consumption patterns, it's often worth it. **One-off line items** charge a single amount that doesn't recur. Setup fees, implementation packages, training sessions, and initial configuration belong here. They're purchased once, invoiced once, and don't appear on future bills. One-off charges let you monetise work that happens outside the normal subscription relationship without creating separate invoicing streams. The customer sees one bill that includes their subscription and any one-time charges, keeping the relationship consolidated even when the charges have different characteristics. ## Combining Line Items into Plans The real power of line items emerges when you combine them. A single plan can include multiple line items of different types, creating pricing models that would be impossible with single-model systems. Consider a hypothetical analytics platform. The core platform provides dashboards and basic reporting, worth a predictable monthly fee. Team members need access, and each additional analyst increases the value the company extracts. Heavy users query the data warehouse extensively, and those queries cost real infrastructure. New customers need onboarding to get value quickly. With line items, this translates naturally. The plan includes a \$199/month flat-rate for platform access, \$29/month per analyst seat, \$0.02 per data warehouse query, and a one-time \$500 onboarding fee. A small team with three analysts running moderate queries might pay \$286/month after the initial setup. An enterprise with 50 analysts running millions of queries pays proportionally more, reflecting the greater value they extract. This composition works because each line item handles its own dimension independently. The flat-rate component is unaffected by seat count. The per-seat charge is unaffected by query volume. Metered billing is unaffected by team size. Each component does one thing well, and combining them creates sophisticated pricing without sophisticated configuration. ## Pricing Model Patterns Certain combinations of line items appear repeatedly across successful SaaS businesses. Recognising these patterns helps you design pricing that fits your product's value delivery. **Base plus usage** combines a flat-rate foundation with metered charges for consumption above a threshold. The base fee provides revenue predictability and includes some level of usage, while metered charges capture value from heavy users. This pattern works well when customers have widely varying usage but everyone needs a minimum level of service. **Platform plus seats** pairs flat-rate platform access with per-seat growth. The platform fee covers infrastructure and capabilities that don't scale with users, while per-seat charges align revenue with the expanding value as more team members adopt the product. Collaboration and productivity tools often follow this pattern. **Committed seats with overage** sets a minimum seat commitment with metered charges for seats beyond the commitment. The customer commits to paying for 10 seats monthly but can burst to 15 during busy periods with overage charges. This balances revenue predictability with customer flexibility. **Tiered base with flat add-ons** offers multiple plan levels with different flat-rate bases, then adds capabilities through additional flat-rate line items. The customer picks their tier (Starter, Professional, Enterprise) and optionally adds modules (Analytics, API Access, White Label). Each piece has simple, predictable pricing while the combination creates significant variety. **Usage-only with minimum** charges purely on consumption but enforces a minimum monthly spend. If actual usage falls below the minimum, the customer pays the minimum. If usage exceeds it, they pay actual usage. This protects your revenue floor while rewarding high-usage customers with pure consumption pricing. ## Designing for Customer Psychology Pricing isn't just mathematics; it's communication. How you structure line items affects how customers perceive value and make purchasing decisions. A few principles from pricing psychology apply directly to line item design. **Anchor high, discount down.** When combining line items, customers perceive value based on the total price before any bundled discounts. Showing the individual line item prices and then applying a bundle discount makes the value feel greater than showing the bundled price alone. **Predictability reduces anxiety.** Customers prefer knowing what they'll pay. If your pricing includes metered components, consider including a usage threshold in the flat-rate portion so customers have a predictable baseline. The metered charges then feel like an option they control rather than an unpredictable cost. **Simplicity wins at checkout.** While line items let you build complex pricing, the checkout experience should feel simple. Show the total monthly cost prominently, with line item breakdown available for customers who want it. Don't force everyone to parse the composition before buying. **Separate value from cost.** One-off charges like setup fees can create friction if they feel like arbitrary costs. Frame them as value delivery: "Onboarding package includes dedicated setup session, data migration, and team training." The line item is still a one-off charge, but the messaging emphasises what the customer receives. ## Implementation Considerations Building line-item-based plans requires coordination between your billing system, your application, and your customer-facing interfaces. Each component has a role to play. Your billing system needs to understand line item composition natively. Salable handles this by letting you add multiple line items to a single plan, each with its own pricing type, currency, and configuration. The system calculates the combined invoice correctly, handling the interactions between flat, per-seat, metered, and one-off charges. Your application needs to report usage for metered line items and track seat counts for per-seat charges. This means instrumenting the relevant actions (API calls, storage consumption, active users) and reporting them to the billing system before invoice generation. The accuracy of your billing depends on the accuracy of this reporting. Your checkout flow needs to communicate the pricing clearly. For plans with multiple line items, show customers what they're getting and what each component costs. If quantities are configurable at checkout (like seat count), update the total dynamically as they adjust. If metered charges apply, explain how usage will be tracked and billed. Your customer portal needs to show line item breakdown on invoices and subscription details. Customers should be able to see exactly what they're paying for and why. This transparency builds trust and reduces billing-related support requests. ## Evolving Your Pricing Over Time One advantage of line-item-based pricing is adaptability. As your product evolves, you can add new line items without restructuring everything. Launch a new feature? Add it as an optional line item on existing plans or create a new add-on plan. Discover that a flat-rate component should scale with usage? Convert it to a metered line item. This flexibility supports pricing experimentation. You can A/B test different line item combinations to see what resonates with customers. You can offer promotional line items that expire after a trial period. You can create customer-specific line items for enterprise deals without building entirely custom plans. The key is treating line items as modular components that can be mixed, matched, and modified over time. Your initial pricing model doesn't have to be perfect because you have the tools to evolve it as you learn what customers value and what they'll pay for. ## The Shift in Thinking Moving from single-model pricing to line-item composition requires a mental shift. Instead of asking "which pricing model should we use?" you ask "what are the different dimensions of value we deliver, and how should each be priced?" Some dimensions are best served by flat charges: predictable, simple, easy to understand. Others align naturally with seat counts: value that scales with team size. Others depend on consumption: value that varies with usage. And some are one-time: value delivered once at the start of the relationship. Once you see your product through this lens, pricing becomes less about constraints and more about expression. Line items let you say exactly what each component is worth and charge accordingly. The billing system handles the composition, calculation, and invoicing. You focus on delivering value; the pricing follows naturally. The project management tool with platform fees, per-user charges, and storage billing isn't a complicated edge case anymore. It's just three line items doing what each does best, combined into a plan that reflects reality. And when reality changes, your plan can change with it. --- ### Introducing Salable Beta: Hybrid Pricing for SaaS Source: https://salable.app/blog/announcement/salable-beta-announcement # Introducing Salable Beta: Hybrid Pricing for SaaS Most billing platforms force you to choose between flat-rate and usage-based billing, and between per-seat and metered billing. Pick one. If your product doesn't fit neatly into a single pricing model, you're left bolting together multiple subscriptions, building custom billing logic, or compromising your charging model. Salable Beta removes that constraint entirely. Today, we're launching a fundamentally different approach to subscription billing—one that lets you combine any charge types you need into a single plan and handle team subscriptions without writing custom code. ## The Problem with "Pick One" Billing The pricing model that made sense at launch rarely survives contact with real customers. You started with flat-rate monthly subscriptions because they were simple. Then, enterprise customers wanted annual contracts. Then you added a feature that only makes sense to charge by usage. Then, teams needed seat management but also wanted a platform fee. Traditional billing systems handle each of these individually. They don't handle them together. So you end up managing multiple subscriptions per customer, reconciling charges over different billing cycles, and explaining to confused customers why their invoice has three line items from three different "products" that are really just one product priced three different ways. The complexity compounds in your codebase. Custom logic to enforce seat limits. Webhook handlers to synchronise subscription states. Edge cases around what happens when someone upgrades one subscription but not the others. Each workaround makes the next change harder. We built Salable Beta because we kept seeing the same pattern: teams hacking together billing workarounds every time their pricing didn't fit the platform's assumptions, rather than working on features for their core product. ## A Different Architecture Salable Beta introduces Line Items—composable pricing components that combine within a single plan. Instead of picking one pricing model, you add the charges that reflect how you actually deliver value. A plan might include a \$99/month platform fee (flat-rate), \$15 per team member (per-seat), \$0.01 per API call (metered), and a \$500 one-time setup charge (one-off). One plan, one subscription, one invoice. Each Line Item appears separately so customers understand exactly what they're paying for. Each Line Item type does what you'd expect. Flat-rate charges a fixed amount per billing cycle. Per-seat multiplies by team size. Metered tracks consumption and bills at period end. One-off charges are once at the start and never again. Combine them however your pricing demands. For products with volume discounts, tiered pricing applies graduated or volume-based rates to any Line Item. The first hundred API calls cost one rate, the next thousand cost less, and enterprise volumes cost less still. The calculation happens automatically—no spreadsheets tracking custom deals, no manual invoice adjustments. Beyond combining charge types within a plan, subscriptions can contain multiple plans altogether. This enables add-on and plugin pricing systems where customers purchase a core product plus optional extras—an analytics module, API access, premium support—all managed as a single subscription with unified billing. Customers build their own bundle from your catalogue; you don't need to anticipate every combination with pre-built packages. ## Per-Seat Billing Made Easy Per-seat billing sounds simple until you implement it. Who pays versus who uses isn't the same question. A company admin buys fifty seats; individual team members need access. Managing that relationship typically requires custom membership tables, invitation flows, and seat enforcement logic scattered across your application. Salable Beta's Grantee Groups model this explicitly. The owner holds the billing relationship. Grantees receive access. Groups manage membership. When you check whether someone can access a feature, you're asking a simple question: Does this grantee have this entitlement? Salable handles the rest. Seat limits are enforced by the group. The subscription allows fifty seats; the group can have at most fifty grantees. No custom enforcement logic required. ## What We're Looking For This is a beta. The architecture is solid—we've been running it internally and with early partners. But we want to see it in the hands of real developers solving real pricing problems. We're looking for developers building SaaS products who've felt the limitations of their billing platforms. You've wanted to charge a base fee plus usage, or combine seat-based licensing with metered features, or offer tiered volume discounts without building custom invoicing. If that sounds familiar, we want you to try Salable Beta and tell us what works and what doesn't. The feedback loop matters. We're actively developing based on what beta users encounter. Issues you report today shape the features we build tomorrow. ## Getting Started The beta is open now. Sign up at [salable.app](https://salable.app), connect your Stripe account (test mode works fine for experimentation), and start building. The documentation walks through core concepts, pricing configuration, and integration patterns. If you're migrating from another billing system—or from Salable's previous version—we're here to help. The architecture is different enough that a fresh look at your pricing model is worthwhile. What compromises did you make because your billing system couldn't handle what you actually wanted to charge? Salable Beta might let you undo those compromises. We're building the billing infrastructure we wished existed when we were building SaaS products ourselves. Today, you can try it. We're keen to see what you build with it. --- **Further Reading** - [Salable Quick Start Guide](https://salable.app/docs/quick-start) - [Core Concepts Guide](https://salable.app/docs/core-concepts) - [Products & Pricing Guide](https://salable.app/docs/products-and-pricing) --- ### One Price. Big Problem. Source: https://salable.app/blog/insights/the-hidden-cost-of-simple-pricing # One Price. Big Problem. "Keep it simple" is sound pricing advice, especially when you're starting out. The single $9/month plan has real appeal: no tiers to agonise over, no usage tracking to implement, no decisions for customers to make. It gets you to revenue fast, and there's genuine wisdom in that. But simple pricing that works at launch rarely stays optimal as you grow. A flat rate that felt fair to your first hundred customers starts leaving money on the table once you're serving enterprises alongside hobbyists. You're either overcharging users who would pay less, or undercharging users who would happily pay more. The question isn't whether to start simple—you probably should. It's knowing when simple stops serving you, and what the minimum complexity looks like that actually captures the value you create. This distinction matters more than most founders realise. Pricing isn't just a number you slap on your product; it's a signal about who you're for, what you value, and how you think about your relationship with customers. Get it wrong, and you'll spend years wondering why growth feels harder than it should. ## The Seduction of the Single Price Every founder who launches with a single price point has good reasons. Multiple tiers mean multiple decisions: which features go where, how to name each tier, where to set price points. A single price sidesteps all of that complexity. Ship faster, iterate later. There's also the fear of overwhelming customers. The conventional wisdom holds that too many choices paralyze buyers—Iyengar and Lepper's famous [jam study](https://psycnet.apa.org/record/2000-16701-012) showed that shoppers bought more jam when offered six varieties than when offered twenty-four. Better to present one clear option and let the product speak for itself. These arguments feel compelling, but they rest on a flawed premise: that all your customers are essentially the same. They're not. The solo developer evaluating your tool for a weekend project values it differently than the enterprise team planning to deploy it across hundreds of engineers. The small agency using your product for one client has different needs than the consultancy building their entire practice around it. A single price forces these wildly different customers into the same box. The solo developer looks at your $99/month price and thinks "I'd pay $20 for this, but not $99." The enterprise team looks at the same price and thinks "This seems suspiciously cheap—is it really enterprise-ready?" You've priced yourself out of both conversations. ## The Math of Misaligned Pricing Let's make this concrete. Imagine your market has 1,000 potential customers, and you've chosen a single price of $49/month. The upper limit on your monthly revenue is $49,000. Seems fine. But now imagine those customers actually fall into three natural segments based on how much value they derive from your product. Three hundred are hobbyists who would pay up to $19. Four hundred are professionals who would happily pay $49—you priced this segment perfectly. Three hundred are teams who would pay $149 because your product drives their daily workflow. With a single $49 price, those 300 hobbyists never convert. They wanted your product, but not at that price. That's 300 customers you never see, but the cost is invisible—you don't know they exist. Meanwhile, the 300 teams are getting a steal. They'd pay three times more, but you never asked. Run the alternative math. With three tiers priced at $19, $49, and $149, you capture all three segments. That's $5,700 from hobbyists, $19,600 from professionals, and $44,700 from teams—a total of $70,000. The "simple" single price left $35,700 per month on the table. Over a year, that's $428,400 in revenue you never captured, not through bad execution, but through a pricing structure that couldn't accommodate the value you were actually creating. This is a simplified example, but it demonstrates the logic you'll need to grapple with: a single price point can only optimise for one customer segment, leaving value uncaptured at both ends—customers who would pay more and customers who won't convert at all. ## Simplicity Versus Clarity: The Crucial Distinction The argument for simple pricing conflates two different concepts: simplicity and clarity. A single price is simple. But a well-designed three-tier structure can be clearer—it tells customers more about who the product is for and how to choose. Consider how this works in practice. A solo consultant lands on your pricing page and sees three options: Starter at $19 for individuals, Professional at $49 for small teams, and Enterprise at $149 for organisations. Within seconds, they know exactly which tier is for them. The tier names and descriptions do the work of qualification that a single price cannot. Contrast this with a single $49 price. The consultant wonders: Is this designed for someone like me, or am I paying for features meant for larger teams? Am I getting good value, or am I subsidizing enterprise functionality I'll never use? The simplicity creates confusion about fit, even as it eliminates choice. Clarity comes from alignment between pricing and customer segments, not from reducing options to one. Three tiers that map to distinct use cases are cognitively easier than a single price that raises questions about who it's meant for. ## Finding the Right Level of Sophistication If simplicity isn't the goal, what is? The answer is the minimum pricing complexity that captures the value you create. This varies by product and market, but a framework can help you find the right level. Start by identifying natural customer segments. These aren't arbitrary divisions you impose; they're groups that already exist in your market with meaningfully different needs and willingness to pay. A project management tool might serve solo freelancers, small teams, and large organisations. Each segment uses the product differently and derives different value from it. Next, identify the value metric—the unit that scales with the value customers receive. For some products, this is users or seats. For others, it's usage volume, projects, or storage. The right value metric passes two tests: customers intuitively understand why they should pay more as this metric increases, and it correlates with the value they're actually getting. Finally, build tiers around the intersection of segments and value metrics. Each tier should have a clear target customer and a price that reflects what that customer would reasonably pay. The tiers should be distinct enough that customers can easily self-select, but not so numerous that the choice becomes overwhelming. Three tiers work remarkably well for most SaaS products. It's enough to capture meaningfully different customer segments while remaining easy to understand. Four or five tiers can work if you genuinely serve distinct segments, but beyond that, you're likely overcomplicating without capturing additional value. ## The Cost of Delaying Pricing Evolution Perhaps the most hidden cost of simple pricing is the opportunity cost of learning. Pricing isn't something you set once and forget; it's a lever you should continuously optimise. But a single price gives you almost no data to learn from. With multiple tiers, you can observe which customer segments expand fastest, which have the highest conversion rates, and where buyers naturally cluster. When your Professional tier converts at twice the rate of Enterprise, you know the Enterprise tier needs repositioning. When customers frequently upgrade from Starter to Professional after three months, you can adjust the Starter feature set to accelerate that journey. None of this learning happens with a single price. You know your conversion rate and your churn rate, but you don't know why customers convert or churn, or which segments you're serving well versus poorly. What started as a feature—simplicity—becomes a blindfold. This compounds over time. The company with tiered pricing iterates based on data, gradually optimizing toward the pricing structure that best fits their market. The company with simple pricing operates on intuition, making large, infrequent changes because they lack the feedback loops to make small, continuous improvements. Patrick Campbell, founder of ProfitWell, puts it bluntly: "The company that iterates on pricing fastest wins." But you can't iterate on what you can't measure, and simple pricing measures almost nothing. ## When Simple Pricing Actually Works All this said, there are contexts where a single price genuinely makes sense. Early-stage products without clear customer segments benefit from starting simple. If you don't yet know who your best customers are or how they derive value, tiered pricing would just be guessing. Better to start with one price, learn from early adopters, and add tiers once you understand the market. Commodity products with undifferentiated value can also justify simple pricing. If every customer gets essentially the same value regardless of how they use the product, there's nothing for tiers to capture. But true commodities are rare in SaaS—most products create different value for different users. Bottom-up products that rely on viral adoption sometimes benefit from simple pricing that removes all friction. Slack started with a radically simple model: free for small teams, paid when you needed history and integrations. This wasn't unsophisticated—it was precisely calibrated to their bottom-up growth motion. The simplicity was strategic, not default. The key is intentionality. Simple pricing as a deliberate choice based on your growth model is different from simple pricing as a way to avoid hard decisions about customer segments and value metrics. ## The Path to Pricing Clarity If your current pricing is leaving money on the table, how do you evolve toward something better? The transition matters as much as the destination. Start with customer research, not competitor analysis. Talk to customers in each segment about how they use your product and what outcomes they care about. Ask about value, not price—"What would it cost you if this product disappeared?" reveals more than "How much would you pay?" The goal is understanding how different segments derive different value, so you can differentiate tiers accordingly. Design tiers around outcomes, not features. Instead of defining the Professional tier by "unlimited projects and 10GB storage," define it by who it serves: "For teams shipping multiple projects who need collaboration features." Then work backward to the features that enable those outcomes. This approach produces tiers that customers can self-select into because they recognise themselves in the description. Communicate the change thoughtfully. Existing customers on a single price will wonder how they're affected. Be transparent about what's changing and why, and consider grandfathering existing customers at their current rate. The goal is capturing more value from new customers, not extracting more from existing ones. Finally, commit to ongoing iteration. Your first tiered pricing won't be optimal, and that's fine. The point is building the infrastructure for learning—the tiers, the analytics, the experimentation mindset. The specific prices and features will evolve as you learn. ## Minimum Viable Sophistication The goal isn't pricing simplicity—it's pricing clarity. A three-tier structure that maps to real customer segments is clearer than a single price that fits no one well. It tells customers who the product is for, helps them self-select into the right tier, and gives you data to continuously improve. The hidden cost of simple pricing isn't the complexity you avoided; it's the revenue you never captured, the customers who never converted, and the learning that never happened. These costs are invisible, which makes them easy to ignore—but they compound over time. Find the minimum complexity that captures the value you create. Don't default to simplicity because sophistication feels risky. The real risk is leaving growth on the table. --- ### Your First Subscription Product: Zero to Revenue Source: https://salable.app/blog/saas-startup-guides/your-first-subscription-product # Your First Subscription Product: Zero to Revenue You've built something people want to pay for. Maybe it's a SaaS tool that's been running free while you validated the concept, or perhaps you're starting fresh with a clear monetisation strategy. Either way, you're facing the same question every developer confronts: how do you actually charge people? The billing landscape is littered with engineers who spent months building custom systems and are now stuck maintaining billing code instead of shipping product features. There's a better path, and after reading this you'll be on it. ## The Overthinking Trap Most developers approach billing like they approach feature development: they map out every edge case, design for scale they don't have, and build flexibility they'll never use. That instinct serves them well when architecting application code, but it's counterproductive for billing. Consider what typically happens. A developer sits down to implement subscriptions and immediately starts listing requirements. They need to handle monthly and annual billing. They need to support multiple tiers. They need upgrade and downgrade paths. They need proration logic. They need to handle failed payments gracefully. They need webhooks to keep their database in sync. Before writing a line of code, they've designed a system complex enough to require weeks of implementation. Meanwhile, their product sits there, free, while potential revenue walks out the door. Here's the insight that changes everything: you don't need any of that complexity on day one. You need a single plan, a checkout flow, and a way to know who's paid. Everything else can wait until customers ask for it. ## The Minimum Viable Billing Stack A working subscription product requires exactly two things: a way to take money and a way to block users who haven't given you any. That's it. For taking money, you need one product to sell. "Pro Plan: $29/month" is enough. You don't need a free tier or multiple pricing options on day one. Customers who want to pay will pay; customers who want options will tell you what options they want after you've launched. For blocking non-paying users, you need an entitlement check—code that answers one question: does this user have access to this feature? On day one, this can be as simple as verifying whether someone has an active subscription. It doesn't need to be sophisticated; it needs to exist. Salable gives you both. ## Building the Happy Path First Your first implementation should handle exactly one scenario: a new customer signs up, pays, and gains access to your product. That's the happy path, and it's the only path that generates revenue. Start by creating your product in your billing system. Give it a name that makes sense to customers, set a price that feels right (you can always change it later), and configure a monthly billing interval. Don't agonise over the price. Pick a number, launch, and let the market tell you if you're wrong. Next, set up the checkout flow. A customer clicks "Subscribe," completes checkout, and returns to your application as a paying customer. Finally, implement the entitlement check. When a user tries to access a paid feature, your code should verify they have an active subscription. If they do, let them through. If they don't, show them the paywall. The entire implementation can be completed in an afternoon. Not because you're cutting corners, but because you're deferring complexity until it's necessary. ## What You're Deliberately Ignoring (For Now) This approach works because it's honest about what matters on day one versus what can wait. You're deliberately setting aside several things that feel important but aren't yet. Multiple tiers can wait. Yes, conventional wisdom says you need a Good/Better/Best pricing page. But that wisdom assumes you know which features belong in which tier. You don't. You're guessing. Launch with one tier, watch how customers use your product, and add tiers when you understand the natural value segments. Annual billing can wait. Annual plans improve cash flow and reduce churn, but they also complicate refunds, proration, and plan changes. More importantly, you don't yet know if customers will stick around for a year. Prove monthly retention before optimising for annual commitment. Free trials can wait. Trials are powerful conversion tools, but they're also a form of delayed revenue and a source of complexity. Trial users need nurturing, trial-to-paid conversion needs tracking, and trial abuse needs preventing. Launch with immediate payment and add trials once you understand your conversion funnel. Usage-based pricing can wait. Metered billing aligns your revenue with customer value, but it requires infrastructure: usage tracking, billing calculations, and customer-facing dashboards. Start with flat-rate pricing until you have usage data that justifies the complexity. None of these features are hard to add later. They're just unnecessary now. Every feature you defer is engineering time you can spend on your actual product. ## The Launch Checklist Before you announce your paid plan, verify that the critical path works end-to-end. Create a test account using your payment processor's test mode. Walk through the checkout flow as a customer would. Confirm that completing payment creates the right records in your system. Verify that your entitlement check correctly identifies paid users. Test that paid features actually unlock. This isn't exhaustive testing; it's smoke testing the one flow that matters. If a new customer can sign up and access paid features, you're ready to launch. If something fails, fix it before moving on. You'll also want a way to handle the edge cases that will inevitably arise. What happens if someone emails saying they paid but can't access the product? You need a way to manually check their subscription status and, if necessary, grant access while you investigate. This doesn't need to be a polished admin interface. It just needs to be possible. ## Your First Customers Aren't Your Last Customers The objection to launching simple is always some variation of "but what about professional customers who need enterprise features?" The answer is that professional customers aren't buying your product today. Early adopters are. Early adopters are tolerant of rough edges and missing features because they're buying potential, not polish. They'll tell you what features matter through support tickets and feature requests. They'll teach you what pricing models make sense for your market. They'll surface the edge cases you couldn't have anticipated. Your job on day one is to capture this learning by having something to sell. Every week you spend building features no one asked for is a week of customer feedback you didn't collect. This doesn't mean you should ship broken software or ignore obvious problems. It means your definition of "ready to launch" should be "can I charge someone for this?" rather than "have I anticipated every possible scenario?" ## Growing Beyond Day One Once you have paying customers, the roadmap becomes clearer. Usage data reveals which features drive value, and those insights shape how you structure tiers. Customer feedback points you toward the pricing models that fit your market. Support tickets highlight which edge cases need automation. The pattern is consistent: launch simple, observe, and expand based on evidence. Add a second tier when customers ask for different feature sets. Add annual billing when monthly retention proves strong. Add usage-based components when flat-rate pricing leaves money on the table. This iterative approach isn't just faster for initial launch; it's more likely to produce pricing that works. Startups that launch with complex pricing models based on intuition usually end up simplifying. Startups that launch simple and expand based on evidence usually get it right. ## The Afternoon That Changes Everything Here's what's possible in a couple of hours: define your product in Salable, configure a checkout flow that handles payment collection, implement an entitlement check that gates access to paid features, and test the end-to-end flow to verify everything works. By the end of the afternoon, you'll have something that seemed complicated this morning: a way to charge money for your work. Not a theoretical system design. Not a roadmap for future billing infrastructure. A real product that real people can pay for, today. The revenue might be modest at first. Your first customer might be someone you know. Your second customer might take a week to find. But you'll have crossed the threshold from "building something" to "running a business." And everything that follows—better pricing, more features, larger customers—builds on that foundation. The complexity can come later. Today, just get paid. --- _Building your first subscription product? [Salable's Quick Start Guide](https://salable.app/docs/quick-start) walks you through the complete setup in under an hour, from product creation through your first checkout._ --- ### The Ultimate Guide to Commercial Models Source: https://salable.app/blog/insights/the-ultimate-guide-to-commercial-models # What is a commercial model—and why should you care? You've built something valuable—a tool, a platform, or a service people actually want to use. But how do you turn that value into profit? It's time to build your commercial model. A commercial model defines how your product makes money, how customers pay, and how that money flows into your business. That might be as simple as a flat monthly fee, or more dynamic, with usage-based pricing, tiered plans, or outcome-based contracts. Getting this right isn't just about setting a price; your commercial model should evolve as your customer base grows. Aligning the value your product delivers to your customers is often a moving target, and there are a range of different pricing approaches available to maximise profit while maintaining a great customer experience. In this guide, we'll break down the key components of a commercial model, explain how they fit together, and show how leading products—and flexible platforms like Salable—handle this in the real world. We've analysed how successful products approach this, and what we're sharing here reflects industry best practices. So, it all starts with the building blocks. ![Commercial Model Guide Diagram](/blog/images/commercial-model-guide-diagram.png 'Commercial Model Guide Diagram') ## The building blocks of a commercial model A commercial model is the full go-to-market blueprint, combining a monetisation strategy, pricing structure, revenue mechanics, and billing logistics into one cohesive system. To understand what this looks like in practice, let's break down the commercial model of one of the most talked-about businesses in the world right now: OpenAI. ![Commercial Model Guide Diagram](/blog/images/commercial-model-guide-quote-marks.png 'Commercial Model Guide Diagram') ## Monetisation strategy: your plan to make money To start off with, monetisation defines where revenue will come from. It’s your high-level strategy for turning value into income. OpenAI’s monetisation strategy for its API is usage-led, it doesn't charge for access or setup. Instead, it earns when customers interact with its products and services. OpenAI monetises on activity, and ties its revenue to the number of tokens consumed–not the number of users for instance. OpenAI actually monetises two main services: - ChatGPT: the Chat interface that users can interact with - OpenAI API: the API that powers ChatGPT, and developers can integrate into their application Each of these is monetised independently, creating multiple revenue streams, all anchored to real product usage. ![Commercial Model Guide Diagram](/blog/images/commercial-model-guide-star-icon.png 'Commercial Model Guide Diagram') ## Revenue mechanics: how money actually flows If monetisation is the strategy, the revenue model is the execution. Your revenue model describes the way money is earned—the specific behaviours your customers take that generate income. The OpenAI API's revenue model, when using the API, is built around per-token fees. Large Language Models (LLMs) break down the ‘cost’ of generating text and images into smaller pieces, or "tokens", which a user pays a fee for each. While enterprise customers may negotiate pricing and terms, the basic revenue model is the same: customer actions in the product. >
**Don’t mix them up!**
> Monetisation is the high-level play—it answers where the money comes from: "We make money when people use our API."
> Revenue is the engine underneath—it shows how that money flows in: "They consume tokens, and we bill them monthly."
> One spots the opportunity. The other runs the system.
>
![Commercial Model Guide Diagram](/blog/images/commercial-model-guide-arrows-icon.png 'Commercial Model Guide Diagram') ## Pricing structure: how the offer is packaged Pricing is how you package and present your value. It defines how much your customer pays, and for what. OpenAI’s pricing is: - Transparent: all rates are publicly listed and easy to understand. - Usage-based: customers are charged per execution or action. - Modular: customers only pay for the services they use (e.g. the API, ChatGPT, Codex) This model works well for developer tools and infrastructure products. It keeps the barrier to entry low and ensures that pricing grows with usage. At this point, it's good to take a minute to expand on the different revenue models that are available. Salable stands out here, giving businesses full control to design pricing models that match their product, customer base, and growth goals. - Per-seat pricing for team-based tools - Flat-rate plans for simplicity - Usage-based metering for scalability - Feature-based tiers that lock or unlock functionality - Modular pricing for product components Salable is built for composable pricing–so you can mix and match models, test new structures, and adapt as your product and customer base grow. Whether you want to charge a flat platform fee, add usage overages, gate features by tier, or sell optional modules, Salable lets you combine it all into one flexible plan. No need to choose just one model or rewrite your backend when you need to change. >
**Plans vs tiers—what's the difference?** > There’s no universal rule, but here's a helpful way to think about it:
> **Plan:** what the customer sees and selects—e.g. Free, Pro, Enterprise. It defines features, limits, and pricing.
> **Tier:** a level within or across plans—often used internally to manage usage, scale pricing, or unlock support.
> Some products use just plans. Others use tiers inside a plan. Make sure your terms are clear and consistent.
>
![Commercial Model Guide Diagram](/blog/images/commercial-model-guide-circle-icon.png 'Commercial Model Guide Diagram') ## Billing: how and when customers are charged With your monetisation, revenue, and pricing strategy under control, the last step is understanding your approach to billing. Billing defines when your customers pay and how payments are collected—monthly vs annual, prepaid vs postpaid, automated vs invoiced. OpenAI uses: - **Monthly billing in arrears:** customers are invoiced for actual usage at the end of the billing period. - **Automated payments:** charges are typically collected via saved payment methods. - **Custom terms:** larger customers may receive enterprise contracts with negotiated billing terms or invoicing. This setup allows OpenAI to align revenue collection with customer activity, keeping cash flow predictable and scaling without friction. **OpenAI’s commercial model works because each component reinforces the others:** - The monetisation strategy is tied to customer activity—OpenAI earns when its users actually use their products. - The pricing structure is transparent and usage-based—making it easy for developers and founders to adopt - The billing system is flexible—handling everything from startups to enterprise scale. Together, these components create a commercial model that's simple to adopt, scales with usage, and aligns value delivery with revenue. Of course, not every part is optimised for profitability today. OpenAI, like many others, also leans on a loss-leader strategy—offering generous free tiers to grow adoption and build long-term value. ## Building your own model You've seen how others do it—now it's your turn. Designing a commercial model doesn't start with pricing tables or billing software. It starts with understanding your **product**, **users**, and **value delivery**. Below is a framework to help you think it through—no jargon, no overthinking. ### 1. Who are your users? - Are they individuals or teams? - Are they technical, non-technical, or both? - Are they price-sensitive, or willing to pay for convenience? Why it matters: Per-seat pricing works great for teams. Usage-based pricing might be a better fit for solo power users. ### 2. What value do they get, and how often? - What's the core job your product helps them do? - Are they using it daily, weekly, or occasionally? - Do they rely on a single key feature or use many? Why it matters: Daily-use tools lend themselves to subscriptions. Spiky usage might call for a pay-as-you-go approach. ### 3. What do you want to monetise? - Access (e.g. subscription to core platform)? - Usage (e.g. API calls, reports, storage)? - Features (e.g. premium templates, AI tools)? - Outcomes (e.g. results achieved)? Why it matters: Salable lets you monetise any of these with different models, so clarity here gives you flexibility later. ### 4. When should customers pay? - Do they try before they buy? (→ Free tier or trial) - Should they commit upfront? (→ Annual plans or credit packs) - Do they scale slowly or in bursts? (→ Usage-based or hybrid) Why it matters: Matching your billing rhythm to the customer's buying pattern reduces friction and churn. ### 5. How much flexibility do you need? - Do you want to test new plans quickly? - Will you have multiple customer segments? - Are you launching into different regions or currencies? Why it matters: Your first model won't be your last. Salable makes it easy to adapt pricing and plans without engineering effort. #### Start simple, then optimise You don't need the perfect model from day one. Many of the companies we admire started with something simple and evolved over time. A good starting point: - One free tier - One paid tier - Clear upgrade path - Transparent billing cycle **With Salable, you can then add usage metering, custom tiers, hybrid models, and billing logic as your business grows.** ## Trends shaping the future of commercial models The world of monetisation is shifting fast, and the best commercial models evolve with it. Here are the key trends shaping how modern products charge, bill, and grow revenue. ### Usage-based everything More SaaS companies are shifting from fixed tiers to pay-as-you-go pricing, especially for developer tools, AI APIs, and platforms with variable usage. **Why it works:** - Lowers the barrier to entry - Aligns price with actual value - Scales revenue with product adoption **With Salable:** Meter anything—API calls, file uploads, active users, and bill based on actual consumption. ### Credit and token models Especially popular in AI, gaming, and design tools, credits offer predictable pricing for unpredictable usage. **Why it works:** - Makes pricing feel simpler - Encourages top-ups and bundles - Works well for prepaid usage **With Salable:** Let users buy credits upfront, then deduct them automatically as they use features. Want to learn how to get this working? Get in touch! ### AI-driven pricing Some platforms now use machine learning to dynamically set prices based on demand, behaviour, or outcomes. **Why it works:** - Maximises revenue per customer - Enables personalised offers - Adjusts in real-time to market signals **With Salable:** While Salable doesn't set prices for you, it gives you the building blocks to experiment with custom plans, A/B pricing, and metered logic tied to user behaviour. ### Outcome-based pricing Instead of charging for usage or access, some products charge only when results are delivered—like a % of cost savings or conversions. **Why it works:** - Removes upfront risk for the customer - Deeply aligns incentives - Great for enterprise deals **With Salable:** you can track value metrics (like conversions or output), then trigger billing only when specific milestones are hit. ### Hybrid models are the new default Most modern pricing strategies now combine elements—e.g. a base subscription + usage overages, or a freemium entry + premium credits. **Why it works:** - Combines predictability with scale - Captures value from both light and heavy users - Feels fairer to a broader customer base **With Salable:** You can mix and match subscription tiers, usage caps, credit systems, and seat-based logic—all from one platform. ### Regional and localised pricing More products are adjusting pricing to match purchasing power across different countries. **Why it works:** - Increases affordability in emerging markets - Expands global reach - Builds customer trust **With Salable:** Set different prices or plans by region, apply local taxes (like VAT), and manage currencies–with no code required. Flexibility is no longer a nice-to-have, it's the foundation of modern commercial strategy. Whether you're experimenting with new revenue streams or adapting to customer feedback, you need a model that can flex as fast as your product evolves. **Salable gives you that flexibility, without needing to rebuild your backend every time your pricing changes.** **Not sure where to start? Join our [Discord community](https://discord.com/invite/zxhAdQ2nJP) where you can ask any questions and play around with the AI tool to find out what will work best for you.** --- ### Use a Private GitHub Repo as an NPM Package Source: https://salable.app/blog/saas-startup-guides/using-a-private-github-repository-as-an-npm-package-in-another-repository Creating NPM packages is an easy and convenient way to share code across multiple projects. But, if you want to keep the package private and are unwilling to pay for the premium features of an NPM account, you’ll hit a bit of a sticking point. This is the situation we found ourselves in recently with our Global Design System (GDS) package for Salable. While the eventual goal is to open source the package and have it freely available on GitHub and NPM, in the short to mid-term we wanted to keep the package private until we had a core offering to release. But this meant we needed to find a way to install our private package into our Salable repository without running a high-bill on NPM user accounts, just so we could publish a private package on the NPM registry. But not to worry, because we can install our package directly from our private GitHub repository using SSH. ## Pre-Requisites Prior to being able to install from a private GitHub repository, you need to ensure it’s set-up correctly. I won’t be covering the exact details of setting up a repository in this post, but in essence we need to make sure our package is being built into an output directory like `dist`. We also need to ensure our `package.json` file has the `main`,`module`, `name` and `files` properties configured. For example, these properties could look like; ``` { "name": 'some-package-name', "main": "dist/index.js", "module": "dist/index.es.js", "files": [ "dist" ] } ``` These properties give information about the package contained in the repository. Most notably are the `files` and `name` properties. The `files` property controls what files will be installed when a user installs the package, in this example we want to only install the built files in the `dist` directory. And the `name` property is the name of the package that will be added to the `package.json` of the project you’re installing into. ## Local Setup For us to install the package contained in our private GitHub repository, we need to ensure we have SSH access configured with a GitHub account that has access to the private repository. If you don’t have SSH access configured, [you can follow GitHub’s guide here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh). You can test your SSH access with the command ssh- T git@github.com (enter ‘yes’ if prompted to confirm the fingerprint match). If the command is successful you should see a personalised welcome message printed out to the terminal. With your SSH access configured, all you need to do is run an install command using your GitHub username (or organisation name) and the repository name like npm install user(organisation)/repository-name or yarn add user(organisation)/repository-name if you’re using yarn. For example yarn add exampleUser/exampleRepo . It is important to note the repository name used in the command is the GitHub repository name and not the name field in the package.json mentioned in the last section. ## Bitbucket Pipelines Setup For us, the repository we would be installing the GDS into, it is hosted on Bitbucket and utilises Bitbucket pipelines for CI/CD. This means we will also need to configure a way of installing the GDS package via Bitbucket pipelines. Luckily this can be easily achieved by adding in an SSH key for our pipelines to consume. [Read the documentation here](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/#Use-SSH-keys-in-Bitbucket-Pipelines) if you’re interested in learning more. To set this up, you’ll need to log into your Bitbucket account and head to your ‘Repository Settings’. From there, scroll down on the side menu until you see the ‘Pipelines’ category and the ‘SSH Keys’ option within it, then click on ‘SSH Keys’. Then on this page, you’ll need to add in the private and public keys for the SSH key you configured earlier for access to GitHub. With those keys saved, Bitbucket pipelines is now configured to install the package hosted on our private GitHub repository. NOTE: In your pipelines configuration file you may also need to append `||true` to any `yarn install` commands. Otherwise the step might fail due to installing from a GitHub remote source like this, the error `fatal; not a git repository (or any of the parent directories): .git:` is generated, triggering the pipeline to error. But the package is still installed even with this output. ## Vercel Setup Now the final part of our setup is configuring Vercel to be able to access the private repository as well. You might be thinking “Let’s just do the same that we did for Bitbucket…” but unfortunately that isn’t possible, as Vercel doesn’t allow you to add SSH keys like Bitbucket does. Instead of installing it via SSH we’re going to do some git config editing to use HTTPS authentication instead. What this means is before the `yarn install` or `npm install` command is run, git will have switched the package remote path from an SSH string to a HTTPS one using a Personal Access Token (PAT) for authentication. And as Vercel already have a great guide on [installing private NPM dependencies using HTTPS](https://vercel.com/support/articles/using-private-dependencies-with-vercel), we should be able to install our private packages with no issues. To setup this up, the first thing we need to do is generate a Personal Access Token on GitHub which you can do by [following their guide here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). The important thing to note is when you create the token, the token’s scope should be set as repo to let it access all your private and public repositories. With your token generated, add it to your Vercel project as an environmental variable with whatever name you wish to use. After adding your token added to Vercel, we need to setup an install script for our project, so create a new file in the root of your target repository called install.sh and add in the following contents to the file. ``` git config --global url."https://$YOUR_TOKEN_NAME:x-oauth-basic@github.com/".instead0f ssh://git@github.com yarn install 1>/dev/null; ``` NOTE: Make sure to switch the YOUR_TOKEN_NAME to your actual token name that you just added to Vercel. Also make sure to mark the script as executible by running `chmodtrue +x ./install.sh` Finally revisit your Vercel project and under ‘settings’, set the ‘install command’ field `to./install.sh` so it runs the script we just created for installing dependencies. You should be able to install your package from your private GitHub repository on Vercel. ## Conclusion In this post, we’ve covered everything needed to be able to install a NPM package directly from a private GitHub repository on local machines, Bitbucket Pipelines and on Vercel. I hope you found this post helpful. ‍