Preview a plan change
Preview the billing impact of a plan change before confirming it.
A plan change belongs to the customer, and a store token previews it on their behalf. preview_payload contains the provider's own preview, so its keys differ per provider. Stripe returns the proration as an upcoming invoice. A subscription the store bills in house also prices the proration, and bills it as a separate proration order on confirmation. PayPal returns no pricing and reports that the change needs the buyer's approval.
Stripe and in-house previews also say what the customer pays and when:
payment_required: truemeansamount_dueis due today. Stripe charges it when you confirm. In house, the confirmation returns anapproval_urlwhere the customer pays it. On Stripe this happens withproration_behavior: invoice_now, and whenever the change starts a new billing period, such as monthly to yearly or a free plan to a paid one.payment_required: falsemeans nothing is charged today. Anamount_duein a Stripe preview is then the next invoice, prorations included, which Stripe collects onrenewal_date.renewal_dateis when the subscription renews next after the change: the end of the current period, or of the new one when a Stripe change starts one.
A plan change during a trial never charges today.
A plan change ends a subscription discount. While the subscription still has discounted payments left and will renew, so it isn't set to cancel, the preview carries discount_warning, a sentence to show the customer before they confirm, such as Changing plan ends your discount; after this change you'll pay $20.00 / month. A Stripe preview's amounts already leave the discount out. In house, the unused part of the current period is credited at the discounted price the customer paid for it.
Provide your SellApp API key as a bearer token.
In: header
Path Parameters
The productSubscription path parameter.
Header Parameters
A caller-generated key, unique per store and subscription lifecycle request. Sending it here is the same as sending idempotency_key in the body.
length <= 128Caller-generated key for safely retrying this operation. The JSON idempotency_key field is also accepted; when both are sent, the header takes precedence.
length <= 128Store slug. Required for OAuth access tokens. API keys may omit it to use their current store, or the first accessible store when no current store is selected.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://sell.app/api/v2/subscriptions/0/actions/change-plan/preview" \ -H "X-STORE: launch-lab" \ -H "Content-Type: application/json" \ -d '{ "target_variant_id": 4321, "effective_timing": "immediate", "proration_behavior": "provider_default" }'{
"data": {
"preview_token": "subprev_9c4b2f",
"expires_at": "2026-07-03T12:30:00.000000Z",
"preview_payload": {
"provider": "stripe",
"invoice_id": "upcoming_in_1QwertyExample",
"amount_due": 2920,
"amount_remaining": 2920,
"subtotal": 2920,
"total": 2920,
"currency": "usd",
"line_items": [
{
"id": "il_tmp_1QwertyUnused",
"amount": -919,
"currency": "usd",
"description": "Unused time on Design kit monthly after 03 Jul 2026",
"proration": true,
"period_start": "2026-07-03T12:00:00+00:00",
"period_end": "2026-08-01T00:00:00+00:00"
},
{
"id": "il_tmp_1QwertyRemaining",
"amount": 1839,
"currency": "usd",
"description": "Remaining time on Design kit pro after 03 Jul 2026",
"proration": true,
"period_start": "2026-07-03T12:00:00+00:00",
"period_end": "2026-08-01T00:00:00+00:00"
},
{
"id": "il_tmp_1QwertyNextPeriod",
"amount": 2000,
"currency": "usd",
"description": "1 × Design kit pro (at $20.00 / month)",
"proration": false,
"period_start": "2026-08-01T00:00:00+00:00",
"period_end": "2026-09-01T00:00:00+00:00"
}
],
"subscription_id": "sub_1QwertyExample",
"subscription_item_id": "si_QwertyExample",
"target_price_id": "price_1QwertyPro",
"proration_behavior": "create_prorations",
"proration_date": 1783080000,
"renewal_date": "2026-08-01T00:00:00+00:00",
"payment_required": false,
"provider_previewed_monetary_proration": true
},
"request_payload": {
"source_variant_id": 99,
"target_variant_id": 4321,
"effective_timing": "immediate",
"proration_behavior": "provider_default"
},
"customer_message": null,
"seller_message": null
},
"message": "Subscription plan change preview created."
}{
"type": "invalid_request_error",
"code": "bad_request",
"message": "The request could not be understood.",
"status": 400,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#bad-request"
}{
"type": "authentication_error",
"code": "unauthenticated",
"message": "Unauthenticated.",
"status": 401,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#unauthenticated"
}{
"type": "authorization_error",
"code": "forbidden",
"message": "This action is unauthorized.",
"status": 403,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#forbidden"
}{
"type": "not_found_error",
"code": "resource_not_found",
"message": "Not Found.",
"status": 404,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#resource-not-found"
}{
"type": "validation_error",
"code": "validation_failed",
"message": "The selected product variant id is invalid.",
"status": 422,
"param": "product_variant_id",
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#validation-failed",
"errors": {
"product_variant_id": [
"The selected product variant id is invalid."
]
}
}{
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Too Many Attempts.",
"status": 429,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#rate-limit-exceeded"
}{
"type": "api_error",
"code": "api_error",
"message": "Server Error",
"status": 500,
"param": null,
"request_id": "01992a65-e064-71ba-b38f-902b7966a6be",
"docs_url": "https://sell.app/docs/api/errors#api-error"
}