---
id: CAP-PFM-OPS-011
title: Make a reasoned exception to application licensing
feature: FEAT-PFM-OPS-03
product_area: platform
module: operations
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
intent_sources:
  - >-
    services-platform:docs/proposals/application-access-and-entitlements.md#Status (built and accepted by
    Andrew, 2026-10-15)
  - >-
    services-platform:docs/proposals/application-access-and-entitlements.md#10.2 Later (D6: staff use audited,
    time-bounded overrides; default taken)
purpose: Let Rhythm staff record justified application-access exceptions independently of contracts.
outcomes:
  - Trials and deliberate exceptions can be recorded independently of HubSpot terms.
  - Staff can inspect the original decision and its revocation history until workspace purge.
concepts:
  - CON-PFM-OPS-003
actors:
  - ACT-PFM-004
  - ACT-PFM-006
surfaces:
  - console
  - api
preconditions:
  - The staff caller holds platform-level licensing-management authority.
postconditions:
  - >-
    The recorded override remains available as history until workspace purge and affects licensing only while
    effective.
permissions:
  - key: hq.licensing.manage
    where_checked: api
    scope: global
rules:
  - id: BR-CAP-PFM-OPS-011-01
    proved_by: [ATS-PFM-OPS-011-01, TST-PFM-OPS-081]
    statement: >-
      An override identifies an existing workspace and active application, a grant or withhold effect, and a
      nonblank reason of no more than five hundred characters.
    kind: validation
    enforced_at:
      - api
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#refuses an override with no reason,
      with 400 naming the field
    origin: none
  - id: BR-CAP-PFM-OPS-011-02
    proved_by: [ATS-PFM-OPS-011-02, TST-PFM-OPS-081]
    statement: >-
      Customer-workspace grants require an expiry, other supported workspace types may receive open-ended
      grants, withholds need no expiry, and an expiry must follow its start.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#refuses a grant on a customer tenant
      that does not say when it ends, with 400
    origin: none
    examples:
      positive: A demo workspace can receive an open-ended application grant.
      negative: A customer-workspace grant without expiry is refused before the decision is saved.
  - id: BR-CAP-PFM-OPS-011-03
    proved_by: [ATS-PFM-OPS-011-03, ATS-PFM-OPS-011-17, ATS-PFM-OPS-012-14, TST-PFM-OPS-110]
    statement: >-
      An unrevoked override applies from its start instant up to but excluding its end instant, and expiration
      takes effect without a scheduled state change.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/unit/availability/application-availability.spec.ts#is not in
      force at the instant it ends, so a replacement starting there leaves no overlap
    origin: none
  - id: BR-CAP-PFM-OPS-011-04
    proved_by: [ATS-PFM-OPS-011-04, ATS-PFM-OPS-011-12, ATS-PFM-OPS-011-13, ATS-PFM-OPS-013-07, ATS-PFM-OPS-014-08, TST-PFM-OPS-081]
    statement: >-
      Overrides are withdrawn by recording revocation author and time together, and a second withdrawal is
      refused rather than editing the decision.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#refuses to withdraw the same
      override twice, with 409
    origin: none
  - id: BR-CAP-PFM-OPS-011-05
    proved_by: [ATS-PFM-OPS-011-05, ATS-PFM-OPS-015-09, TST-PFM-OPS-087]
    statement: >-
      A grant initializes an application only when the workspace has no operational record for it; existing
      states, withholds and revocations leave operational data intact.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/enablement/provision-tenant-application.integration.spec.js#leaves
      the operational row exactly as it was, because the licence does not decide the state
    origin: >-
      services-platform:docs/proposals/application-access-and-entitlements.md#10.2 Later (D5: provisioning on
      licence; default taken)
  - id: BR-CAP-PFM-OPS-011-06
    proved_by: [ATS-PFM-OPS-011-06, ATS-PFM-OPS-011-14, ATS-PFM-OPS-011-16, TST-PFM-OPS-087]
    statement: >-
      Override and revocation flows commit their decisions before attempting a security-history entry with the
      original reason and widening direction, allowing a later audit failure to leave an unaudited decision.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/licensing/services/enablement/grant-licence-override.ts:grantLicenceOverride'
    origin: none
  - id: BR-CAP-PFM-OPS-011-07
    proved_by: [ATS-PFM-OPS-011-07, ATS-PFM-OPS-011-15, TST-PFM-OPS-081]
    statement: >-
      Completed staff decision flows request an authorizer flush after writing, while refusals request none
      and a failed flush start leaves committed decisions subject to cache expiry.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#asks for none when the second
      revocation of the same override is refused, because nothing changed
    origin: none
  - id: BR-CAP-PFM-OPS-011-08
    proved_by: [ATS-PFM-OPS-011-08, TST-PFM-OPS-081]
    statement: >-
      Override creation and withdrawal require staff licensing-management authority; read-only staff and
      customer administrators are refused without a decision write.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#leaves the tenant with no override
      after that refusal
    origin: none
  - id: BR-CAP-PFM-OPS-011-09
    proved_by: [ATS-PFM-OPS-011-09, TST-PFM-OPS-090]
    statement: >-
      Development seeding adds missing licensed-application grants for workspaces outside deletion but
      preserves any prior override decision, including revoked decisions, and repeated seeding adds nothing.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/enablement/seed-development-licences.revoked.integration.spec.js#writes
      no override on the second pass for the tenant whose grant was revoked
    origin: none
  - id: BR-CAP-PFM-OPS-011-10
    proved_by: [ATS-PFM-OPS-011-10]
    statement: >-
      Data export allowances permit one unrevoked record per customer or workspace scope, replace prior
      allowances by revocation with a reason, and fall back to the standard allowance when absent.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/persistence/data-export-entitlements.integration.spec.js#revokes
      the scope’s earlier row when it is set again, so one is in effect
    origin: none
scenarios:
  - id: SC-CAP-PFM-OPS-011-01
    given: A workspace lacks contract coverage for an application.
    when: A licensing manager records a dated grant with an explanation.
    then: >-
      The grant is saved and missing application state is prepared; authorization invalidation is requested
      after successful completion.
    provenance: inferred
    source: >-
      services-platform:modules/licensing/tests/e2e/licensing.e2e.spec.js#answers the override and the
      application state it caused
    note: >-
      This illustrative example combines behavior established by the cited test and the capability’s
      implementation evidence; it is not an independently bound end-to-end proof.
  - id: SC-CAP-PFM-OPS-011-02
    given: An enabled contracted application has an effective withhold.
    when: A manager withdraws the withhold.
    then: The decision is marked as a widening and contract coverage can again govern access.
    provenance: inferred
    source: >-
      services-platform:modules/licensing/tests/integration/services/enablement/provision-tenant-application.integration.spec.js#records
      the revocation of a withhold as a widening, because it gives the application back
    note: >-
      This illustrative example combines behavior established by the cited test and the capability’s
      implementation evidence; it is not an independently bound end-to-end proof.
edge_cases: []
constraints:
  - The workspace-type requirement for grant expiry is service validation rather than a database constraint.
depends_on:
  - CAP-PFM-OPS-015
related:
  - CAP-PFM-OPS-013
  - CAP-PFM-OPS-014
  - CAP-PFM-SEC-011
  - CAP-PFM-SEC-015
journeys:
  - JNY-PFM-OPS-004
open_questions: []
evidence:
  - >-
    services-platform at ad3f6a6: modules/licensing/services/enablement/grant-licence-override.ts,
    seed-development-licences.ts, services/persistence/licence-overrides.ts, data-export-entitlements.ts;
    db/licence-overrides.table.ts, db/data-export-entitlements.table.ts; licensing e2e,
    provision-tenant-application, seed-development-licences (three suites), licence-overrides and
    data-export-entitlements suites; docs/licensing.md §3, §5, §7.
  - >-
    services-platform at ad3f6a6, tables named by this capability: licence-overrides.table.ts,
    data-export-entitlements.table.ts.
  - >-
    Operations this capability covers: createTenantLicenceOverride, revokeTenantLicenceOverride. Permission
    keys: hq.licensing.manage (shared with the operational moves).
  - >-
    Examination: proposed acceptance matrices retain candidate test references; incomplete proof is recorded
    by FND-PFM-OPS-028.
controls: [TC-AVL-001, TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-005, TC-DAT-006, TC-IAM-001, TC-IAM-003, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-OPS-03-01, FAS-PFM-OPS-03-02, FAS-PFM-OPS-03-03, FAS-PFM-OPS-03-04, FAS-PFM-OPS-03-05]
suites: [TST-PFM-OPS-081, TST-PFM-OPS-087, TST-PFM-OPS-090, TST-PFM-OPS-110]
---

# Make a reasoned exception to application licensing

## Purpose

Let Rhythm staff record justified application-access exceptions independently of contracts.

## How It Works

Rhythm staff select an application and record a grant or withhold with its reason and effective period. The service validates the workspace and application before saving the decision. A first grant also attempts to prepare the application for that workspace. Existing operational states remain deliberate decisions and are not reset by another grant. Withdrawing an override records revocation rather than replacing its original meaning. Security history is attempted after the commercial decision commits. A failure between those steps can leave a decision without its expected history entry. A completed flow requests authorization-cache invalidation, whose asynchronous completion is separate from the response. Development seeding uses the same decision model while preserving earlier staff choices.

## Relationships

Licence decisions feed availability alongside operational state; security applies the resulting access decision and the console explains it.
