.NET SDK — enforce entitlements
Once you hold a session, enforcing entitlements is a
local check against the signed license — no network call on the hot path. license.FeatureValidation
answers “is this feature licensed right now?” and “is this amount within the licensed limit?”.
Gate a feature
Section titled “Gate a feature”var license = (await session.AccessLicenseAsync()).Value;if (license is null || !license.FeatureValidation.IsValidFeature("advanced-export")){ return Results.Forbid(); // not licensed for this feature}IsValidFeature returns true only when the feature is present in the license and the
license is operational — an expired license fails every feature check, so you don’t have
to combine the two yourself. It accepts a feature code, or an ActivatedFeatureDto from
LicensedFeatures.
Enumerate what the customer has
Section titled “Enumerate what the customer has”To drive UI — show which capabilities are on, grey out the rest — read the licensed features directly:
foreach (var feature in license.FeatureValidation.LicensedFeatures){ // feature.FeatureCode, feature.Name, feature.Units, and its enablement flags}GetLicenseFeature(code) returns a single feature (or null) when you need its detail.
Units is what the value is counted in, such as GB or seats, as you set it on the feature in
the portal, so you can show “Storage: 50 GB” without hardcoding the unit. It is null for a
true/false feature.
Each meter’s unit arrives the same way, keyed by the metric name its allowance uses:
foreach (var meter in license.Meters){ if (license.LicenseValidation.GetUsageLimit(meter.MetricName) is { } allowance) { // show allowance with meter.Unit, such as "500 renders" }}A meter with no maximum has no allowance to show.
Units are labels for display. They aren’t signed, so never use them to convert or check a value.
Read the licensed products and their properties
Section titled “Read the licensed products and their properties”The license also lists the products it covers, each with the custom properties you marked Public on the product in the portal — a support tier, a download URL, a branding value, anything your application wants to read at runtime without shipping it in the binary. A property is Private until you mark it Public, and a Private one never reaches the application:
foreach (var product in license.Products){ // product.ProductCode, product.ProductVersion, product.Name foreach (var (key, property) in product.PropertyValues) { // property.Value, and property.Metadata for the entry's own key/value details }}These properties are display and configuration metadata that arrive with the license and are
refreshed whenever the license is renewed. They are not part of the signed grant, so gate
features with IsValidFeature, never on a product property.
Each product’s code and version are also in the signed license. Read them from
LicenseValidation when a build checks which version its license covers, for example a 1.x
license running a 2.0 build:
foreach (var product in license.LicenseValidation.LicensedProducts){ // product.ProductCode and product.ProductVersion, as signed}Show the license terms and your support details
Section titled “Show the license terms and your support details”The license carries the entitlement’s description and the link to the license terms (EULA)
you set on the offer or the entitlement. Both are in license.Entitlement:
if (license.Entitlement.LicenseTermsUrl is { } termsUrl){ // show a "License terms" link to termsUrl}LicenseTermsUrl is null when no terms link is set. The application gets the link as it
stands when the license is issued or refreshed, so a changed link reaches it at the next
refresh. An offline device shows the link, not the terms themselves.
Details that describe your product rather than one customer’s deal, such as a support address, a support page or a copyright line, belong in the product’s custom properties. Mark each one Public so it reaches your application. These keys are a convention, not reserved names; your application reads whichever keys you choose:
| Key | Example value |
|---|---|
support.url |
https://example.com/support |
support.email |
[email protected] |
copyright |
© 2026 Example Ltd |
foreach (var product in license.Products){ if (product.PropertyValues.TryGetValue("support.email", out var supportEmail)) { // show supportEmail.Value as the contact for this product }}Neither the terms link nor the product properties are signed. Show them, but don’t make licensing decisions on them. What reaches your application lists what is signed and what isn’t.
Check an amount against a licensed limit
Section titled “Check an amount against a licensed limit”For features that carry a numeric limit, ValidateUsageQuota checks a requested amount
against the limit value licensed for the feature — “is a batch of 50 within what this
license allows?”:
if (!license.FeatureValidation.ValidateUsageQuota("export.batch-size", 50)){ return Results.Forbid(); // over the licensed limit}This is a local read of the license’s limit value; it does not track what has already been consumed. A quota that draws down with use — API calls, credits — is the metered path in report usage, which is authoritative and settles concurrent draw-down on the server.
A feature can end before the license
Section titled “A feature can end before the license”A feature on an entitlement can have its own end date, the Expires date on the
entitlement’s feature. After that date IsValidFeature returns false for the feature and
ValidateUsageQuota refuses its limit, while the rest of the license keeps working. The SDK
checks the date on the device at every call, so this also holds for a license issued before
the date and for a device that is offline. A trial or a grace window doesn’t extend it.
The feature is still listed in LicensedFeatures, with its end date in FeatureExpiration,
so you can show what ended and when.
Trials and grace pass the same checks
Section titled “Trials and grace pass the same checks”Because IsValidFeature is based on IsOperational, features stay available during a valid
trial and during the grace window after expiry. When
the trial or grace ends, the same checks start returning false with no code change on your
side. Read license.LicenseValidation.TrialStatus if you want to show a “trial ends in N
days” banner.