Billing & Subscriptions
Organizations in your CI Account have a Billing surface listing the paid add-ons that can be attached to an organization — custom domains, subdomain packs, and user support.
You cannot subscribe to anything today, and nothing can be purchased.
Billing only appears on a deployment that has a live payment processor
configured. No deployment has one, so the Billing tab is not rendered at
all, and a direct API call to subscribe is refused with 503 BILLING_NOT_CONFIGURED. This page documents the catalog and the refusal
behaviour, not a purchase flow — there is no purchase flow. See What is not
available yet.
What it is
Billing is scoped to an organization, not to an individual account. Each add-on is a separate line item that an organization owner or admin would subscribe to independently.
| Add-on | Price | Billing interval |
|---|---|---|
| Custom Domains | $12.00 | per month |
| Subdomain Pack | $5.00 | per month |
| User Support | $29.00 | per month |
Prices are quoted in USD and are the same for every organization. There are no annual plans, tiers, or trials.
This catalog is real — it is what GET /api/organizations/:orgId/billing returns, and any member of the organization can read it. What is not real is the ability to buy any of it.
Why billing is switched off
A CI Account deployment picks its payment processor from a single secret, STRIPE_SECRET_KEY. When that secret is absent the account falls back to a mock payment processor, which reports every subscription as active while no money moves and no card is ever involved.
That fallback is fine for development and unacceptable for anything a user can reach: it hands out paid add-ons for free while recording them in the database as if they had been paid for. So the CI Account treats “no payment processor” as refuse, never as “proceed”:
- The server reports its real capability.
GET /api/organizations/:orgId/billingreturnsstripeEnabled, derived from the processor that would actually run the charge — not from a second, separately-read copy of the configuration. - The web app hides the whole surface unless that flag is exactly
true. Loading, request failed, or field missing all count as “not configured”. The Billing tab trigger and its panel are not rendered — not merely disabled, because a disabled button still exists in the page and its handler is still reachable. - The subscribe endpoint refuses independently. The UI gate is not the control that matters;
POST /api/organizations/:orgId/billing/subscriptionsis reachable directly by any organization owner or admin with a session, so it checks for a live processor itself and returns503before touching the database.
No STRIPE_SECRET_KEY is set in any environment, so stripeEnabled is false everywhere and all three of the above are in force everywhere.
Where to find it
There is nothing to find yet. Open Organization settings on hub.ci.computer and you will see General, Members, Invitations and Audit Log — and no Billing tab. That is the expected, correct result, not a fault with your account or your role.
For reference, this is what the surface looks like on a development build that has a payment processor configured:
Development build only. This screenshot is here so you can recognise the surface when it ships; it is not something you can reach on hub.ci.computer, where the Billing tab is not rendered. The buttons in it have never taken a payment.
Each add-on is shown as a card with its name, its price per interval, and either the organization’s current subscription status or “No active subscription yet.” Subscribe buttons render for organization owners and admins only; members see the cards without buttons.
What happens if you try to subscribe anyway
The web app gives you no way to start, so this only applies to calling the API directly.
POST /api/organizations/:orgId/billing/subscriptions returns:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{
"error": "Billing is not available on this deployment",
"code": "BILLING_NOT_CONFIGURED"
}The check runs before anything is written, so a refused attempt leaves no subscription row and no org.billing.subscribe audit entry. The attempt is recorded in the deployment’s server logs only.
Treat BILLING_NOT_CONFIGURED as terminal rather than transient. Despite the 503, retrying will not help — the response means the deployment has no payment processor, and nothing about it changes until an operator configures one.
For developers
Two endpoints back the Billing surface. Both are scoped to an organization you are a member of; requests for organizations you do not belong to return 404.
| Method | Path | Who can call it | Today’s behaviour |
|---|---|---|---|
GET | /api/organizations/:orgId/billing | any member of the organization | 200 with stripeEnabled: false, the catalog, and [] |
POST | /api/organizations/:orgId/billing/subscriptions | owner or admin only | 503 BILLING_NOT_CONFIGURED — always, on every deployment |
GET .../billing returns the add-on catalog, the organization’s current subscriptions, and stripeEnabled — whether a live payment processor is configured. A client deciding whether to offer a purchase must read stripeEnabled and require a literal true; a missing field means an older deployment that predates the flag and must be treated as not configured.
POST .../billing/subscriptions takes a featureKey (custom_domain, subdomain_pack, or user_support) and an optional quantity between 1 and 100. On a deployment with a live processor it returns 201 with the stored subscription; on every current deployment it returns 503 as above.
Configuration
These apply to whoever operates the CI Account deployment — not to end users. Names only; never commit or paste the values.
| Name | Kind | Purpose |
|---|---|---|
STRIPE_SECRET_KEY | secret | The switch. Present and non-blank ⇒ billing is offered; absent ⇒ the surface is hidden and subscribe returns 503. |
STRIPE_PRICE_CUSTOM_DOMAIN | variable | Price identifier for the Custom Domains add-on |
STRIPE_PRICE_SUBDOMAIN_PACK | variable | Price identifier for the Subdomain Pack add-on |
STRIPE_PRICE_USER_SUPPORT | variable | Price identifier for the User Support add-on |
None of these are set in any environment today. They must reach the CI Account
worker as runtime secrets — a value stored only as a CI/CD repository secret
never reaches the running worker, and the deploy pipeline does not sync them.
Setting STRIPE_SECRET_KEY is what turns the Billing surface on, so it should
not be set until the gaps in What is not available
yet are closed.
What is not available yet
This list is exhaustive as of this page’s last update. Assume anything not described above does not exist.
- No purchase, at all. Billing is not offered on any deployment, and the subscribe endpoint refuses every request.
- No payment method collection. There is no Stripe Checkout, no card form, and no client-side Stripe integration, so no payment could be taken even if the surface were enabled.
- No provider callbacks. There is no webhook endpoint of any kind. Subscription state would be written once, when the button is clicked, and never updated — payment success or failure, dunning, cancellation and refunds would all be invisible to the CI Account.
- No cancellation or downgrade. There is no way to cancel or reduce a subscription from the UI or the API.
- No invoices, receipts, or billing portal. There is no invoice history and no link to a payment-processor customer portal.
- No entitlement enforcement. A subscription grants, unlocks and limits nothing. Custom Domains does not check whether you are subscribed — it is in development and unusable for other reasons entirely.
- No quantity control. The web app has no seat or pack picker.
- Not on hub.ci.computer. The production CI Account does not carry the Billing surface at all — it is not a matter of the tab being hidden there, the code is not deployed.
Troubleshooting
| Issue | Cause / fix |
|---|---|
| No Billing tab in organization settings | Expected everywhere today. The deployment has no payment processor configured, so the tab is not rendered. It is not a permissions problem — owners and admins do not see it either. |
503 with code: BILLING_NOT_CONFIGURED from the API | Expected everywhere today. Terminal, not transient: retrying cannot succeed until an operator configures a payment processor. |
| Billing tab appears, then disappears | The billing request failed after first render. The surface fails closed on any error, so it hides itself rather than offering a purchase it cannot honour. Check the browser network log. |
| Add-on cards load but there is no button | You are a member. Only owners and admins get subscribe buttons, even where billing is configured. |
An older build shows a Billing tab and reports active | That build predates the billing gate and is using the mock payment processor. The subscription is not real, no payment occurred, and nothing was purchased. Do not treat it as an entitlement. |
404 from either endpoint | You are not a member of that organization. |
Limits
Applicable once a payment processor is configured; none of it is reachable today.
- One subscription per organization per add-on
- Quantity is capped at 100 by the API
- USD and monthly billing only
- Prices are fixed in the catalog — no discounts, coupons, or custom pricing
See also: Organizations, Custom Domains.