billing
Covers 6 API operations in the billing category.
| Method | Path | Description |
|---|---|---|
| POST | /v1/hooks/polar | Polar billing webhook (docs/billing.md) |
| GET | /v1/orgs/{orgSlug}/billing | Org's plan and billing status (docs/billing.md) |
| POST | /v1/orgs/{orgSlug}/billing/checkout | Creates a checkout session for the Pro plan |
| POST | /v1/orgs/{orgSlug}/billing/portal | Creates a customer portal session (payment methods, receipts, cancellation) |
| POST | /v1/orgs/{orgSlug}/billing/session | Customer session token (authenticates the payment method embed) |
| POST | /v1/orgs/{orgSlug}/billing/cancel | Schedules or reverses cancellation at period end |
POST /v1/hooks/polar
This is not a path users call. Polar delivers subscription events here. Authentication
is not a token but a Standard Webhooks signature — it verifies using the three
headers webhook-id, webhook-timestamp, and webhook-signature together with the
server's RUNLOT_POLAR_WEBHOOK_SECRET (otherwise 403, timestamp within ±5 minutes). The
same webhook-id is applied only once (a redelivery returns 200 and is ignored).
This is the only place that changes orgs.plan: for subscription.active and
updated, status active/trialing/past_due maps to pro, and everything else
(canceled, unpaid, and so on) maps to free. Returns 501 if the server has no token.
Client libraries do not call this.
operationId polarHook
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | received (if applied is true, the plan changed) | — |
| 400 | — | — |
| 403 | the signature does not match | — |
| 501 | billing is off | — |
GET /v1/orgs/{orgSlug}/billing
Returns the plan (plan) and, for Pro, its status (status: active · past_due ·
canceled) and the end of the current period (periodEnd). If enabled is false,
billing is not configured for this deployment — the screen shows only a contact address.
If sandbox is true, this is Polar's test environment, so there is no real billing.
limits is the limit table for the two plans (cp/internal/plan). The reason no numbers
appear on the screen is the same as for OrgMembership.projectLimit. Viewer or above.
operationId getBilling
| Status code | Description | Response body |
|---|---|---|
| 200 | Billing status | BillingStatus |
| 403 | — | — |
| 404 | — | — |
POST /v1/orgs/{orgSlug}/billing/checkout
Creates a single Polar checkout session and returns its url. The dashboard displays
that URL in an iframe on its own page (@polar-sh/checkout; the session's
embed_origin is the origin of returnTo). Even after payment completes, it does not
change the plan: the webhook (/v1/hooks/polar) changes the plan. The screen re-reads
state after the success event.
returnTo must be under the server's RUNLOT_PUBLIC_URL (otherwise 400). If already
Pro, 409 already_pro — open the portal instead. admin.
operationId createBillingCheckout
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | Checkout session | object |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 409 | already_pro | Error |
| 501 | billing_off — billing is not configured | — |
| 502 | billing_unavailable — Polar is not responding | — |
POST /v1/orgs/{orgSlug}/billing/portal
Returns a one-time url for the Polar customer portal. Changing payment
methods, receipts, and cancellation all happen there — we do not put that
form on our own screens (keeping card numbers off our servers is the
value of MoR). An org that has never made a payment returns 404.
Requires admin.
operationId createBillingPortal
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | Portal session | object |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |
POST /v1/orgs/{orgSlug}/billing/session
The customer session token required by the payment method embed in @polar-sh/checkout.
Valid for 1 hour, for that one customer. Fetched fresh each time the screen opens. An
org with no billing history gets 404. admin.
operationId createBillingSession
| Status code | Description | Response body |
|---|---|---|
| 200 | Customer session | object |
| 403 | — | — |
| 404 | — | — |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |
POST /v1/orgs/{orgSlug}/billing/cancel
If cancel: true, Pro ends at the close of the current period (Pro until then) —
Polar's cancel_at_period_end. false reverses the scheduled cancellation. There is no
immediate revocation: the period already paid for is used in full. The response
immediately updates our row's status, and the webhook soon arrives at the same
conclusion. If not Pro, 409 not_pro. admin.
operationId cancelBilling
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | The status that changed | object |
| 403 | — | — |
| 404 | — | — |
| 409 | not_pro | Error |
| 501 | billing_off | — |
| 502 | billing_unavailable | — |