Farther ShoreDocs
Go to Farther Shore
What is FartherShore
Install the CLI
Quickstart
Core concepts
The @Product class
Meters & resources
Features & routes
Capabilities & entitlements
Plans & pricing
The Manifest IR
Bring your own backend
Transport modes
Metering & verification
Runtime tokens
Frontend SDK
Root & data components
Auth & sessions
Entitlement gates
Connect Stripe
Subscriptions & usage
Plan changes & grandfathering
Billing strategies
1. Flat subscription2. Usage-based (pay as you go)3. Subscription + overage4. Prepaid creditsMixing strategies on a ladderRelated
Apply & deploy
Environments
Migrations
Docs versions & archive
Operate with an agent
Operation classes
MCP server
End-to-end via CLI/MCP
CLI reference
@farthershore/product
@farthershore/backend
@farthershore/farthershore-js
Environment variables
Response & deny codes
Add a metered capability
Gate a feature
Change a price
Prepaid credits
Meter AI tokens
Operate via an agent
Prepare for launch
Status
Docs/Monetize/Billing strategies

Billing strategies

Subscription, usage, overage, and prepaid-credit shapes — each as a @Plan.

PreviousPlan changes & grandfatheringNextApply & deploy

There is no "billing model" setting to pick. A pricing strategy is just the shape of a @Plan — combine a recurring price, per-unit meter overage, a spend cap, and credit grants, and you get flat subscriptions, usage-based, hybrid, or prepaid. Below are the four canonical shapes, each as the exact @Plan that expresses it. They all sit on the same CronCloud product, so you can mix them across a plan ladder.

All money is integer-only: price.amount and maxMonthlySpendCents are cents; meter micros is micro-dollars (1,000,000 µ = $1.00); credit amount_cents is cents.

1. Flat subscription

A fixed recurring fee. Capacity is bounded by rate limits; usage is not billed per unit. The simplest shape — and the floor every plan needs (at least one limit).

@Plan("starter", {
  name: "Starter",
  price: { amount: 2900, currency: "usd", interval: "month" }, // $29/mo flat
  limits: {
    requests: { rate: 600, interval: "minute", enforcement: "enforce" },
  },
  // Bound a counted resource, e.g. 10 cron jobs, via a capability grant:
  grants: [capabilityGrant("managed-cron", { limits: { cron_jobs: 10 } })],
})
starter!: unknown;

Over-limit calls are rejected by the gateway with 429 (rate_limited) because enforcement: "enforce". No metered charges ever appear on the invoice.

2. Usage-based (pay as you go)

No (or minimal) recurring fee; the customer pays per unit of a metered dimension. Declare the @Meter, report real usage from a route, and price it with meter. includedUnits gives a free allotment before billing starts.

@Meter("tokens_used", { unit: "token", estimate: 500 })
tokensUsed!: unknown;

@Feature("runs", {
  routes: { "POST /v1/chat": { reports: "tokens_used" } }, // upstream reports actual
})
runs!: unknown;

@Plan("payg", {
  name: "Pay as you go",
  price: { free: true }, // no base fee
  limits: { requests: { rate: 600, interval: "minute", enforcement: "enforce" } },
  // $0.000002/token after the first 100,000 free tokens each period:
  meter: { tokens_used: { micros: 2, includedUnits: 100_000 } },
})
payg!: unknown;

estimate: 500 on the meter is the admission estimate — what the gateway reserves before the upstream reports the true tokens_used. A reported meter needs an estimate, or compilation fails.

3. Subscription + overage

A base fee that includes an allotment, then per-unit billing beyond it — the common SaaS hybrid. Add a spend cap so a runaway month can't surprise the customer.

@Plan("pro", {
  name: "Pro",
  price: { amount: 19900, currency: "usd", interval: "month" }, // $199 base
  limits: { requests: { rate: 6000, interval: "minute", enforcement: "enforce" } },
  // 1M tokens included in the base fee, then $0.000002 each:
  meter: { tokens_used: { micros: 2, includedUnits: 1_000_000 } },
  maxMonthlySpendCents: 50_000,        // cap overage at $500/mo
  overageBehavior: "allow_and_bill",   // let it through and charge, up to the cap
})
pro!: unknown;

Set overageBehavior: "block" instead to deny calls past the included allotment (a hard ceiling, no surprise bill). Use minMonthlySpendCents to invoice a floor for committed-spend deals.

4. Prepaid credits

The customer holds a credit balance that usage draws down; you grant credits with the subscription and/or sell top-up packs. Credit grants are authored on the plan via grants:

@Plan("credits", {
  name: "Credits",
  price: { amount: 2000, currency: "usd", interval: "month" },
  limits: { requests: { rate: 600, interval: "minute", enforcement: "enforce" } },
  grants: [
    // $20 of credit granted every period (recurs); usage burns it down:
    { kind: "credit", amount_cents: 2000, recurs: true },
    // Purchasable pack (sku + label required): pay $50, get $60 of credit:
    {
      kind: "top_up",
      sku: "credit-50",
      label: "$50 credit pack",
      price_cents: 5000,
      credit_cents: 6000,
    },
  ],
  meter: { tokens_used: { micros: 2 } }, // priced against the balance
})
credits!: unknown;

Credit-policy knobs (rollover, auto-recharge) ride through the plan's raw escape hatch, mirroring the platform creditPolicy schema:

@Plan("credits", {
  name: "Credits",
  price: { amount: 2000, currency: "usd", interval: "month" },
  limits: { requests: { rate: 600, interval: "minute", enforcement: "enforce" } },
  grants: [{ kind: "credit", amount_cents: 2000, recurs: true }],
  raw: {
    creditPolicy: {
      rollover: { percent: 50 },                                   // carry 50% of unused
      auto_recharge: { threshold_cents: 500, refill_cents: 2000 }, // refill $20 at $5 left
    },
  },
})
credits!: unknown;
  • recurs: true — granted every period (free-then-bill). recurs: false (default) grants once at subscription start (a signup credit).
  • label (+ optional expires_after_days) marks a campaign/promotional grant.
  • When credit runs out, billable calls are denied with 402 (credit exhausted) until the balance is topped up or refilled.

On the customer side, fs.billing.creditBalance() reads the balance and fs.billing.createTopUpCheckout({ amountCents }) starts a top-up; the zero-prop <CreditBalance/> and <TopUpPrompt/> components render both.

Mixing strategies on a ladder

A real product usually stacks these: a free plan, a flat Starter, a Pro with overage, and a credit-funded tier — all on one @Product class. Because each is just a @Plan shape, you compose them freely; the gateway enforces limits and spend caps per subscriber's active plan, and Stripe handles the invoicing.

Related

  • Subscriptions & usage — limits, metering, and spend caps in depth.
  • Plan changes & grandfathering — reprice these safely.
  • Connect Stripe — connect payments before publishing.