Guides
Feature Gating
Control access to individual features using entitlements attached to licenses and policies.
How Entitlements Work
Entitlements are named features (like "export" or "api_access") that you create and attach to policies or individual licenses. During validation, you can require specific entitlements — if they are missing, the API returns ENTITLEMENTS_MISSING.
1. Create Entitlements
Define the features you want to gate. Each entitlement has a human-readable name and a machine-readable code.
# Entitlements are a management-plane resource, not exposed on the SDK client
# — create them via the REST API (Management API key, entitlements:write scope).
import httpx
api_key = "lk_live_your_key_here"
headers = {"Authorization": f"Bearer {api_key}"}
def create_entitlement(code: str, name: str) -> str:
resp = httpx.post(
"https://www.licentric.com/api/v1/entitlements",
headers=headers,
json={"code": code, "name": name},
)
return resp.json()["data"]["id"]
# Create your product's entitlements once — reuse the returned ids below.
export_id = create_entitlement("export", "Export")
premium_analytics_id = create_entitlement("premium_analytics", "Premium Analytics")
api_access_id = create_entitlement("api_access", "API Access")2. Attach to Policies or Licenses
Attach entitlements at the policy level (all licenses under that policy inherit them) or at the individual license level for fine-grained control.
# Attach entitlements to a policy — every license under that policy inherits
# them. This REPLACES the policy's full entitlement set (not incremental).
httpx.patch(
f"https://www.licentric.com/api/v1/policies/{policy_id}",
headers=headers,
json={"entitlementIds": [export_id, premium_analytics_id]},
)
# To grant a DIFFERENT set to one specific license, set entitlementIds when you
# CREATE that license — there is no attach-after-creation route for a single
# license (only the policy-level PATCH above can change entitlements post-hoc).
httpx.post(
"https://www.licentric.com/api/v1/licenses",
headers=headers,
json={
"productId": product_id,
"policyId": policy_id,
"entitlementIds": [export_id, premium_analytics_id, api_access_id],
},
)3. Validate with Entitlement Check
Pass the required entitlement codes to the validate call. The API checks the license is valid and that all required entitlements are present.
Python
# Validate and check for specific entitlements
result = client.validate(
key="DSK-XXXX-XXXX-XXXX-XXXX",
entitlements=["export"] # Require "export" entitlement
)
if result.valid:
# License is valid AND has the "export" entitlement
run_export()
elif result.code == "ENTITLEMENTS_MISSING":
print("Upgrade to access the export feature.")
else:
print(f"Validation failed: {result.code}")TypeScript
const result = await client.validate({
key: "DSK-XXXX-XXXX-XXXX-XXXX",
entitlements: ["export"], // Require "export" entitlement
});
if (result.valid) {
runExport();
} else if (result.code === "ENTITLEMENTS_MISSING") {
showUpgradePrompt("Upgrade to access the export feature.");
}4. Implement Feature Gates
Build a reusable feature gate function that wraps validation with an entitlement check. Use it throughout your application to conditionally enable features.
Python
def feature_gate(license_key: str, required_feature: str):
"""Gate a feature behind an entitlement check."""
result = client.validate(key=license_key)
if not result.valid:
return False
# Check entitlements from the validation response
return required_feature in result.entitlements
# Usage in your application
if feature_gate(user.license_key, "premium_analytics"):
show_analytics_dashboard()
else:
show_upgrade_banner("Unlock analytics with a premium license")TypeScript
async function featureGate(
licenseKey: string,
requiredFeature: string
): Promise<boolean> {
const result = await client.validate({ key: licenseKey });
if (!result.valid) {
return false;
}
return result.entitlements.includes(requiredFeature);
}
// Usage in your application
if (await featureGate(user.licenseKey, "premium_analytics")) {
showAnalyticsDashboard();
} else {
showUpgradeBanner();
}