Farther ShoreDocs
Go to Farther Shore
Design and operate commerce
Entitlements vs economics
Cohorts & releases
Plan transitions
Connect Stripe
Subscriptions & usage
Define the contractThe pipelineWhat a subscription pinsSubscription lifecycleWhat the subscriber seesLimits are not invoicesCancellation and renewal
Plan changes
Billing strategies
Pricing catalogs
Funding & allowances
Economic agreements
Commercial releases
Bill preview API
Usage & billing policy
Ledger & settlement
Subscription + overage
Freemium that converts
Add a trial
Add a spend cap
Change a price
Prepaid wallet
Meter AI tokens
commerce HTTP contracts
Status
Docs/Monetize/Subscriptions & usage

Subscriptions & usage

How a subscription pins its plan, how measurements become charges, and how funding and settlement close the loop.

A subscription assigns one subscriber organization to one compiled plan inside one commercial release. That assignment drives gateway grants and limits, selects the rating context that prices the subscriber's measurements, and issues the plan's funding buckets.

Define the contract

ts
import * as fs from "@farthershore/business";

const requests = fs.requests();
const units = fs.measure("units");
const usage = fs.meter("api_usage", { measures: [units] });
const usagePricing = fs.pricing("api_usage", {
  meter: usage,
  catalog: [fs.rate.perUnit(fs.money.usd(0.01))],
});
const api = fs.backend("api", {
  transport: { mode: "direct" },
  default: true,
});
const account = fs.route("/v1/account", {
  get: { backend: api, costs: [requests.fixed(1)], reports: [usage] },
});
fs.meterRoutes("account-usage", account, { reports: [usage] });

fs.plan("pro", {
  kind: fs.plan.kind.hybrid,
  price: fs.money.usd(29).monthly(),
  usagePricing: usagePricing.current(),
  funding: { buckets: [fs.included(fs.money.usd(5))] },
  spendPolicy: {
    onExhaustion: fs.exhaustion.overage(usagePricing.current()),
  },
  grants: [account],
  limits: [requests.perMinute(120)],
});

export default fs.business();

The grant authorizes the route. The meterRoutes binding says the backend reports api_usage there. The catalog prices each unit. The included bucket pays for the first $5 of rated usage each period; overage is rated at the same catalog and settled on the invoice. The recurring fee charges independently of usage.

The pipeline

Measurement → Rating context → RatedCharge → Funding → Ledger → Settlement
  1. Measurement. The backend reports facts (values, dims) through report(). The gateway stamps the served identity — subscription, release, rating context — inside the signed usage event.
  2. Rating context. Core resolves the plan's usagePricing binding to an immutable rating context: catalog version, selector match sets, contract modifiers, tier semantics, exact rational rates.
  3. RatedCharge. The rating engine computes an exact nanodollar charge per measurement. Rating is order-insensitive; a deterministic re-rating pass at window close handles cumulative tiers and late events.
  4. Funding. Eligible buckets pay the charge in a fixed total order (priority, soonest expiry, promo < referral < included < prepaid, bucket id). The remainder becomes amount due.
  5. Ledger. Every step is a balanced posting; bucket balances are a view of the ledger and are reconciled against it.
  6. Settlement. Stripe collects amount due and recurring fees. Stripe never rates usage or owns a balance.

Read Ledger & settlement for the operator view.

What a subscription pins

A subscription holds two pins:

  • the recurring-price pin — the fee promised when it was created; a later release never changes it silently;
  • the usage-pricing binding — normally pricing.current(), so metered rates track the live catalog automatically. withContractTerms() also tracks activated catalog releases while preserving the subject's agreement terms; those terms change only through an agreement amendment. fixedVersion(n) never moves.

Publishing a release with a new recurring price affects new subscriptions. Moving an existing subscriber to the latest plan is a deferred post-launch operation.

Subscription lifecycle

Checkout creates settlement intent, but access changes only when the platform records the authoritative lifecycle transition. Payment events can arrive later, repeat, or be delivered out of order; the control plane deduplicates them by event id and applies them to durable subscription state.

Render pending, active, past-due, trial, scheduled-transition, and canceled states rather than assuming checkout is synchronous. A trial (lifecycle: { trialDays }) suppresses obligation while it runs; rating still records the served context.

What the subscriber sees

The only money surface for subscribers is the bill preview API, which runs the same rating engine and ledger reads as invoicing — preview equals invoice by construction. Plans with fs.disclosure.opaque return allowance-remaining only; no rate can be recovered from the response.

Limits are not invoices

The edge may deny use because of rate, capacity, resource, or funding state (credit_exhausted on a block plan). An invoice can still include already accepted usage. Conversely, successful payment does not bypass a route or member permission deny. Handle the stable deny envelope in the client rather than inferring state from Stripe.

Cancellation and renewal

Period-end cancellation keeps the current compiled entitlements until the effective boundary. Included buckets are reissued each period by the plan's posting template; unused included value expires as a contra reversal, unused prepaid value is the subscriber's until it expires or is refunded per the bucket's rules. Delayed settlement events resolve against the release the usage was admitted under.

Use subscription state and transition timestamps from Farther Shore for UI; Stripe ids are implementation details.

PreviousConnect StripeNextPlan changes

On this page

Define the contractThe pipelineWhat a subscription pinsSubscription lifecycleWhat the subscriber seesLimits are not invoicesCancellation and renewal