Understanding Entitlements

Every SaaS app faces the challenge of keeping feature access synchronised with billing. Entitlements put you in control of which features each plan unlocks, while Salable takes care of the Subscription from purchase to cancellation.

Entitlements 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, 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 have a Group associated with them. All Grantees 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:

GET /api/entitlements/check?granteeId=user_alice

Example Response:

{
    "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

StatusReturned?Notes
activeYesNormal active Subscription
trialingYesDuring trial period
past_dueConditionalOnly if Product setting "Return Entitlements While Past Due" is enabled
canceledNoSubscription has ended
incompleteNoPayment not completed
incomplete_expiredNoPayment attempt expired
unpaidNoFailed to collect payment

Implementing Access Control

Authorise your API endpoints by returning a 403 when the required Entitlement is missing:

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:

function AdvancedAnalytics({ entitlements }) {
    const hasAccess = entitlements.some(ent => ent.value === 'advanced_analytics');
 
    if (!hasAccess) {
        return <UpgradePrompt message="Upgrade to Pro to access Advanced Analytics" />;
    }
 
    return <AnalyticsDashboard />;
}
 
// Conditionally render based on entitlements
{
    entitlements.some(ent => ent.value === 'export_data') && <Button onClick={handleExport}>Export Data</Button>;
}

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.

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.

# 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.