Billing
All billing endpoints require authentication. They're thin wrappers around Stripe Checkout and the customer portal; we don't touch card details ourselves.
POST /billing/checkout
Start a Stripe Checkout session for a tier upgrade. Returns the URL to redirect the user to.
Body
{ "tierId": "desktop" | "cloud" | "team" }
200 OK
{ "url": "https://checkout.stripe.com/..." }
Errors
400 VALIDATION_ERROR
400 ALREADY_OWNED
400 ALREADY_SUBSCRIBEDPOST /billing/portal
Open the Stripe customer portal for the signed-in user (update card, cancel, switch plans, download invoices).
200 OK
{ "url": "https://billing.stripe.com/..." }GET /billing/status
Return the current subscription state.
200 OK
{
"tier": "free" | "desktop" | "cloud" | "team",
"ownsDesktop": boolean,
"subscription": null | {
"status": string, // Stripe status
"currentPeriodStart": string, // ISO timestamp
"currentPeriodEnd": string,
"canceledAt": string | null
}
}GET /billing/invoices
Return recent Stripe invoices for the signed-in user. Empty array when billing isn't configured or the user has no Stripe customer yet.
200 OK
{ "invoices": [ { ...Stripe invoice fields... } ] }