---
title: "Preview Plan Change"
method: POST
path: "/v1/billing/change-plan/preview"
tags: ["billing"]
---

# Preview Plan Change

`POST /v1/billing/change-plan/preview`

## Request

- Base URL: `https://api.elaan.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.elaan.io/v1/billing/change-plan/preview`
- Auth: none declared

Quote what /change-plan with the same body would do — Paddle's dry-run
proration preview, run through the exact admissibility rules of the execute
path so a shown quote can always be confirmed. Commits nothing.

## Headers

- `authorization` string, nullable

## Request body

- ChangePlanRequest: Move an existing paid subscription to this tier/period. The fields *are* the domain enums, so an unknown value is a clean 422 listing the permitted ones — the same way the other modules type their enum fields.
  - `plan` 'free' | 'pro' | 'business' | 'scale', required: A subscription tier. The natural identity of a plan is its slug; the price lives in Paddle (per price id), and the *limits* live in this module's `PLAN_LIMITS` so a pricing change is a one-line edit, not a data migration.
  - `period` 'monthly' | 'annual', required: How often a paid tier renews. Mirrors the pricing page's monthly/annual toggle; each (plan, period) pair maps to one Paddle price id.

## Response `200`

Successful Response

- PlanChangeQuoteResponse: The confirmation dialog's content: current tier, target tier, the immediate proration, and (when Paddle supplied it) the next bill.
  - `current` TierResponse, required
    - `plan` string, required
    - `period` string, nullable, required
  - `current_period_end` string, date-time, nullable, required
  - `target` TierResponse, required
    - `plan` string, required
    - `period` string, nullable, required
  - `due_now` PlanChangeDueNow, required: What confirming would charge (or credit) immediately. `action` is Paddle's vocabulary (`charge`/`credit`); `amount` is minor units.
    - `action` string, required
    - `amount` string, required
    - `currency_code` string, required
  - `next_bill` PlanChangeNextBill, required
    - `amount` string, required
    - `currency_code` string, nullable, required
    - `billed_at` string, date-time, nullable, required

## Other responses

- `409`: No paid subscription to change, or already on that tier
- `422`: Unknown plan/period, or no price configured for it

---

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