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

# Change Plan

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

## Request body

- ChangePlanRequest
  - `plan` 'free' | 'hobbyist' | 'developer' | 'startup' | 'enterprise', required
  - `startup_variant` 'outbound', nullable — Startup only: pass the fixed value 'outbound' when selecting the supported $200 plan. Omit or pass null while keeping the current Startup subscription to clear a pending change without changing its legacy price.

## Response `200`

Successful Response

- SubscriptionStatusResponse
  - `plan` 'free' | 'hobbyist' | 'developer' | 'startup' | 'enterprise', required
  - `status` string, nullable — Stripe subscription status (active, past_due, ...) or None on Free.
  - `cancel_at_period_end` boolean — Whether the subscription is set to cancel at the period boundary.
  - `current_period_end` integer, nullable — Unix seconds when the current billing period ends, if subscribed.
  - `pending_plan` 'free' | 'hobbyist' | 'developer' | 'startup' | 'enterprise'
  - `pending_change_date` integer, nullable — Unix seconds when the pending plan change / cancellation takes effect.
  - `pending_startup_variant` 'outbound' | 'inbound'
  - `legacy_developer_price` boolean — True when the subscription's flat price is the retired $25 Developer price (sunset migration pending); the console surfaces the phase-out banner from this.
  - `stripe_configured` boolean, required — False when Stripe isn't configured server-side; the UI hides upgrade actions.
  - `balance` integer, nullable — Stripe customer account balance in cents (negative = credit available, positive = amount owed). None on Free with no customer yet, or if the read failed.
  - `estimated_next_bill` integer, nullable — Amount due in cents on the upcoming invoice (plan fee + metered overage accrued so far this cycle), net of any account credit. None when there's no subscription (Free) or the preview read failed.
  - `entitlements` EntitlementsResponse
    - `is_custom` boolean, required — True when the active subscription price matches a custom_plans row.
    - `display_name` string, required — Plan display name — the custom plan's name, else the tier name.
    - `plan_amount_cents` integer, nullable — Flat recurring plan fee in cents; None when it can't be determined.
    - `phone_numbers` integer, required — Phone-enabled identities cap (resolved PHONE_NUMBERS quota).
    - `sending_domains` integer, required — Custom email domains cap (resolved MAIL_SENDING_DOMAINS_ACTIVE quota).
    - `identities` integer, nullable — Agent-identity cap (resolved IDENTITIES_ACTIVE quota).
    - `mail_storage_bytes_per_identity` integer, required — Stored-mail cap per mailbox in bytes (resolved MAIL_STORAGE_BYTES_PER_IDENTITY quota).
    - `imessage_messages_per_org_per_cycle` integer, required — Org-wide shared-pool iMessage messages per billing cycle (resolved IMESSAGE_MESSAGES_PER_ORG_PER_CYCLE quota; dedicated-line traffic has its own cap).
    - `imessage_dedicated_messages_per_org_per_cycle` integer, nullable — Org-wide dedicated-line iMessage messages per billing cycle (resolved IMESSAGE_DEDICATED_MESSAGES_PER_ORG_PER_CYCLE quota); None/0 when the plan includes no dedicated lines.
    - `startup_variant` 'outbound' | 'inbound'
    - `imessage_unique_recipients_per_org_per_cycle` integer, required — Org-wide unique iMessage recipients per billing cycle (resolved IMESSAGE_UNIQUE_RECIPIENTS_PER_ORG_PER_CYCLE quota).
    - `sms_allowance_per_identity` integer, required — Included SMS allowance per phone-enabled identity per cycle.
    - `voice_sec_allowance_per_identity` integer, required — Included voice allowance per phone-enabled identity per cycle, in seconds.
    - `imessage_enabled` boolean, required — Whether iMessage is included on the plan's identities.
    - `dedicated_imessage_inbound_lines` integer, nullable — Dedicated inbound (receive/reply-only) iMessage lines the org may claim (resolved IMESSAGE_DEDICATED_INBOUND_LINES quota; 0 when the plan includes none).
    - `dedicated_imessage_outbound_lines` integer, nullable — Dedicated outbound (may initiate) iMessage lines the org may claim (resolved IMESSAGE_DEDICATED_OUTBOUND_LINES quota; 0 when the plan includes none).
    - `imessage_included_messages_per_cycle` integer, nullable — iMessage volume included per billing cycle before overage billing; None when the plan has no such term.
    - `imessage_overage_cents_per_million` integer, nullable — Price in cents per additional 1M iMessages (or portion thereof) beyond the included volume; None when the plan has no such term.
    - `campaign_fee_cents` integer, nullable — Monthly fee in cents per carrier-approved 10DLC campaign; None when the fee isn't offered in this environment.
    - `campaign_fee_active` boolean — Whether the org is currently being billed the 10DLC campaign fee.

## Other responses

- `422` — Validation Error
- `4XX` — Client error with Support Agent information.
- `5XX` — Server error with Support Agent information.

---

[API](https://skmtc.dev/inkbox/apis/inkbox-api.md) · [All operations](https://skmtc.dev/inkbox/apis/inkbox-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/inkbox/inkbox-api/revisions/31f357e7e6f8/schema)
