v5.57.0 7 June 2026 Feature Improvement Breaking

Live Pricing — Single Source from Stripe

Why This Matters

A price is a promise, and the only honest way to display one is to read it from the system that charges it. Until now, plan prices lived in several places at once: constants in the API, literals in the marketing pages, a hand-maintained block in the machine-readable site index. Checkout always charged Stripe’s price, so a mismatch could never overcharge you, but a stale display price was possible in principle. As of 5.57.0 there is one source, and every link in the chain between Stripe and your screen fails loudly rather than silently.


Live Plan Pricing

New Public Plans Endpoint

  • GET /v1/public/plans An unauthenticated endpoint returning current plan pricing, sourced live from Stripe with a five-minute cache. These are the same Price objects checkout charges. If Stripe is unreachable the API serves declared fallback values and announces the divergence at warning level in the logs, an operational control that makes silent fallback impossible to miss.

  • Public surface renamed The unauthenticated read surface moved from /v1/early-access to /v1/public, a name that describes what it is rather than the campaign it launched with. The seat-availability stats endpoint moved with it to /v1/public/stats. The unauthenticated surface is enumerated explicitly, so every public path is a deliberate, reviewable decision.

Billing Plans

  • Authenticated plan listing now live-sourced GET /v1/billing/plans serves from the same live pricing source and the same cache as the public endpoint. Response shape and contents are unchanged; what changed is that the values can no longer drift from what you are charged.

Marketing Site

  • Prices baked from source at build time A build step fetches current plans from the public endpoint and writes them into the static pages. The pricing page, FAQ, sitemap, and machine-readable site index all render from this single fetched file; the hand-maintained price literals are gone.

  • Price-integrity deploy gate The marketing deploy pipeline gained a fail-closed gate: every price in the fetched data must appear on the rendered pricing page and in the machine-readable index, or the deploy stops. A release whose displayed prices disagree with source cannot ship.

Proof anchor: fail-closed price-integrity gate (G5) in the marketing deploy pipeline, derive-and-compare against the Stripe-fetched plans data; executable proof anchors 76/76 green on API deploy (Phase 13).


Operational Impact

  • A price change made in Stripe propagates to every display surface automatically: API cache within five minutes, marketing site at the next build, with no code change
  • Zero silent pricing fallback: any divergence between live and declared values logs at warning level
  • A marketing deploy with mismatched prices is stopped by the gate, not discovered by a customer
  • Six hand-maintained price literals eliminated across the marketing site, including the long-standing TODO in the machine-readable index

Files Changed

Backend:

  • api/src/routes/public.ts — renamed from the early-access module; gains the plans endpoint
  • api/src/routes/billing.ts — plans listing switched to the live pricing source
  • api/src/lib/stripe.ts — live plan pricing with cache, fallback, and drift warning
  • api/src/server.ts — public mount and allowlist updated

Marketing site:

  • scripts/fetch-plans.mjs — new; fetches live plans during deploy
  • src/components/PricingCard.tsx, src/components/PricingSection.tsx — render from fetched plans; seat counter moved to the new stats path
  • src/pages/pricing.astro, src/pages/faq.astro, src/pages/sitemap.astro, src/pages/llms-full.txt.ts — prices derived from fetched plans
  • deploy.sh — plans fetch added to artifact sync; price-integrity gate added

Known Issues

  • The retired /v1/early-access/stats path now requires authentication and effectively returns 401. The marketing site was its only consumer and moved with it; if you scripted against this undocumented-for-integration path, switch to /v1/public/stats.

Displayed prices and charged prices are now the same fact read from the same place, which is how it should have been from the start.