---
title: "Billing Checkout"
method: POST
path: "/me/billing/checkout"
---

# Billing Checkout

`POST /me/billing/checkout`

## Request

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

Mint a checkout for a tier and answer the URL the buyer opens.

A POST, not a GET: the route CREATES a checkout at the payment
provider. A GET is prefetchable, so a link scanner would mint a real
checkout each time a user points at the link. Every route that mints
outbound is a POST
(``POST /me/billing/plan``, ``POST /apps/{id}/exports``).

Console-only, per-current-user: Bearer-authed like the ``/exports``
routes — the web server forwards the session token in the
``Authorization: Bearer`` header and ``current_token`` verifies it.
The route then demands ``billing:write``, which the console session
mint alone grants, so an agent credential answers 403. The caller's
id goes to the provider as the buyer hint, so the provider's webhook
can bind the new subscription back to this account.

The route refuses a caller who already holds a granting subscription
(409). A provider accepts a second subscription and charges the card
twice, and the plan projection takes the highest granting tier, so
the extra subscription stays invisible. xhostd owns subscription
state, so the guard belongs here and not in the console.

**The 409 is the one enforcement point on the PUBLIC surface, and it
is no longer the only mint point.**
``python -m xhostd.cli.admin mint-checkout``
(:mod:`xhostd.cli.admin`) mints the same checkout and skips exactly
this guard, so an operator can hand a live subscriber a second
checkout without a gap in their plan. That verb needs a shell on the
edge box, so nothing a caller sends reaches it.

The route hands the provider no landing URL. The client took
``settings.platform_domain`` at construction
(``xhostd/__main__.py::_build_billing_client``) and builds the apex
checkout page from :mod:`xhostd.billing` itself, so a target from
the console cannot become an open-redirect input.

## Headers

- `authorization` string, nullable

## Request body

- CheckoutRequest
  - `tier` string, required

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

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