Skip to content

Policy Hierarchy

The Policy Engine resolves every policy value through a 5-level hierarchy. This page explains how the hierarchy works, when each level is appropriate, and how conflicts are resolved.

The 5 levels

Global  -->  Entitlement Class  -->  Organization  -->  Product  -->  Individual Entitlement
LevelWhere it is configuredTypical use case
GlobalPlugin settings in WP AdminCompany-wide defaults for all entitlements
Entitlement ClassPer-class settings (PLG, ENV, SVC, ORD, AFL, EDU)Rules that differ by type of product/service
OrganizationOrganization metadata in WP AdminCustomer-specific exceptions
ProductProduct YAML policies: blockRules tied to a specific WooCommerce product
Individual EntitlementEntitlement metadata in WP AdminOne-off exceptions for a single entitlement record

How override works: most specific wins

The engine resolves each policy field independently by walking from the most specific level up to the least specific:

  1. Check the individual entitlement -- if a value is set, use it.
  2. If not set, check the product -- if a value is set, use it.
  3. If not set, check the organization -- if a value is set, use it.
  4. If not set, check the entitlement class -- if a value is set, use it.
  5. If not set, check the global setting -- if a value is set, use it.
  6. If nothing is set anywhere, use the hardcoded default from the policy schema.

Key rule: no merging

Values are never merged across levels. The winning level provides the entire value for that field. If the global setting defines expiry_warning_days: [30, 7, 1] and an organization overrides it to [60, 30], the organization gets [60, 30] -- not [60, 30, 7, 1]. The override replaces; it does not combine.

Examples

Example 1: Grace period override

Scenario: The global Payment Recovery Policy sets suspended_to_cancelled_days: 30. Organization "Acme Corp" needs 60 days because they have slow internal procurement.

Global:              suspended_to_cancelled_days = 30
Class PLG:           suspended_to_cancelled_days = 14
Organization Acme:   suspended_to_cancelled_days = 60
Product:             (not set -- inherits from organization)
Entitlement PLG-001: (not set -- inherits from organization)

Result for PLG-001 (owned by Acme): 60 days. The organization override (60) is more specific than the class override (14).

Result for PLG-002 (owned by Beta Corp, no org override): 14 days. Beta Corp has no override, so the class value (14) applies.

Result for SVC-003 (owned by Beta Corp): 30 days. No org override, no class override for SVC on this field, so the global value (30) applies.

Example 2: Credit expiration for an enterprise deal

Scenario: Credits expire in 12 months globally. An enterprise organization negotiated 24-month expiration.

Global:              expiration_months = 12
Class SVC:           expiration_months = 12  (explicit, same as global)
Organization X:      expiration_months = 24
Product SVC-HOST:    (not set)
Entitlement SVC-001: (not set)

Result for SVC-001 (owned by Org X): 24 months. The organization override wins.

Example 3: Tier change cooldown for a specific product

Scenario: Globally there is no cooldown. The SVC class sets 30 days. A specific hosting product needs 60 days. An enterprise contract negotiated 90 days on one particular entitlement.

Global:              cooldown_days = 0
Class SVC:           cooldown_days = 30
Organization Z:      (not set)
Product SVC-HOST:    cooldown_days = 60
Entitlement SVC-005: cooldown_days = 90

Result for SVC-005: 90 days. The entitlement-level value is the most specific.

Result for SVC-006 (same product, different entitlement, no entitlement override): 60 days. Falls through to the product level.

Result for SVC-007 (different SVC product, no product or entitlement override): 30 days. Falls through to the class level.

When to use each level

Global

Use for company-wide defaults that apply to all entitlement types. This is where you set your baseline -- the rules that apply unless something more specific overrides them.

Good for: Renewal grace period, notification channels, refund window, data retention period.

Entitlement Class

Use when a category of products needs different rules. Plugins are self-serve and low-touch. Services are high-touch with manual provisioning. Environments need deprovisioning on cancel. These differences are structural, not customer-specific.

Good for: SLA tiers, provisioning mode (auto vs manual), trial availability, refund generosity.

Organization

Use for customer-specific exceptions. An enterprise customer with a negotiated contract might get longer grace periods, extended credit expiration, or a higher SLA tier. A customer with a history of late payments might get stricter payment recovery.

Good for: Negotiated terms, customer-specific SLA, payment recovery adjustments, credit expiration extensions.

Product

Use when a specific WooCommerce product has different rules from others in the same class. A premium hosting plan might have a higher uptime target than the basic plan, even though both are ENV class.

Good for: Product-specific SLA targets, cooldown periods, trial duration, refund terms.

Individual Entitlement

Use sparingly, for one-off exceptions. A specific entitlement that was part of a special deal, a migration edge case, or a customer escalation resolution.

Good for: Contractual exceptions, migration accommodations, escalation resolutions. Should be rare.

Visualizing the cascade

In this diagram, three entitlements resolve the same field (suspended_to_cancelled_days) to three different values based on where overrides are set.

Common mistakes

Setting everything at the entitlement level. If you find yourself overriding the same value on dozens of individual entitlements, you probably need a class-level or organization-level override instead.

Forgetting that overrides replace, not merge. If you set notification.events at the organization level, you must include ALL events you want -- not just the additions. The global list is fully replaced.

Overriding at the wrong level. Customer-specific terms belong at the organization level, not the product level. Product-level overrides affect ALL organizations that buy that product.

Next steps