FFDB platform billing
Read organization entitlements and use operator-configured Stripe Checkout and Customer Portal sessions in a self-hosted deployment.
Charge for hosted FFDB usage
Platform billing is the released organization-entitlement contract for self-hosted deployments, including an always-readable summary and operator-configured Stripe Checkout, Customer Portal, and webhook handling.
It lets each deployer choose private or team analytics without tenant charges, or run a monetized BYO/Connect instance that enforces the operator-owned Free, pay-as-you-go, and Pro contract.
Use it when reading an organization's tier or limits, offering a configured billing redirect, or operating the Stripe webhook boundary.
Requirements for FFDB platform billing
- Prerequisite — An authorized platform session and the target organization ID for summary, Checkout, or Portal calls.
- Prerequisite — A self-hosted server operator must configure Stripe before Checkout, Portal, or webhook processing is available; Free reads do not require Stripe.
- Required value — organization_id, the intended tier pay_as_you_go or pro, and an Idempotency-Key for each logical Checkout or Portal operation.
- Required value — For provider processing: Stripe credentials, verified raw webhook payloads, provider event IDs, and server-owned organization billing state.
Status: implemented for self-hosted configuration
The released API implements deployment-owned, organization-scoped Free, pay-as-you-go, and Pro billing with durable reads, writes, logical-storage, storage byte-hour, and monthly-active-user metering. During /app/ first-run setup, the owner chooses private or team analytics without tenant charges, or enables a monetized instance with operator-owned Stripe credentials or Stripe Connect. BYO and Connect setup provision the plan catalog automatically; the operator owns the customer relationship, prices, invoices, and Stripe account. After any organization enters billing, FFDB locks the instance to that billing mode and Stripe account until every organization subscription is canceled and reconciled; same-account BYO key rotation remains available.
- Free: $0, two projects, 1 GB storage, 1 million reads, 50,000 writes, and 5,000 MAU each month; reads continue at the limit while write, signup, and storage growth admission pauses.
- Pay as you go: the Free allowances, then $0.20 per GB-month from byte-hours, $0.25 per million reads, $1.50 per million writes through one million and $2.25 per million after, plus $0.005 per MAU through 50,000 and $0.015 after.
- Pro: $7 per month including 10 GB storage, 15 million reads, 750,000 writes, and 50,000 MAU, followed by the same provisioned usage dimensions and invoice reconciliation.
self_hosted_billing_api: implemented
private_and_team: analytics_without_tenant_charges
platform_byo: operator_owned_billing
platform_connect: connected_operator_billing
plans: [free, pay_as_you_go, pro]
free_project_limit: 2
stripe_catalog: automatically_provisioned
usage_reporting: automatic_and_reconciledRead organization billing
GET /v1/organizations/:organization_id/billing and organizationBilling() return the organization's entitlement and instance enforcement policy. GET /billing/usage returns current reads, writes, storage, storage byte-hours, MAU, period bounds, and reporting health; GET /billing/invoices returns verified invoice history. Private, team, and explicitly exempt organizations are unmetered for billing while still retaining usage analytics.
- tier is free, pay_as_you_go, or pro; status reports the current billing lifecycle state.
- billing_enforcement_enabled and billing_exempt explain whether allowances are billable for this organization.
- Free reads continue beyond the included amount; writes, new active users, and storage growth pause at their limits. Paid tiers report positive usage deltas through a durable outbox and reconcile all four dimensions before finalization.
- current_period_start_ms, current_period_end_ms, invoice history, and reporting_status make provider progress visible without treating redirects as payment proof.
const billing = await ffdb.organizationBilling(organizationId);
console.log({
tier: billing.tier,
status: billing.status,
projectLimit: billing.project_limit,
providerConfigured: billing.provider_configured,
});Create Checkout and Customer Portal sessions
POST /v1/organizations/:organization_id/billing/checkout accepts exactly { tier: "pay_as_you_go" | "pro" }. POST /v1/organizations/:organization_id/billing/portal has no request body. Both require an authorized platform session and an Idempotency-Key header, and both return a short-lived redirect URL owned by the configured provider.
- Use a fresh, stable idempotency key for each logical Checkout or Portal request and reuse it only when retrying that same operation.
- Do not mark an organization paid after a redirect; verified Stripe events update the server-owned billing summary.
- When Stripe is not configured, Checkout and Portal fail with 503 billing.provider_unavailable while Free entitlement reads continue to work.
const checkout = await ffdb.createBillingCheckout(
organizationId,
{ tier: "pay_as_you_go" },
{ idempotencyKey: crypto.randomUUID() },
);
const portal = await ffdb.createBillingPortal(organizationId, {
idempotencyKey: crypto.randomUUID(),
});CLI billing commands
The trusted operator CLI exposes the same organization summary and configured provider redirects. Pass the organization ID explicitly and choose exactly pay_as_you_go or pro for Checkout.
ffdb billing status "$FFDB_ORGANIZATION_ID"
ffdb billing checkout "$FFDB_ORGANIZATION_ID" pay_as_you_go
ffdb billing checkout "$FFDB_ORGANIZATION_ID" pro
ffdb billing portal "$FFDB_ORGANIZATION_ID"
ffdb billing usage "$FFDB_ORGANIZATION_ID"
ffdb billing invoices "$FFDB_ORGANIZATION_ID"Raw HTTP contracts
GET /v1/organizations/:organization_id/billing
Authorization: Bearer <platform-session>
POST /v1/organizations/:organization_id/billing/checkout
Authorization: Bearer <platform-session>
Content-Type: application/json
Idempotency-Key: <unique-operation-id>
{"tier":"pro"}
POST /v1/organizations/:organization_id/billing/portal
Authorization: Bearer <platform-session>
Idempotency-Key: <unique-operation-id>Verified Stripe webhook boundary
POST /v1/billing/webhooks/stripe receives the raw Stripe event payload. The server verifies its Stripe-Signature against the configured webhook secret before parsing or applying the event; browsers and application backends do not call this endpoint as a billing mutation.
- Webhook event IDs are processed idempotently.
- Older provider events must not regress newer organization billing state.
- Platform billing is organization-scoped and remains separate from the project-payments capability contract.
POST /v1/billing/webhooks/stripe
Stripe-Signature: <provider-signature>
Content-Type: application/json
<raw Stripe event bytes>FFDB platform billing workflow
- 1. Call organizationBilling() or GET /v1/organizations/:organization_id/billing and inspect tier, status, project_limit, allowances, and provider_configured.
- 2. Treat Free with project_limit 2 as a working entitlement and handle project-limit enforcement instead of assuming billing is unavailable.
- 3. If the operator configured Stripe, call createBillingCheckout() with pay_as_you_go or pro and a stable idempotency key, then send the user to the returned URL.
- 4. For an existing provider customer, call createBillingPortal() with a stable idempotency key and use its short-lived redirect.
- 5. Treat redirects as navigation only; let verified, idempotent Stripe webhooks update state and re-read the billing summary.
Verify ffdb platform billing
Private/team instances retain analytics without billing enforcement; monetized BYO/Connect instances enforce Free allowances and reconcile operator-owned paid entitlements, usage, and invoices.
Troubleshoot ffdb platform billing
- Checkout or Portal returns billing.provider_unavailable — keep Free behavior active and ask the server operator to configure Stripe.
- Project creation reaches the Free two-project limit — do not bypass it; present the current entitlement and available operator-configured upgrade path.
- A redirect succeeds but billing remains unchanged — wait for or diagnose the verified webhook and re-read the summary.
- Webhook signature verification fails — reject the event without applying state and inspect the operator's endpoint-secret configuration.
Continue from FFDB platform billing
- Configure Project commerce independently for application sales.
- Review production security for provider-secret and webhook-boundary operation.