curl --request GET \
--url https://api.botsubscription.com/v1/endpoint \
--header 'Authorization: Bearer sk_live_•••'Update Project Plan - Modify Subscription Settings via API
Modify an existing subscription plan's settings. Change the name, adjust pricing, update the billing cycle, or toggle payment options. Omitted top-level fields remain unchanged. When supplying plan_data, send both billing flags explicitly as described below.
Authorization
API key access: Unrestricted project API key required. Restricted keys cannot call this endpoint.
Path Parameters
project_idstringrequiredThe unique identifier of the project.
plan_idstringrequiredThe unique identifier of the plan to update.
Request Body
plan_namestringDisplay name (3–50 characters). Supports letters in any language, numbers, spaces, hyphens, underscores, parentheses, square brackets, dot, comma, percent, plus, supported currency symbols and emojis.
plan_pricestring|numberNonnegative price within the resulting currency's decimal limit. Prefer a decimal string such as 1.25; JSON numbers must also satisfy the numeric precision limits.
plan_currencystringA supported currency code (e.g., USD, EUR).
plan_durationstringDuration of the plan (e.g., '1 hour', '1 day', '1 month').
plan_price_formattedstringFormatted price display string (e.g., '$10.00').
plan_targetsarrayTarget selections, for example [{"kind":"role","external_id":"123456789012345678"}]. Each kind is role, channel or group; external_id is the platform numeric ID as a string.
plan_dataobjectAdditional plan configuration options.
plan_data.plan_recurringbooleanWhether the plan supports recurring payments.
plan_data.plan_one_timebooleanWhether the plan supports one-time payments.
plan_data.plan_trialstringTrial duration, for example '1 day'. Use a positive relative duration for a trial, not an absolute date or lifetime '0'.
All listed top-level fields are optional; unknown top-level fields are rejected. Recognized plan_data properties merge with existing properties, but a supplied plan_data object defaults omitted plan_recurring to false and omitted plan_one_time to true. Include both flags when changing trial configuration.
For example, {"plan_data":{"plan_recurring":false,"plan_one_time":true,"plan_trial":"1 day"}} switches the plan to a nonrecurring trial. This changes configuration, not trial history: a customer with a completed checkout on the same plan does not become eligible again. See Create a Trial Plan and Automatic Trial Checkout.
Prices follow the decimal input rules and the resulting currency's precision. USD allows 2 decimal places, JPY 0, BTC 8, and ETH 18; trailing zeroes do not add precision. Changing only plan_currency validates the stored price against the new currency. For example, switching a plan priced at "10.01" to JPY returns 422 VALIDATION_ERROR.
An unchanged stored price can be retained even if it exceeds the currency's decimal limit. Unrelated edits do not require a price correction. Resending an unchanged price and currency must still satisfy the input schema. A changed price or currency must satisfy the resulting currency limit.
Recurring plans require a finite duration; lifetime "0" is not supported for recurring billing. plan_price_formatted is a separate display string; "1" is valid without a currency symbol.
A universal plan can return expanded applicable targets with is_universal: true; a populated plan_targets array does not make it a restricted plan.
Add or remove this plan's targets
plan_targets replaces the plan's explicit target selection. To add a target, send the existing selections plus the new one. To remove a target, send the selections you want to keep. Omitting plan_targets leaves the selection unchanged.
For example, this request selects a Telegram channel and group using their platform IDs as strings:
{
"plan_targets": [
{ "kind": "channel", "external_id": "-1001234567890" },
{ "kind": "group", "external_id": "-1002345678901" }
]
}To remove the channel from this plan, send only the group entry. Other plans using that channel keep their selections. Read the plan back to confirm is_universal: false and the expanded target objects.
Sending {"plan_targets":[]} removes the explicit restriction and makes the plan universal across applicable project targets. It does not disable access. The response uses is_universal: true and can contain a populated plan_targets array.
Changing a target's name or enabled state uses the separate target update endpoint and affects that shared target across plans.