quotastack Docs
Docs / Concepts / Plans & Variants

Plans & Variants

How a sellable plan is put together — variants, credit grants, and what retiring one actually does.

Mental model — A plan is the name on the box; a variant is one way to buy what is inside. Customers never subscribe to a plan — they subscribe to a variant, and every commercial term they get (cycle, trial, credits, entitlements) hangs off that variant.

Quick take

  • Customers subscribe to a variant, never to a plan
  • The variant carries billing cycle, billing mode, trial days, and the overdue settings
  • Credit grants and entitlements attach to the variant
  • grant_interval takes a keyword or an ISO 8601 duration like PT5H — minimum 5 minutes
  • Retiring stops new subscriptions; existing ones keep running untouched
SELLABLE Plan has many PRICED TIER Plan variant attaches attaches GRANTS Credit grant template ALLOWS Entitlement overridden by ONE CUSTOMER Subscription override A subscription points at a VARIANT, not at the plan

Plans & Variants

A plan is the thing you sell. A variant is one way to buy it. Everything a customer actually gets — credits, trial length, billing cycle — hangs off the variant, not the plan.

The shape

Plan  "Pro"
 ├── Variant  monthly, prepaid, 14-day trial
 │    ├── Credit grant   50,000 credits every billing_cycle
 │    └── Entitlements   seats: 10, api_access: true
 └── Variant  yearly, prepaid, 14-day trial
      ├── Credit grant   700,000 credits every billing_cycle
      └── Entitlements   seats: 25, api_access: true

Customers subscribe to a variant, never to a plan. The plan is the name on the box.

The plan

A plan holds very little: a name, a status, and whether it is your default.

FieldMeaning
nameWhat you call it.
statusactive or retired.
is_defaultThe plan new customers land on when you have not chosen one.

You cannot retire your default plan. Make another plan the default first, then retire the old one.

The variant

The variant carries the commercial terms.

FieldMeaning
billing_cycledaily, weekly, monthly, yearly, or one_time.
billing_modeprepaid (credits up front) or postpaid (usage billed after).
trial_daysDays of trial. Above zero, a new subscription starts in trialing.
renewal_due_daysHow long a renewal can sit unpaid before the subscription goes overdue.
grace_period_daysHow long an overdue subscription keeps working.
allow_usage_while_overdueWhether an overdue customer can keep spending.
statusactive or retired.

Credit grants

A grant is a standing instruction: give this customer this many credits, this often.

FieldMeaning
creditsMillicredits per grant.
grant_intervalHow often. See below.
grant_typerecurring, one_time, or trial.
expires_after_secondsHow long each granted block lasts. Empty means it never expires.
accumulation_capThe most that can pile up unspent.
rollover_percentageHow much unspent credit carries into the next period.
max_rollover_cyclesHow many periods credit can keep rolling.

Grant intervals

grant_interval takes one of five keywords:

  • daily
  • weekly
  • monthly
  • billing_cycle — grant on each renewal, matching the variant’s cycle
  • on_activation — grant once, when the subscription starts

Or it takes an ISO 8601 duration, which is any value starting with P:

ValueMeans
PT5HEvery 5 hours
PT4HEvery 4 hours
P3DEvery 3 days
P1WEvery week

Custom intervals exist for products that refill on a clock rather than a calendar — a five-hour usage window, for example. The smallest allowed custom interval is 5 minutes. Anything shorter is rejected, because it would turn the scheduler into a hot loop.

Anything that does not start with P and is not one of the five keywords is rejected.

Entitlements

Grants give credits. Entitlements give everything else: seat counts, feature flags, fixed limits. They attach to the variant too.

That is its own topic — see Plan-Variant Entitlements.

Retiring a plan or a variant

Set status to retired. Here is exactly what that does, and what it does not do.

It stops new subscriptions. Subscribing to a retired variant is refused. This is the point of retiring: the plan is no longer for sale.

It does not touch existing subscriptions. Customers already on that variant keep their subscription, keep their grants, and keep renewing. Nothing is cancelled and nothing is migrated. If you want existing customers moved, move them yourself with an upgrade or downgrade.

It does not delete anything. The plan, its variants, and its grants all still exist. Retiring is a visibility change, not a deletion.

The catalog is shared across environments

This is the part that catches people. Plans, variants, and grants have no environment. There is one copy, and both sandbox and live read it.

Retire a plan while testing in sandbox and you have retired it in live. Change a grant amount in sandbox and live customers get the new amount on their next renewal.

Read Environments & the Shared Catalog before you edit a plan you did not create today.

Building one

The order matters, because each step attaches to the one before it.

  1. Create the plan.
  2. Add a variant to the plan.
  3. Add a credit grant to the variant.
  4. Add entitlements to the variant.
  5. Subscribe a customer to the variant.

The dashboard walks this in one flow under Plans. Every step is also a single API call if you would rather script it.

Common mistakes

Don't expect retiring a plan to move or cancel its subscribers

Retiring only stops new subscriptions. Existing customers keep their subscription and keep renewing. Migrate them yourself with an upgrade or downgrade.

Don't edit a plan in sandbox to see what happens

The catalog is shared. There is one copy of every plan, variant, and grant, and live reads the same row you just changed.

Don't use a custom grant interval shorter than 5 minutes

It is rejected. Anything faster would turn the grant scheduler into a hot loop.

Don't try to retire your default plan

It is refused. Make another plan the default first, then retire the old one.