---
title: "Create Subscription Checkout"
method: POST
path: "/api/billing/subscription/checkout"
tags: ["billing"]
---

# Create Subscription Checkout

`POST /api/billing/subscription/checkout`

## Request

- Base URL: `https://api.civilquants.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.civilquants.com/api/billing/subscription/checkout`
- Auth: none declared

Create a Stripe Checkout Session in `mode=subscription`.

Auth required (Clerk JWT). `metadata.clerk_user_id` is set from the
validated JWT; the request body's `clerk_user_id` / similar fields
(if present) are ignored — they're not even in the schema. This is
the Codex P3.3.g HIGH-2 lesson: trust anchor stays server-side.

Currency resolution: explicit override → cf-ipcountry → Accept-
Language → GBP fallback. Same path as the 7-day-pass route.

Tier seat semantics:
  * Solo:     quantity = 1 (single user)
  * Studio:   quantity = 5 (5-seat pack, fixed at v1)
  * Practice: quantity = 25 (25-seat minimum, hardcoded at v1)
Above-minimum expansion deferred to v1.1 via Customer Portal.

No `trial_period_days` anywhere on the path — the freemium MCP
tier IS the trial; the £10 7-day pass is the second on-ramp;
PASS_CREDIT_FIRST_MONTH is the bridge.

## Headers

- `authorization` string, nullable

## Request body

- CheckoutSubscriptionRequest: Body for POST /api/billing/subscription/checkout. Pydantic Literal types reject any tier / billing_cycle outside the locked v1 set with a 422. The request schema deliberately does NOT accept `clerk_user_id` / `client_reference_id` / `customer_id` — those are server-side fields derived from the JWT (Codex P3.3.g HIGH-2 trust-anchor discipline).
  - `tier` 'solo' | 'studio' | 'practice', required
  - `billing_cycle` 'monthly' | 'annual', required
  - `currency` 'GBP' | 'USD' | 'EUR' | 'AUD' | 'NZD' | 'SGD' | 'CAD' | 'MYR'
  - `coupon` string, nullable

## Response `200`

Successful Response

- CheckoutSubscriptionResponse
  - `checkout_url` string, required
  - `session_id` string, required

## Other responses

- `422`: Validation Error

---

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