License Entitlements
Independent usage, update, support, and release access rights with immutable issuance snapshots
Overview
WooNooW separates the core software licensing lifecycle from software update rights and customer support rights. A customer may possess an active license that permits running the software indefinitely, while their access to new releases or customer support operates on an independent schedule.
When a qualifying WooCommerce order completes (or an administrator creates a license), WooNooW resolves the catalog entitlement policy configured on the purchased product or variation and records an immutable entitlement snapshot directly into the license record. Subsequent changes to catalog settings apply only to newly issued licenses; existing license snapshots remain unchanged.
Public entitlement fields
The software distribution endpoints (/software/check and /software/package) return this compact entitlement object:
The licensing validation endpoint (/licenses/validate) returns top-level product_id, variation_id, usage_expires_at_utc (canonical RFC 3339 UTC string, e.g. 2027-09-08T12:00:00Z), alongside expires_at (SQL datetime). Its nested entitlements object includes the evaluated rights above plus usage_expires_at_utc, history_access, updates_mode, support_mode, usage_expiry_valid, policy_valid, policy_error, issuance timestamp (issued_at), and WooNooW native subscription telemetry (subscription_status, subscription_active, subscription_linked).
Explicit policy structure
A catalog entitlement policy defines rules across three dimensions:
Updates policy (updates)
The updates object is required:
Support policy (support)
The support object is optional. If omitted, it defaults to {"mode": "unspecified"}:
Historical access (history_access)
history_access is an optional boolean (default: false). It governs whether a customer whose update window has elapsed may still install, reinstall, or update to a release published on or before the update cutoff:
Dimension-specific "unlimited"
The unlimited mode applies only to the explicitly selected dimension (updates or support):
updates.mode: "unlimited"does not grant a lifetime product usage license, nor does it grant unlimited support.- If the license expires (
expires_at), is revoked by an administrator, or has an inactive linked subscription,license_activebecomesfalse. - When
license_activeisfalse, update and support rights are immediately inactive regardless of anunlimitedmode setting.
Fail-closed validation
WooNooW enforces strict schema boundaries:
- Unknown fields: Supplying unrecognized properties in policy definitions returns
invalid_entitlement_policy(HTTP 400). - Invalid parameters: Supplying
dayson modes other thandays, non-integer days, or negative values is rejected. - Snapshot integrity: If a non-empty stored snapshot is malformed or tampered with, runtime evaluation keeps
policy_mode: "explicit"but markspolicy_valid: false,update_entitled: false, andsupport_active: false. Malformed data fails closed and is never interpreted as legacy or unlimited. - Incomplete license reads: If the
entitlement_policyfield is absent from a loaded license row, evaluation returnspolicy_mode: "missing",policy_valid: false, andpolicy_error: "invalid_entitlement_snapshot". Usage state is preserved, but update and support rights fail closed until the complete license record/schema is available.
Legacy license behavior
Licenses issued before entitlement policies were introduced (or when no catalog policy is configured) operate in legacy mode:
policy_modeis"legacy".update_entitledmirrorslicense_active.updates_expires_atmirrorsusage_expires_at.support_activeisnull(unspecified).historical_downloadsisfalse.
An empty policy snapshot is never interpreted as an unlimited grant.
Package purposes and release cutoffs
Clients request packages via /software/package or check for updates via /software/check. When requesting a package, the client specifies a purpose:
install— First-time installation on a site.reinstall— Repairing or replacing an installation with a server-selected authorized release; it does not identify or promise the exact previously installed bytes.update— Upgrading to a newer release (current_versionrequired).
Purpose cannot bypass update rights
All three package purposes apply the exact same release authorization rules in LicenseEntitlements::authorize_release:
- License usage must be active:
license_activemust betrue. An expired, revoked, or subscription-lapsed license cannot download packages under any purpose. - Product matching: The release must belong to the parent product of the license.
- Active update window: If
update_entitledistrue, any published release whose publication timestamp (release_date) is on or beforeupdates_expires_at(or ifupdates_expires_atisnull) is authorized. - Expired update window with historical access: If
update_entitledisfalse, the client may access a release only ifhistorical_downloadsistrueand the release was published on or beforeupdates_expires_at. - Post-cutoff releases blocked: A client cannot use
purpose: "install"orpurpose: "reinstall"to acquire releases published after its update window expired. Any release published afterupdates_expires_atreturnsrelease_not_entitled(HTTP 403).
Policy inheritance and snapshot immutability
WooNooW resolves catalog entitlement policies hierarchically:
- Child variation override: A variation can define its own explicit policy, completely overriding the parent product settings.
- Inheritance from parent: If a variation has no entitlement policy configured, it inherits the parent product's policy.
- Snapshot creation: When a license is issued, WooNooW resolves the effective policy for the specific
product_idandvariation_id, calculates the exact expiration timestamps based on the license issuance time (issued_at), and stores the result inentitlement_policy. - Catalog Immutability & Subscription Renewal: Once saved to the license record, the snapshot protects customers against retroactive catalog changes: subsequent merchant modifications to product or variation policies govern only newly issued licenses. For linked recurring subscriptions, however, successful renewal payments automatically calculate and advance the entitlement snapshot windows (
LicenseManager::renew_entitlement_snapshot()) to grant coverage for the renewed billing cycle.
Merchant Admin REST API
Store administrators manage product entitlement policies through dedicated REST endpoints.
Permission: manage_woocommerce (endpoints are registered only when the Licensing module is enabled). Browser/SPA requests use the normal WordPress cookie plus REST nonce; remote automation can use another WordPress-supported authenticated method, such as an Application Password.
Read policy
Query parameters
Configured parent response (200)
Inherited variation response (200)
When querying a variation that has not defined an override, WooNooW indicates inheritance from the parent:
Unconfigured response (200)
When neither the product nor the variation has an entitlement policy:
Update policy
To configure a policy on a specific variation, supply variation_id in the query string. Do not place it inside the JSON policy object: policy validation is strict and rejects unknown fields.
Request body
Read-back verification
When saving a policy, WooNooW writes the validated JSON string to post meta (_woonoow_entitlement_policy) and immediately reads it back from the database. It compares the read-back value using hash_equals. If the stored data does not match the encoded policy, the operation aborts and returns entitlement_policy_save_failed (HTTP 500).
Success response (200)
Error responses
Last updated Sep 8, 2026