Revvly
Docs
Guide

One component. Four scopes.

Variable Components describe the shape of pricing. Variable Component Prices ship the numbers. The same component can carry different numbers at the Subscription, Bundle, Module, and Global default scopes — Revvly walks them at billing time and picks the first match.

The resolution order

From most specific to least specific.

When a subscription is being billed, Revvly looks at every Variable Component on the subscription and resolves a price for each one. For each component, the resolver walks the scopes in this order until it finds a published row with a matching currency:

  1. 1
    Subscription scope
    A Variable Component Price targeting this exact subscription. Rarely used — Custom Pricing Agreements are usually a better fit.
  2. 2
    Bundle scope
    A price targeting the subscription’s pinned (Bundle id, Bundle version). Only applies if the subscription is bundle-based.
  3. 3
    Module scope
    A price targeting any (Module id, Module version) that the subscription pins — directly or through its bundle.
  4. 4
    Global default
    The fallback price ladder for the component. Required: every component needs at least one Global default published.

Currency selection. At each scope, Revvly looks for a row whose pricesByCurrency list contains the subscription’s currency. A Bundle-scope price with only USD won’t apply to an EUR subscription — the resolver keeps walking. This means you can ship a Bundle override in only the currencies where you actually want it, and fall through to Global for the rest.

Shape matching. A scope replaces the resolved price rather than merging into it, so every row has to carry the monetary fields its component’s model bills on — one rate per tier for a volume or graduated tiered component, one flat fee per step for a stairstep one, both halves of the committed and overage pair. A row carrying a single rate for a tiered component is rejected when you save it, at every scope, because it could never produce a charge. See overriding component pricing for the field-by-field breakdown.

Custom Pricing Agreements sit above all of this. A Published Custom Pricing Agreement for the same (customer, component) wins even over a Subscription scope. See the private-deal guide for the full flow.

Price resolution order
Most specific first
  1. Custom Pricing Agreement
    Published, for this customer and component.
  2. Subscription scope
    A price on this exact subscription.
  3. Bundle scope
    The pinned bundle and version.
  4. Module scope
    A pinned module and version, directly or through the bundle.
  5. Global default
    Required fallback for every component.
Revvly stops at the first published row that matches the subscription's currency.

The four scopes, with examples

01Global default

When to reach for it. Every component needs at least one Global default — the fallback used when no more-specific scope applies.

A Global default Variable Component Price ships the canonical numbers for a component, per currency. Most components will only ever need this one row.

Seats v2 → Global default → { USD: $5, EUR: €4.50, GBP: £4 }

02Module override

When to reach for it. Use when a specific module bundles the component at a different rate than the global default — for example, the “Analytics Module” gives a built-in seat discount.

Module-scoped prices target a single (Module id, Module version) pair. They apply to any subscription whose pinned module composition includes that exact module version.

Seats v2 → Module: Analytics v3 → { USD: $4 } (seats inside Analytics are cheaper)

03Bundle override

When to reach for it. Use to ship plan-level pricing — the Pro Bundle includes seats at one rate, Enterprise at another.

Bundle-scoped prices target a single (Bundle id, Bundle version) pair and beat module-scoped prices for subscriptions on that bundle.

Seats v2 → Bundle: Enterprise v1 → { USD: $3, EUR: €2.80 }

04Subscription override

When to reach for it. Use for one-off deals you don’t want as a Custom Pricing Agreement — typically while migrating a single customer or testing a new rate.

Subscription-scoped prices target a single subscription. They are the most specific scope and beat every other scope for that subscription only — which is why they must carry the component’s whole price, not just the figure being changed. If you need a richer override (different model, custom tiers, multi-axis change), prefer a Custom Pricing Agreement.

Seats v2 (flat) → Subscription: Acme’s prod sub → { USD: $2.50 }
Events v3 (3 tiers) → Subscription: Acme’s prod sub → { USD: [$0.010, $0.008, $0.005] }

Why scopes, not Pricing Rules?

The original Revvly pricing model used Pricing Rules with a single price per Bundle × Region × Period. Scope-based component pricing replaces that with a layered model so you can express the same intent more locally: a regional override is just a Bundle-scope price in that region’s currency, an enterprise rate is a Bundle-scope price on the Enterprise bundle, a one-off discount is a Subscription scope or a Custom Pricing Agreement. The pricing engine still consults Pricing Rules for legacy subscriptions and for the bundle-pricing calculator, but new work should reach for the component model.

Next up