Create or Update Plan
apply_planSet up or reshape ONE subscription plan in a single call: its name, description, trial, prices, features and quotas together. Creates the plan when the key is new and updates it when it exists, in one write, so a plan is never left half-configured. A new plan needs name, at least one price and, when it charges, trial (true or false); a price above 0 needs currency unless the plan's prices already use exactly one. These are the developer's decisions: when one is missing the tool changes nothing and returns DECISION_NEEDED listing the questions to ask. prices ([{ amount, interval, currency? }]) are upserted by currency + interval and quotas ([{ metric, limit, policy, kind?, source?, priceAmount?, priceCurrency? }]) by metric, with the same rules as set_plan_price and set_plan_quota; features ([{ key, name? }], the on/off features the plan includes) are upserted by key; removePrices ([{ interval, currency? }]), removeQuotas ([metric]) and removeFeatures ([key]) remove the named ones. Anything the call does not mention is kept. Returns the full plan. The CLI's bridge plan apply --spec takes the same JSON. Call list_plan_quotas without a planKey first to reuse the metric names the app already has instead of inventing new ones. Seats: name the limit (e.g. seats), kind gauge, source membership; Bridge counts the workspace's active members, pending invites included. Enforce it where invites happen: the built-in team page with its seat-limit setting, or @RequireQuota on the app's own invite handler. Never a flag or an entitlement. Count usage once, where the action happens: ask the developer whether the action calls their server. If it does, the backend decorator (@RequireQuota) counts it. If it does not, the browser reports it (bridge.usage.report / set) with on the button; that is a complete, first-class setup. Never count one metric in both places. Before writing app code that consumes this, call get_integration_guide with topic=billing and the framework the app uses.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| app | No | Id of the Bridge app to act on for this call (see list_apps). Omit to use the current app. | |
| key | Yes | Plan KEY. A new key creates the plan; an existing one reshapes it. | |
| name | No | Display name. Required when the plan is new. | |
| trial | No | Whether the plan has a trial period. false removes one. | |
| prices | No | Prices to add or replace, one per currency + interval. A new plan needs at least one. | |
| quotas | No | Quotas to add or replace, one per metric. kind: counter (default) or gauge. If deleting it frees room, it's a gauge and your app counts it; if it happened, it's a counter and Bridge counts it. source: "membership" on a hard gauge makes Bridge count the workspace's active members (seats). | |
| features | No | On/off features the plan includes, added or renamed by key. A plan feature is an on/off feature the plan sells; gate it with a feature flag whose rule is `bridge:billing.entitlement.<featureKey>` eq true, so the flag follows the plan's feature list and never names plans. | |
| trialDays | No | Trial length in days. Required when trial is true. | |
| description | No | Short description shown with the plan. | |
| removePrices | No | Prices to remove, by interval + currency (default: the plan's one currency). | |
| removeQuotas | No | Metric keys whose quotas to remove. | |
| removeFeatures | No | Feature keys to remove from the plan. |