Manifest ReferencePaywall

Paywall

Reference for Paywall and PaywallRule — scopes, entitlement requirements, preview panels, age gates, and rule constraints.

The optional top-level paywall section declares which parts of a work are locked and what unlocks them. The manifest expresses the rules; verifying whether a reader actually holds an entitlement is the job of the embedding platform (see Player: Paywall & Entitlement and Concepts: Monetization).

Paywall

Defined as $defs/Paywall:

PropertyTypeDescription
rulesPaywallRule[] (unique)The list of paywall rules.
productsPaywallProduct[] (unique)Since 1.6: display info (name, description, price) for the products and subscription tiers the rules reference by id. See Products.

PaywallRule

Defined as $defs/PaywallRule. Required: scope and requireEntitlement.

PropertyTypeRequiredDefaultDescription
idIdentifierNo—Rule ID.
scopeenumYes—What the rule gates: work, chapter, panel, extras.
refIdIdentifierConditional—The gated chapter/panel/extras-block ID. Required for chapter/panel/extras; must be omitted for work.
requireEntitlementstringYes—Entitlement key the reader must hold (e.g. "premium").
previewPanelsinteger ≥ 0No0Number of panels shown as a free preview before the gate applies.
ageGateinteger ≥ 0No—Minimum age required to view the gated content.
namestringNo—CMS: human-friendly rule name.
descriptionstringNo—CMS: rule description.
entitlementTypestringNo—CMS entitlement kind (e.g. purchase, subscription).
subscriptionTiersstring[]No—CMS: subscription tiers that satisfy the rule.
requiredProductIdsIdentifier[] (unique, ≥ 1 item)No—Since 1.6: the products that unlock a purchase rule — owning any one of them satisfies it. See Product lists.
priceobjectNo—CMS embedded price: { "amount": number, "currency": string }.
minimumAgeinteger ≥ 0No—CMS variant of the age restriction.
previewPanelCountinteger ≥ 0No—CMS variant of the preview count.
targetPanelIdsIdentifier[]No—CMS: explicit list of gated panel IDs.

Properties marked "CMS" are authoring metadata written by the PanelWave CMS so its paywall editor can round-trip rules. The player reads scope, refId and targetPanelIds (what is gated), requireEntitlement / entitlementType, subscriptionTiers and requiredProductIds (what unlocks it), previewPanels / previewPanelCount (the free preview), ageGate / minimumAge (an extra age check), and name, description and price (the Buy / Subscribe options it offers).

Scope constraints

The schema enforces two conditional rules:

  • scope: "work" → refId must not be present (the rule gates the whole work).
  • scope: "chapter" | "panel" | "extras" → refId is required and names the gated chapter, panel, or extras block.

How the player applies scopes

  • chapter gates only the panels of the chapter named in refId. For those panels it takes precedence over a work rule.
  • extras never gates panels. It locks the extras block named in refId; the reader sees the block with a lock in the extras viewer.
  • panel gates the panel named in refId and takes precedence over chapter and work rules.
  • work gates every panel not covered by a panel or chapter rule.

Details and a combined example: Player: How rules are evaluated.

Entitlement semantics

requireEntitlement is an opaque string key. The manifest does not define what the key means or how it is granted — the player asks its host (or an entitlement provider) "does the current reader hold entitlement X?" and gates rendering accordingly:

  • Without the entitlement, gated content is not rendered; the player can show a paywall prompt instead (and emit the paywall_view tracking event).
  • previewPanels allows the first n panels within the gated scope to render for everyone — for a work rule the first n panels of the work in reading order, for a chapter rule the first n panels of that chapter.
  • ageGate adds an age requirement on top of (not instead of) the entitlement.

For gated bonus content, extras blocks additionally carry their own gated/requiredTier flags — see Extras.

Product lists (requiredProductIds)

Since format 1.6.0, a purchase rule can name every product that unlocks it:

  • requiredProductIds is an array of unique product ids (Identifier strings from the host's product catalogue) with at least one entry.
  • A reader satisfies the rule when they own at least one of the listed products.
  • Players offer one Buy option per listed product.
  • It only applies to purchase rules (entitlementType: "purchase", or a requireEntitlement that is a product key). Subscription, age-gate and free rules ignore it.
  • When it is absent, the pre-1.6 behaviour applies: requireEntitlement is the only product hint — a value that is not an entitlement type name names the single product to offer — and a reader that cannot resolve it treats any purchase as satisfying the rule.

To show readers a name and price per product, describe the products in paywall.products. Exporters that also target players older than 1.6 should keep requireEntitlement set to the first listed product — the PanelWave CMS does this.

Products (PaywallProduct)

Since format 1.6.0, the optional paywall.products array describes the products and subscription tiers the rules sell, so readers can label each Buy / Subscribe option. Defined as $defs/PaywallProduct; additionalProperties: false.

PropertyTypeRequiredDescription
idIdentifierYesThe id rules use to reference it: an entry of requiredProductIds, a product-key requireEntitlement, or an entry of subscriptionTiers.
nameLocalizedStringNoReader-facing product name.
descriptionLocalizedStringNoReader-facing description.
price{ amount, currency }NoDisplay price: amount (number ≥ 0) and currency (ISO 4217 code), both required when price is present.
type"purchase" | "subscription"NoOne-time purchase or subscription tier. Informational — the referencing rule decides how the option is sold.

How readers use it: an option whose id has an entry takes the entry's name and description (in the reader's locale, falling back like any localized string) and its price. Without an entry, the option falls back to the rule's name (a single option) or the bare id (several options), and to the rule's description / price.

  • Entries are informational: the rules decide what unlocks what, and your checkout stays the authority on the charged price.
  • Entries no rule references are allowed and ignored.
  • Ids should be unique within the array. JSON Schema cannot enforce this on a property; the player uses the first entry for an id.

Examples

Gate one chapter after a free preview

{
  "paywall": {
    "rules": [
      {
        "id": "pw-ch2",
        "scope": "chapter",
        "refId": "ch-2",
        "requireEntitlement": "premium",
        "previewPanels": 3
      }
    ]
  }
}

Readers without the premium entitlement see the first three panels of chapter ch-2, then the paywall. Only ch-2 is gated — every other chapter stays free. (The player reads premium as a subscription requirement.)

Chapter unlocked by either of two products (1.6+)

{
  "paywall": {
    "products": [
      {
        "id": "premium-edition",
        "name": { "en-US": "Premium edition", "de-DE": "Premium-Ausgabe" },
        "price": { "amount": 4.99, "currency": "EUR" },
        "type": "purchase"
      },
      {
        "id": "ch8-single",
        "name": { "en-US": "Chapter 8 only", "de-DE": "Nur Kapitel 8" },
        "price": { "amount": 1.99, "currency": "EUR" },
        "type": "purchase"
      }
    ],
    "rules": [
      {
        "id": "pw-ch8",
        "scope": "chapter",
        "refId": "ch-8",
        "requireEntitlement": "premium-edition",
        "entitlementType": "purchase",
        "requiredProductIds": ["premium-edition", "ch8-single"],
        "name": "The finale",
        "price": { "amount": 2.99, "currency": "EUR" }
      }
    ]
  }
}

A reader who bought either the premium edition or the single chapter reads chapter ch-8; everyone else sees two Buy options — Premium edition €4.99 and Chapter 8 only €1.99 (in German: Premium-Ausgabe, Nur Kapitel 8). The manifest declares "version": "1.6.0" (or later) in its panelwave header.

Panel-level gating with an age gate

{
  "paywall": {
    "rules": [
      {
        "id": "pw-p2-3",
        "scope": "panel",
        "refId": "p2-3",
        "requireEntitlement": "premium",
        "previewPanels": 0,
        "ageGate": 16
      }
    ]
  }
}

Whole-work purchase

{
  "paywall": {
    "rules": [
      {
        "id": "pw-work",
        "scope": "work",
        "requireEntitlement": "purchase:work-city-noir",
        "previewPanels": 10
      }
    ]
  }
}