📋 Billing & SubscriptionsSubscriptions

Change subscription plan

Move an active, trialing or past_due subscription to another recurring price, from now.

POST/subscriptions/{id}/change-plan

Move an active, trialing or past_due subscription to another recurring price, from now.

Nothing is charged when the plan changes. With prorationBehavior next_invoice (the default, and what invoice_immediately means here), the difference for the rest of the current period is added to the next renewal invoice, or credited there for a downgrade. Two changes before a renewal are netted. With none the plan changes and no proration is added. The renewal amount becomes the new price times the subscription's quantity.

The new price must bill on the subscription's own interval and in its currency (interval_mismatch, currency_mismatch), and must have an amount above zero (price_amount_missing). See the figures first with POST /v2/subscriptions/{id}/preview-change-plan, which uses the same calculation.

Direct Debit. A Direct Debit subscription is refused with 409 direct_debit_plan_change_unsupported: a new amount needs advance notice to the payer, which this operation does not send.

Emits subscription.plan_changed. Requires the subscriptions:write scope and the subscription:update permission. Send an Idempotency-Key header to make retries safe.


Related endpoints

  • POST /subscriptions — Create a new subscription
  • GET /subscriptions — List subscriptions
  • GET /subscriptions/{id} — Get subscription by ID
  • PATCH /subscriptions/{id} — Update subscription details
  • POST /subscriptions/{id}/preview-change-plan — Preview a plan change
  • POST /subscriptions/{id}/pause — Pause subscription
  • POST /subscriptions/{id}/resume — Resume subscription
  • POST /subscriptions/{id}/cancel — Cancel subscription

Common errors

  • 400 invalid_request — malformed payload or failed validation.
  • 401 authentication_error — missing, invalid, expired, or revoked credential. Codes: authentication_failed, invalid_api_key, expired_api_key, api_key_revoked, session_invalid, merchant_required. Carries a WWW-Authenticate: Bearer challenge.
  • 403 authorization_error — the caller is identified but the action is denied. Codes include insufficient_permissions, ip_not_allowed, merchant_mismatch, and origin_not_allowed.
  • 404 resource_missing — the referenced resource does not exist or is not visible to your key.
  • 409 conflict — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.
  • 429 rate_limit_error — code rate_limit_exceeded. Back off using the Retry-After header (whole seconds); X-RateLimit-Reset is a UNIX timestamp in seconds.

Idempotency

Pass an Idempotency-Key header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see the idempotency guide.

x-api-key<token>

Your RevKeen merchant API key. Create and manage keys in Dashboard → Settings → Developer. Use rk_sandbox_* for staging/test and rk_live_* for production. The same key may be sent as Authorization: Bearer <key> if that suits your HTTP client better. A missing, invalid, expired, or revoked key returns 401 with a WWW-Authenticate: Bearer challenge; a valid key without the required scope returns 403.

In: header

Path Parameters

id*string

Subscription UUID

Formatuuid

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

Stuck on an error response? Ask the RevKeen assistant to explain it.
curl -X POST "https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan" \
  -H "x-api-key: $REVKEEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "newPlanId": "11111111-1111-4111-8111-111111111111"
  }'

Synthetic documentation data, validated against the response schema; not a live API result.

{  "data": {    "id": "d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24",    "merchant_id": "8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60",    "customer_id": "c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11",    "product_id": "a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42",    "price_id": "b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73",    "plan_id": null,    "status": "active",    "quantity": 1,    "amount_minor": 4900,    "currency": "GBP",    "billing_interval": "month",    "current_period_start": "2026-04-16T10:00:00.000Z",    "current_period_end": "2026-05-16T10:00:00.000Z",    "trial_end": "2026-04-16T10:00:00.000Z",    "canceled_at": null,    "created_at": "2026-04-02T10:00:00.000Z",    "updated_at": "2026-04-02T10:05:00.000Z",    "dunning": null,    "payment_rail": "card",    "mandate_id": null,    "payment_method_id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65"  },  "plan_change": {    "subscription_id": "d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24",    "current_price_id": "00000000-0000-4000-8000-000000000001",    "current_plan_name": "string",    "new_price_id": "b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73",    "new_plan_name": "string",    "new_product_id": "string",    "billing_interval": "string",    "billing_interval_count": 0,    "quantity": 0,    "current_amount_minor": 1000,    "new_amount_minor": 3000,    "proration_behavior": "next_invoice",    "effective_date": "2026-09-01T12:00:00Z",    "proration": {      "credit_amount_minor": 1000,      "charge_amount_minor": 3000,      "net_amount_minor": 2000,      "is_upgrade": true,      "days_remaining": 0,      "days_in_period": 0,      "currency": "GBP"    },    "pending_proration_net_amount_minor": 2000  }}

Put this endpoint to work