Skip to main content
A plan is pricing you attach to a product. It is a single type with an ordered options[] list. What a plan “is” — recurring, usage-based, hybrid, or one-time — is a derived label, not a field you send. Console templates (Recurring, Usage-based, One-time, Hybrid, Blank) only seed that list. They are not persisted types. Money on options is integer minor units of the option’s currency (cents for USD). Usage rates in the console are money per meter noun ($0.02 / request). Credits are only the prepaid wallet — see Plans and billing glossary.

How a plan is stored

Plans live on the product. There is no separate plans collection, no plan type discriminator, and no subtype fields such as setupFee, creditsPerUnit, basePrice, or freeUnits. planType on some responses is a back-compat label for analytics and UI. Do not send type when you create or update a plan.

Option catalog

Each option is discriminated on kind. Optional label is allowed on every kind.

charge

A price. per is flat, unit, or seat. Per-unit charges require meter. A plan offers several currencies by holding multiple charges of the same role that differ only by currency. Amounts are set per currency, not FX-converted.

billingCycle

Makes the plan recurring. Omit it for one-time or pay-as-you-go usage.

tier

Volume or graduated rates for a meter. The embedded charge is per-unit. One option per band — see Tiered for a worked example and for how the two modes differ.

limit

Included cap for a meter. Copy says included. cap: 0 means unlimited. A limit needs a billingCycle. Pay-as-you-go (no cycle) cannot carry included usage. A missing limit means every counted item is paid from prepaid credits. It is not unlimited. onExceed is what happens after the included count:
  • block stops access. The denial offers the product’s other plans, and checkout opens on the plan the customer picks
  • draw_credits draws prepaid credits for usage past the cap, and refuses the next request when the balance cannot cover one unit
Usage is never billed to the card. The API still accepts onExceed: "top_up" and "charge" and stores both as draw_credits.

entitlement

A feature flag or numeric benefit on the plan. The console label is Benefit. The wire kind stays entitlement.

trial

convert moves the customer onto the plan’s paid terms. cancel ends access. A reverse trial is cancel plus an auto-assigned free plan on the product, so the customer stays on the free tier when the trial ends.

discount

Markers

Markers are catalog metadata. They do not change pricing math.

Common compositions

These labels are derived. Send the options, not "type": "…".

Recurring — flat charge + billing cycle

A $0 flat charge plus autoAssigned is a free recurring fallback.

Usage-based — per-unit charge, no billing cycle

amountMinor: 2 is $0.02 per request. This plan has no billing cycle and no included cap. Do not send creditsPerUnit as a plan field. Minimum top-up is a provider setting (minTopUpMinor in SolvaPay Console settings), not a plan option. See Auto-recharge.

Tiered — banded rates for one meter

A tier option is one band. Send one per band, contiguous and covering the whole range: each band’s from is the previous band’s to, the first starts at 0, and exactly one band — the last — sets to: null for everything above it.
Every band in a group shares one mode, and the two modes price the same usage differently. For 1,500 requests against the plan above: Group bands by meter. A plan can tier two meters by sending two groups, each contiguous in its own charge.meter. Bands are grouped by that meter, never by their position in options[]. A meter is priced by bands or by a charge with per: "unit" — not both. Both are per-request prices on the same requests, so sending both is rejected. Bands and limit compose: the cap is the included count, and the bands price the overage above it. A plan with "cap": 1000 and the bands above includes the first 1,000 requests and prices request 1,001 onward from the band it lands in. Set onExceed: "draw_credits" so usage past the included count is paid from prepaid credits rather than blocked. Tiered usage is paid from the credit balance as each request is served, on a recurring plan and on a plan with no billing cycle alike.

Hybrid — recurring fee plus metered usage

Bands replace the flat per-unit charge rather than joining it — a meter is priced one way or the other. The derived label is still hybrid when a billing cycle and a meter are both present.

One-time — flat charge, no cycle

Identity fields

These sit on the plan next to options[]:

Hidden and auto-assigned plans

Add { "kind": "hidden" } to keep a plan off checkout, the SDK catalog, and MCP. Customers cannot self-select it. Assign it from the SolvaPay Console customer page. Direct assignment stamps the purchase with admin_assignment. Add { "kind": "autoAssigned" } to make the plan the product fallback (MCP tool access when no tool-specific plan is set, and new-customer pre-selection). Setting a new auto-assigned plan clears the marker on the previous one.

Plans are embedded in products

A product owns its plans. Create, update, and delete them through the product. Plan references are globally unique.

Plan API

All plan endpoints are scoped to a product. Send options[]. Do not send type, creditsPerUnit, basePrice, setupFee, or freeUnits. PUT replaces the full options[] when you send it.

Plans and MCP servers

  • Server default plan — defaultPlanRef on an MCP server uses the product’s auto-assigned plan when no tool-specific plan is set
  • Per-tool plan assignments — tools can reference plans via planIds
When plans change, MCP server configurations sync automatically.

Purchase snapshots

A purchase freezes the plan’s options[] at buy time. Changing the live plan does not change existing purchases.
price and planType on the snapshot are derived from those options.

Next steps