Features and plans
Manage the catalog from code. Most teams use the dashboard instead.
features.create
{
"id": "credits",
"name": "Credits",
"type": "credit_system",
"unit": "credits",
"credit_schema": [
{ "feature_id": "videos", "credits": 10 },
{ "feature_id": "scripts", "credits": 2 }
],
"credit_value_usd": 0.01,
"markup": 0.3
}typeismetered,credit_systemorboolean. Formetered,consumable: falsemakes a count that never resets, such as seats.event_nameson a metered feature, such as["message-sent"], lets onetrackwith thatevent_namedrain every feature that listens to it.credit_value_usdandmarkupon a credit system letbalances.track_tokensprice AI tokens. With a value of 0.01 and a markup of 0.3, a call that cost $0.50 spends 65 credits.- Ids are lowercase letters, numbers and underscores, and never change.
features.update, features.get, features.delete, features.list
features.update takes { "feature_id", ...fields }. features.delete removes a feature no plan or balance uses, and archives it otherwise. features.list and features.get work with a publishable key.
plans.create
{
"id": "pro",
"name": "Pro",
"interval": "month",
"group": "main",
"prices": [{ "currency": "USD", "amount": 2000 }, { "currency": "GHS", "amount": 30000 }],
"trial_days": 7,
"trial_card_required": true,
"items": [
{ "feature_id": "messages", "included": 100, "interval": "month", "rollover": { "max": 50, "duration_cycles": 1 } },
{ "feature_id": "api_calls", "included": 1000, "usage_model": "usage_based", "unit_prices": [{ "currency": "USD", "amount": 100 }], "billing_units": 1000, "max_purchase": 5000 },
{ "feature_id": "seats", "included": 3, "usage_model": "usage_based", "unit_prices": [{ "currency": "USD", "amount": 500 }], "proration": "prorate_immediately" },
{ "feature_id": "sso" }
],
"billing_controls": { "usage_limits": [{ "feature_id": "messages", "limit": 200, "interval": "day" }] }
}Amounts are minor units: 2000 is $20.00 and 30000 is GHS 300.00.
| Plan field | Meaning |
|---|---|
interval | Billing interval: week, month, quarter, semi_annual, year or one_off |
group | Plans in a group replace each other. Add ons and other groups stack |
is_default (or auto_enable) | Free plans given to every new customer |
is_add_on | Bought on top of a main plan |
trial_days, trial_card_required | A free trial, once per customer. With a card required, the card is checked with a small charge refunded at once and the plan bills when the trial ends |
billing_controls | Defaults for every customer on the plan. A rule on the customer replaces the plan's for the same feature |
| Item field | Meaning |
|---|---|
included | Amount granted. unlimited: true never blocks |
interval, interval_count | How often it resets: hour to year, or none. interval_count: 4 with hour resets every 4 hours |
usage_model | included (default), prepaid (bought in packs with feature_quantities at attach) or usage_based (pay as you go, billed at the end of the period) |
unit_prices, billing_units | Price per pack, such as USD 1.00 per 1,000 calls |
tiers, tier_behavior | [{ "up_to": 1000, "unit_prices": [...] }, { "up_to": null, ... }]. graduated prices each tier on its own; volume prices everything at the tier the total lands in |
max_purchase | The most a customer can use or buy, counting the included amount |
reset_usage_when_enabled | true (default) gives the full amount when the plan starts. false keeps usage from the plan before |
rollover | { "max": 50, "duration_cycles": 1 } carries unused amounts into the next periods, up to max |
proration | For counts that never reset, such as seats: prorate_immediately charges extras for the days left, prorate_next_cycle and none wait for renewal |
plans.update
{ "plan_id": "pro", ...fields }. Changing prices or items on a plan with customers creates a new version and answers new_version_created: true. Current customers keep their version, price included, until you migrate them. Send "disable_version": true to change the plan in place.
plans.versions and plans.migrate
plans.versions with { "plan_id": "pro" } lists every version with its subscriber count. plans.migrate with { "plan_id": "pro", "from_version"?: 1, "to_version"?: 2, "customer_ids"?: [...] } moves subscribers. Their grants change now and usage carries over; the new price applies from their next renewal.
plans.list, plans.get, plans.delete
plans.list and plans.get work with a publishable key, so a pricing page can read them from the browser. Pass customer_id with a secret key to get eligibility. plans.delete removes a plan nobody ever had and archives it otherwise.