---
title: "Switch an existing subscription to another plan (principal-scoped)"
method: POST
path: "/v2/billing/plan"
tags: ["Billing"]
---

# Switch an existing subscription to another plan (principal-scoped)

`POST /v2/billing/plan`

## Request

- Base URL: `https://api.infolang.ai`
- URL: `https://api.infolang.ai/v2/billing/plan`
- Auth: HTTP bearer

The caller's own account. Modifies the existing subscription in place — up or down — with `create_prorations` in both directions, settled on the next invoice rather than charged today. A downgrade applies immediately, including its smaller quota ceiling.

`enterprise` and `free` are not switch targets: the first is quoted and hand-provisioned, the second is what cancelling leads to.

The response reports what STRIPE accepted. `plan` on the billing summary is written by the `customer.subscription.*` webhook a moment later, so a summary fetched in the same breath may still report the old plan — render the value returned here and re-fetch the summary shortly after, rather than treating the lag as a failure.

## Request body

- object
  - `plan` string, required

## Response `200`

The subscription after the switch

- object
  - `plan` string, required
  - `cancel_at_period_end` boolean, required
  - `current_period_end` string, nullable, required

## Other responses

- `401`: Missing or invalid session token
- `403`: Caller lacks a grant on the workspace, or role too low. Or `lane_not_supported` — an `il_live_`/`il_test_`/`il_oauth_` credential, though valid, was presented to a route that accepts the session lane only
- `409`: no_subscription — this account holds no live Stripe subscription, so there is nothing to switch or cancel. Start one with the checkout route.
- `422`: invalid_request — no `plan` in the body, an unknown plan, or a plan that is not a self-serve switch target (`enterprise`, `free`).
- `500`: internal_error — unexpected failure (e.g. billing backend misconfigured or unreachable)
- `503`: not_configured — self-serve billing is not available on this deployment: either no payment processor is wired, or its prices do not reconcile with the published catalog. Returned INSTEAD of a checkout URL, because the alternatives were a link to a domain that does not resolve and a checkout that charges an amount the page never showed. Mirrors `self_serve_checkout` on both summaries exactly — same evaluation, same cache.

---

[API](https://skmtc.dev/infolang/apis/infolang-api.md) · [All operations](https://skmtc.dev/infolang/apis/infolang-api/llms.txt) · [OpenAPI document](https://skmtc.dev/infolang/apis/infolang-api/revisions/d04bf6de72c8?raw)
