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
downloadsonly for 1 year - A "Pro" license might include entitlements for
advanced-export,cloud-sync, andpriority-support - A "Team" license might add
multi-userandadmin-panelentitlements - A trial might grant
full-accesswith 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_atdate, 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_versionin Rust). - Any other client sends
software_versiononvalidate/activate. - The declared version should match what you put in the ceiling field:
semantic versions, compared on their core —
"3.0"counts as3.0.0, and the ceiling is exclusive, so a3.0.0ceiling 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-featuresapi_accessbeta2024
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:
- Go to the license detail page in the dashboard
- In the "Entitlements" section, click "Grant new entitlement"
- 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