runlot
ReferenceAPI

billing

Covers 6 API operations in the billing category.

MethodPathDescription
POST/v1/hooks/polarPolar billing webhook (docs/billing.md)
GET/v1/orgs/{orgSlug}/billingOrg's plan and billing status (docs/billing.md)
POST/v1/orgs/{orgSlug}/billing/checkoutCreates a checkout session for the Pro plan
POST/v1/orgs/{orgSlug}/billing/portalCreates a customer portal session (payment methods, receipts, cancellation)
POST/v1/orgs/{orgSlug}/billing/sessionCustomer session token (authenticates the payment method embed)
POST/v1/orgs/{orgSlug}/billing/cancelSchedules 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 codeDescriptionResponse body
200received (if applied is true, the plan changed)
400
403the signature does not match
501billing 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 codeDescriptionResponse body
200Billing statusBillingStatus
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 codeDescriptionResponse body
200Checkout sessionobject
400
403
404
409already_proError
501billing_off — billing is not configured
502billing_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 codeDescriptionResponse body
200Portal sessionobject
400
403
404
501billing_off
502billing_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 codeDescriptionResponse body
200Customer sessionobject
403
404
501billing_off
502billing_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 codeDescriptionResponse body
200The status that changedobject
403
404
409not_proError
501billing_off
502billing_unavailable

On this page