Update a subscription
Update subscription-level settings, manage items (add/remove/modify), adjust quantities and prices, with control over proration timing and behavior for mid-cycle changes.
Update subscription fields
Update metadata, payment method, trial period, or discount on an active subscription without changing line items.
API
Python SDK
Updatable fields
Lifecycle impact: Changing cancel_at_period_end, trial_end, or trial_period_days triggers a re-evaluation of the subscription lifecycle and may reschedule automated actions.
Invoice impact: Changing coupon_id or promotion_code voids any pending renewal invoices to ensure correct pricing on the next billing cycle.
Field clearing on lifecycle actions
When subscriptions transition between states, certain fields are automatically cleared to maintain data consistency. Understanding this behavior is important when building integrations that depend on these fields.
Why activate preserves scheduled cancellation: A customer may schedule a cancellation during their trial or while recovering from a failed payment. When the subscription activates (trial converts or payment succeeds), the scheduled cancellation should persist—it’s a deliberate user action that shouldn’t be silently discarded.
Why pause clears scheduled cancellation: Pausing supersedes any scheduled cancellation. The customer explicitly chose to pause instead, and can re-schedule cancellation after resuming if desired. This prevents edge cases where a cancel_at date passes while paused, causing unexpected immediate cancellation upon resume.
Update subscription items
Add, remove, or change items on a subscription. This is how you handle quantity changes, add-ons, and price swaps within the same billing interval.
Item operations
Combine multiple operations in a single request. All changes are applied atomically.
Proration behavior
The proration_behavior field is required and controls how mid-cycle changes are billed:
always_invoice: Creates an invoice immediately and attempts payment. Use when you want the customer to pay for the upgrade right away.create_prorations: Creates floating items (invoice_id=NULL) that are collected on the next renewal invoice. 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.
You can optionally specify proration_date to set a custom date for proration calculation (defaults to now).
Response fields
The response includes:
subscription_id: The updated subscription’s external IDinvoice_id: Created invoice ID (ifproration_behavior=always_invoicewith upgrade)credit_note_id: Created credit note ID (ifproration_behavior=always_invoicewith downgrade)payment_status: Payment result ("paid","processing","requires_action","failed","no_payment_method","skipped")payment_error: Error message if payment failedfloating_items_created: Count of floating items (ifproration_behavior=create_prorations)proration_amount_atom: Net proration amount in atomsvoided_invoice_ids: External IDs of voided pending renewal invoicesnew_renewal_invoice_id: External ID of replacement renewal invoice (if a voided invoice had active dunning)new_invoice_payment_status: Payment status of the replacement renewal invoice
Items must have the same billing interval as the subscription. To change a customer from monthly to annual billing, use the subscription change requests workflow instead (recommended) or the legacy change plan endpoint (deprecated).
Proration behavior
When items change mid-cycle, PaymentKit calculates the prorated difference. Control how proration is handled with the proration_behavior field:
Example: Immediate upgrade
Upgrade a customer from a basic plan to a pro plan and charge the prorated difference immediately:
The response includes the created invoice ID and payment status:
Payment status values:
paid: Payment succeededprocessing: Async payment (ACH/SEPA/BACS) submitted, awaiting webhook confirmationrequires_action: Customer action required (e.g., 3D Secure authentication)failed: Payment failedno_payment_method: No payment method availableskipped: Payment processing disabled for this account
Invoice voiding: When immediate updates are made, any pending renewal invoices (DRAFT, OPEN, or PAST_DUE) are automatically voided to ensure correct billing. The voided_invoice_ids field contains a list of the voided invoices. If the voided invoice had active dunning, a new renewal invoice is created with updated items, and its ID is returned in new_renewal_invoice_id.
Payment error responses
When payment fails with proration_behavior: "always_invoice", the update uses the Charge First pattern — payment is attempted before applying any item changes. If payment fails, the API returns a 402 Payment Required error and the subscription remains unchanged:
Card declined (402 response):
The subscription items are NOT updated. The customer must update their payment method and retry the request.
No payment method (402 response):
Charge First guarantees: With proration_behavior: "always_invoice", payment is collected BEFORE any subscription changes are applied. If payment fails:
- ✅ Subscription items remain unchanged
- ✅ Original subscription state preserved
- ✅ No
incompletesubscriptions created - ✅ Safe to retry without double-charging
This is the same Charge First pattern used by the subscription change requests workflow.
3D Secure handling:
If payment requires 3D Secure authentication, the MIT orchestrator will attempt to handle it automatically. If the orchestrator returns requires_action, you’ll need to collect the payment separately using the invoice collection endpoint.
Example: Deferred billing
Add an add-on that will be billed on the next renewal:
The response includes the count of floating items created:
Prorated line items are stored as floating items (with invoice_id set to null) and automatically included in the next renewal invoice.
Change subscription plan
To move items to different billing intervals or contract terms (e.g., monthly to annual), use the subscription change requests workflow:
The subscription change requests workflow provides:
- Preview before applying — show customers exact proration amounts before committing
- Charge First pattern — guarantee payment succeeds before modifying the subscription
- Idempotent retry — built-in retry safety prevents double-charging
- Status tracking — DRAFT → READY → APPLIED state machine
- Cross-interval support — handles monthly to annual and other billing term changes
- Term-based splitting — items automatically group into new subscriptions by billing terms
Both Update Items (this endpoint) and Change Requests use the Charge First pattern with proration_behavior: "always_invoice". The key difference is that Change Requests provide explicit preview and multi-step workflow for user confirmation.
Legacy endpoint (deprecated)
The change subscription plan endpoint is a single-call approach that is planned for deprecation. New integrations should use subscription change requests instead.
View legacy change plan endpoint details
The change plan endpoint provides:
- Term-based subscription splitting — items automatically group into new subscriptions by billing interval and contract terms
- Proration — credits for unused time, charges for new periods
- Scheduled changes — defer execution to period end with
effective_at: "period_end" - Pay-before-change — require payment success before committing changes
- Lineage tracking — trace new subscriptions back to the original
See change subscription plan documentation for full details (deprecated).
Pending invoice handling
When you modify a subscription with immediate changes, PaymentKit automatically voids any pending renewal invoices to ensure the next invoice reflects the updated configuration.
What gets voided:
- Only renewal invoices (
billing_reason: "subscription_cycle") - With status
draft,open, orpast_due - Proration invoices (
billing_reason: "subscription_update") are never voided
When voiding occurs:
Pending invoices are only voided for immediate changes that affect current-period billing:
- Adding items (without
start_at_end: true) - Updating items (quantity or price changes)
- Deleting items
Scheduled changes do not trigger voiding:
- Adding items with
start_at_end: true - Scheduling item removal with
drop_at_end: true
The response includes the voided invoice IDs:
Handling active dunning:
If a voided invoice has active dunning (failed payment with scheduled retries), PaymentKit uses a two-phase approach to ensure the replacement invoice has the correct subscription items:
-
Phase 1 - Void old invoice:
- Marks the old invoice as
void - Marks the dunning state as
invoice_voided
- Marks the old invoice as
-
Phase 2 - Create replacement invoice (after item changes are applied):
- Creates a new renewal invoice with the updated subscription items
- Finalizes the invoice to
openstatus - The dunning system automatically picks up the new invoice for collection
This ensures customers are always billed the correct amount for their current subscription configuration, even when dunning is in progress.
Preview upcoming invoice
Before making changes, preview what the next invoice will look like:
This is a read-only operation that computes what the next renewal invoice will be without creating anything in the database. No invoice numbers are claimed, no billing cycles are decremented, and no events are emitted.
The response includes:
- subscription: Complete subscription details including all items and pricing
- upcoming_invoice: Preview of the next billing cycle invoice with:
- Line items (descriptions, quantities, amounts, period dates)
- Amount breakdowns (subtotal, tax, total, due amount)
- Due date (calculated from
net_dpayment terms) - Billing period (period_start, period_end)
- Currency and collection method
Example response: