Change subscription plan
Use the change-plan endpoint when you need to move items to different billing intervals or contract terms. This is the recommended approach for upgrades and downgrades that change the billing cycle.
For changes within the same billing interval (quantity updates, add-ons, price swaps), use the update subscription items endpoint instead.
Request fields
Proration behavior options
always_invoice: Create invoice immediately and attempt payment now. Use when you want the customer to pay for the upgrade immediately.create_prorations: Create floating items (invoice_id=NULL) that will be collected at the next renewal. Use for deferred billing.none: No proration charges or credits are created. Items are updated immediately but no billing adjustment is made for the mid-cycle change.
How term-based splitting works
When items change to prices with different billing terms, PaymentKit automatically creates new subscriptions grouped by their terms:
Items are grouped by their subscription terms:
- Billing interval (month, year, etc.)
- Billing interval count (every 1 month, every 3 months, etc.)
- Contract length (12 billing cycles, indefinite, etc.)
- Auto-renew setting (whether contract renews at end of term)
Item actions
Each item mapping requires an action field and additional fields depending on the action:
Items NOT included in the request stay unchanged on the original subscription.
Response fields
Created subscription fields
Each subscription in created_subscriptions includes:
Examples
Monthly to annual upgrade
Upgrade a customer from monthly (1,000/yr) billing with immediate payment:
API
Python SDK
The response includes details about the plan change:
In this example:
- The customer receives a $50 credit for unused time on the monthly plan
- The customer is charged $1,000 for the annual plan
- The net invoice is $950
- The original subscription is cancelled (all items moved)
- A new annual subscription is created
Add items during plan change
Add a new item while upgrading to a different plan:
Partial plan change
Change only some items while keeping others unchanged:
Items not included in the request remain on the original subscription with their current billing terms.
Deferred billing with create_prorations
Upgrade a plan but defer billing to the next renewal:
With create_prorations, proration amounts are calculated but stored as floating line items (invoice_id=NULL). They will be included in the next renewal invoice rather than creating an immediate invoice.
Pay-before-change flow
Require payment before applying the plan change. This ensures the customer stays on their original plan if payment fails:
With pay_before_change: true:
- New subscriptions are created in
incompletestate - items are added but not yet active - Original subscription remains unchanged - items are NOT deactivated and the subscription is NOT cancelled until payment succeeds
- A 402 Payment Required response is returned with the
invoice_idto collect - Collect the invoice separately to complete the plan change:
- If payment succeeds: Items on the original subscription are deactivated, the original subscription is cancelled (if empty), and new subscriptions are activated automatically
- If payment fails or requires action (e.g., 3D Secure): The customer stays on their original subscription with no disruption
pay_before_change: true requires proration_behavior: "always_invoice". Without an invoice, there is nothing to collect payment on.
Scheduled plan changes
Use effective_at: "period_end" to schedule a plan change for the end of the current billing period instead of applying it immediately. This is useful when you want to honor a customer’s remaining time on their current plan before switching.
When effective_at is set to "period_end", the plan change is stored as a pending change on the subscription and executed automatically at the end of the current billing period. No items are moved, no invoices are created, and no subscriptions are cancelled until that time.
The response confirms the pending change was stored:
For deferred changes, original_items_remaining reflects the current item count on the subscription. Items are not moved or cancelled until the change executes at scheduled_for. Fields like created_subscriptions, invoice_id, and proration amounts are empty because the actual plan change has not executed yet.
Deferred change response fields
Cancel a pending plan change
Cancel a scheduled plan change before it executes using the cancel endpoint:
Response when a pending change exists:
The subscription_item_id and new_price_id values in the items array are internal identifiers, not the external IDs used in other API endpoints.
Response when no pending change exists:
Constraints and limitations
- One pending change at a time. A subscription can only have one pending plan change. To schedule a different change, cancel the existing one first, then schedule the new change.
pay_before_changeis not supported.effective_at: "period_end"cannot be combined withpay_before_change: true. Deferred changes don’t create an invoice until execution time, so there is nothing to collect payment on in advance.- Default
proration_behaviorisalways_invoice. When the deferred change executes at period end, an invoice is created immediately for the first period of the new subscription and payment is attempted. If payment fails, the new subscription is created inincompletestate and enters dunning. You can override this by specifying a differentproration_behaviorwhen scheduling the change. - Deferred execution vs. deferred billing.
effective_at: "period_end"defers when the plan change happens — items stay on the current plan until period end. This is different fromproration_behavior: "create_prorations", which executes the change immediately but defers billing to the next renewal invoice. You can useeffective_at: "period_end"with anyproration_behaviorvalue — the proration behavior applies when the change executes at period end.
Webhooks
Webhook sequence for immediate changes
When a plan change executes immediately (default behavior), the webhook sequence depends on whether items move to different billing intervals:
Same-interval changes (no subscription split):
customer.subscription.updated— subscription items updated
Cross-interval changes (subscription split):
customer.subscription.created— new subscription(s) created with the target billing intervalcustomer.subscription.cancelled— original subscription cancelled (if all items moved)invoice.created— proration invoice for the new subscription periodinvoice.paid— payment collected on the proration invoice
Webhook sequence for scheduled changes
When a scheduled plan change executes at period end, the same webhook sequence as immediate changes applies at execution time.
Identifying plan-change cancellations
When a subscription is cancelled due to a plan change (all items moved to new subscriptions), the customer.subscription.cancelled webhook includes:
cancellation_reason: "change_plan"cancellation_details: {"reason": "change_plan"}
Use these fields to distinguish plan-change cancellations from customer-initiated cancellations.
Delivery order is NOT guaranteed. Webhooks may arrive in any order. Design your integration to handle events idempotently — do not assume subscription.created arrives before subscription.cancelled.
Lineage tracking with split_from_subscription_id
New subscriptions created by a plan change include lineage metadata that links them back to the original subscription. In webhook payloads, this value is located at data.object.metadata.split_from_subscription_id:
Use data.object.metadata.split_from_subscription_id from the webhook payload to trace which original subscription a new subscription was split from. The same metadata.split_from_subscription_id field is also available in API responses when retrieving the subscription directly.