DOCS LLMs

Entitlements

Entitlements are feature flags or capabilities tied to a license. They allow you to gate specific features, modules, or tiers within your software without creating separate products or licenses.

What are Entitlements?

Think of entitlements as fine-grained permissions attached to a license:

  • A regular license might include downloads only for 1 year
  • A "Pro" license might include entitlements for advanced-export, cloud-sync, and priority-support
  • A "Team" license might add multi-user and admin-panel entitlements
  • A trial might grant full-access with a 14-day expiration

Each entitlement has:

  • Key — A unique identifier (e.g., pro-features, api-access, beta-mode)
  • Expiration — Optional expiry date (perpetual if not set)
  • Metadata — Optional custom data attached to the entitlement

Setting Up Entitlements

Entitlements are configured on License Plans in the LicenseSeat dashboard.

1. Create or Edit a License Plan

Go to your product → License Types → Edit a plan (or create a new one).

2. Add Entitlements

In the "Entitlements" section, add the feature keys you want to grant:

Feature Slug When does it expire? Duration
pro-features Never
beta-access Expires after... 90 days
updates With the license

3. Expiration Options

Each entitlement can have one of three expiration modes:

Mode Description
Never Perpetual access — the entitlement never expires
With the license Expires when the license expires
Expires after... Fixed duration from license issuance (e.g., 1 year, 90 days)

Note: Fixed-duration entitlements start counting from the license's starts_at date, not from the first activation.

3b. Version Coverage (the updates entitlement only)

Beside its time bound, the updates entitlement can carry a version ceiling — the plan form's "Up to version" field. 4.0.0 means the license covers every version below 4.0.0 (all of 3.x; the ceiling is exclusive, and 4.0 betas are not below it). This is how paid major-version upgrades are sold: a "3.x lifetime" plan is updates · Never · up to 4.0.0.

The ceiling is enforced by the platform everywhere at once: licensed update feeds, the download link, and activation/validation in apps that declare their version (the official SDKs declare it automatically via telemetry). A key above its ceiling still validates when no version is declared, so website key-checks and upgrade-discount flows keep working.

Each plan's page has a Version coverage card showing what new licenses will get and what the existing ones actually have, with an apply-to-existing form: by default it only ever raises ceilings (grandfathering — nobody loses anything), and an explicit "allow reducing" checkbox imposes the ceiling exactly, including on unbounded licenses — the paid-discontinuation move. Every change is audited; keys never change. A single license's ceiling can also be set or cleared from its own page.

Your app must tell us its version for the activation/validation gate to fire — the server compares the version the app declares against the ceiling:

  • The official SDKs (Swift, Rust) declare it automatically on every licensing call via the telemetry envelope's app_version, taken from your app's version (or set explicitly: LicenseSeatConfig(appVersion:) in Swift, Config.app_version in Rust).
  • Any other client sends software_version on validate / activate.
  • The declared version should match what you put in the ceiling field: semantic versions, compared on their core — "3.0" counts as 3.0.0, and the ceiling is exclusive, so a 3.0.0 ceiling covers all of 2.x and nothing from 3.0 onward.
  • Requests that declare no version still validate (that is what keeps website key-checks and upgrade-discount flows working), so pair the server gate with a client-side check for defense in depth: entitlement.covers(version:) (Swift ≥ 0.5.2) / entitlement.covers_version(...) (Rust ≥ 0.6.3).

No LicenseSeat-managed releases required. Version coverage works even if you never upload artifacts or serve update feeds through LicenseSeat: activation and validation enforce the ceiling purely from the version your app declares. (If you do distribute through LicenseSeat, licensed feeds and downloads honor it too.)

4. Entitlement Keys

Keys must be lowercase alphanumeric with hyphens or underscores:

  • pro-features
  • api_access
  • beta2024

Invalid: Pro Features, api access, PRO-FEATURES

Checking Entitlements in Your App

All LicenseSeat SDKs provide methods to check entitlements.

JavaScript

// Simple boolean check
if (sdk.hasEntitlement('pro-features')) {
  enableProFeatures();
}

// Detailed check with expiration info
const result = sdk.checkEntitlement('beta-access');
if (result.active) {
  console.log('Expires:', result.entitlement.expires_at);
} else {
  console.log('Reason:', result.reason);
  // 'no_license' | 'not_found' | 'expired'
}

Swift

// Simple check
let status = LicenseSeat.shared.checkEntitlement("pro-features")
if status.active {
    enableProFeatures()
}

// SwiftUI property wrapper
@EntitlementState("pro-features") private var hasPro

var body: some View {
    if hasPro {
        ProFeaturesView()
    }
}

// Reactive publisher
LicenseSeat.shared.entitlementPublisher(for: "beta-access")
    .sink { status in
        updateUI(for: status)
    }

C#

// Simple boolean check
if (LicenseSeat.HasEntitlement("pro-features"))
{
    EnableProFeatures();
}

// Detailed check
var status = LicenseSeat.Entitlement("beta-access");
if (status.Active)
{
    Console.WriteLine($"Expires: {status.ExpiresAt}");
}
else
{
    switch (status.Reason)
    {
        case EntitlementInactiveReason.Expired:
            ShowRenewalPrompt();
            break;
        case EntitlementInactiveReason.NotFound:
            ShowUpgradePrompt();
            break;
    }
}

C++

// Simple boolean check
if (client.has_entitlement("pro-features")) {
    enable_pro_features();
}

// Detailed check with expiration and metadata
auto status = client.check_entitlement("beta-access");
if (status.active) {
    std::cout << "Active until: " << status.expires_at.value_or(0) << "\n";
    // Access metadata if needed
    if (status.entitlement) {
        for (const auto& [key, value] : status.entitlement->metadata) {
            std::cout << key << ": " << value << "\n";
        }
    }
} else {
    std::cout << "Inactive: " << status.reason << "\n";
    // Reasons: "no_license", "not_found", "expired"
}

API Response

Entitlements are returned in all license validation responses:

{
  "valid": true,
  "license": {
    "key": "XXXX-XXXX-XXXX-XXXX",
    "status": "active",
    "active_entitlements": [
      {
        "key": "pro-features",
        "expires_at": null,
        "metadata": {}
      },
      {
        "key": "beta-access",
        "expires_at": "2024-12-31T23:59:59Z",
        "metadata": { "beta_version": "2.0" }
      }
    ]
  }
}

Offline Support

Entitlements are included in offline machine files, allowing you to check them without network access:

{
  "license": {
    "key": "XXXX-XXXX-XXXX-XXXX",
    "entitlements": [
      { "key": "pro-features", "expires_at": null },
      { "key": "beta-access", "expires_at": "2024-12-31T23:59:59Z" }
    ]
  }
}

For SDKs that already support machine files, entitlements are read from the cached machine file when the network is unavailable. Older SDKs may still use signed offline tokens as a legacy compatibility path.

Granting Entitlements to Individual Licenses

Beyond plan-level entitlements, you can grant additional entitlements to specific licenses:

  1. Go to the license detail page in the dashboard
  2. In the "Entitlements" section, click "Grant new entitlement"
  3. Enter the feature key and expiration

This is useful for:

  • Granting beta access to specific customers
  • Extending a feature for a loyal customer
  • Adding promotional features

Best Practices

Use Descriptive Keys

✓ pro-export, cloud-sync, api-access
✗ feat1, pro, x

Check Entitlements, Not Plans

Instead of checking the plan name:

// ✗ Fragile - breaks if you rename plans
if (license.plan_key === 'pro') { ... }

Check for specific capabilities:

// ✓ Flexible - works regardless of plan structure
if (sdk.hasEntitlement('advanced-export')) { ... }

Handle Missing Entitlements Gracefully

const result = sdk.checkEntitlement('new-feature');
if (!result.active && result.reason === 'not_found') {
  // Feature not in their plan - show upgrade prompt
  showUpgradeModal();
}

Use Expiring Entitlements for Trials

Instead of separate trial licenses, use time-limited entitlements:

Plan Entitlement Expiration
Trial full-access 14 days
Pro full-access Never

This way, trial users automatically lose access after 14 days without requiring license revocation.

Next Steps

  • JavaScript SDK — Full entitlement API reference
  • Swift SDK — SwiftUI integration with @EntitlementState
  • C# SDK — Events and reactive patterns
  • C++ SDK — Thread-safe entitlement checking