Pricing Plans
Pricing plans describe what your app charges, on the app itself. Plans render on your marketplace listing (the pricing section and the "From $X/mo" summary), and the Billing API can price charges from them, so what sellers see on the listing and what you charge never drift apart.
Plans are managed from the Partners dashboard under Apps > your app > Pricing.
Plan fields
| Field | Description |
|---|---|
handle | Unique identifier per app, referenced by the Billing API. Lowercase letters, numbers, and hyphens. Generated from the name, editable. |
name | Display name on the listing and the seller's confirmation page. |
type | recurring or onetime. |
price | Price in USD. min:0.00, max:1000.00. |
interval | Recurring plans only: 30 (monthly) or 365 (yearly). |
trial_days | Recurring plans only: free trial days, min:0, max:90. |
features | Feature bullets shown on the listing's pricing cards. Max 10. |
is_free | Marks a free tier. Price is forced to zero. |
is_visible | Hidden plans do not render on the listing but stay available to the Billing API by handle, which is how custom per-seller deals work. |
An app can have at most 12 plans.
Charging with a plan handle
Pass the plan's handle when creating a charge and the charge is priced from the catalog. Price, interval, and trial come from the plan, so a price change in the dashboard applies to new charges without an app deploy.
// POST /billing/apps/charges/recurring
{
"name": "Pro",
"return_url": "https://myapp.com/billing/success",
"plans": [{ "type": "time_based", "handle": "pro" }]
}// POST /billing/apps/charges/onetime
{
"name": "Lifetime unlock",
"return_url": "https://myapp.com/billing/success",
"plan_handle": "lifetime"
}An unknown handle, or a handle whose plan type does not match the endpoint, returns 422.
trial_days on the request overrides the plan's trial when present.
Raw charges still work
The Billing API is unchanged for existing integrations: raw amounts stay first-class, and nothing requires a catalog. A raw charge that matches a catalog plan (same amount, interval, and trial days) is linked to that plan automatically, and charges created before the catalog existed link up when you add a matching plan later. Linked charges carry the plan's id as plan_id in API responses.
Raw amounts remain the right tool for genuinely dynamic pricing, usage-style amounts, or one-off negotiated charges. For the charges you make repeatedly, your standard tiers, create plans first and reference them by handle:
- Your listing advertises exactly what you charge, and stays correct when prices change.
- Price changes happen in the dashboard and apply to new charges without an app deploy.
- Charges resolve to a plan, so you can tell which tier a store is on.
TIP
Keep listing and reality in sync: reference plans by handle from your app, and treat the dashboard as the single place where prices change.