ADR-0023: V10 monetization model — per-seat business pricing, free for individuals¶
- Status: Accepted, with the pricing dimension reopened by ADR-0053 (per-seat and free-for-individuals are no longer settled; an enterprise self-host licence is added).
- Date: 2026-09-18
- Deciders: Jian (product owner)
Context¶
A product/business review of the repo (2026-09-18) found the full V5 multi-tenant substrate
(Postgres + RLS, Better Auth workspaces, an encrypted BYOK vault, a durable hosted run manager)
built and tested, but zero monetization code anywhere in the tree — no stripe, billing,
subscription, or pricing reference in packages/ or apps/. ADR-0022 named the gap but
deferred it: "Free/paid tier metering sits on the deployment record, later", without settling
what gets metered. KAN-1514 was filed to force that decision before KAN-1436 (webhook ingress +
deployment record) writes a schema that assumes the wrong dimension.
A planning discussion (2026-09-18) settled the business model: this is a B2B SaaS — free for individuals, monthly/yearly per-seat pricing for businesses.
Decision¶
Pricing dimension: per-seat subscription (monthly or yearly), not usage metering. Runs/month and live-workflow-count metering (the alternatives ADR-0022 gestured at) are both rejected as the primary pricing lever — seats are.
- Free tier: individual / personal workspaces. No billing integration, no enforced cap, for a workspace used by a single person for their own agents. This matches the developer/self-host persona this project has served since V1-V9 — they should never hit a paywall for personal use.
- Paid tier: business/enterprise workspaces, billed per seat. A workspace with more than one member is a business workspace; its subscription quantity tracks its member count.
- Seats map directly onto Better Auth's
organizationmember count (ADR-0015). No new seat-tracking concept is needed —workspace_members(ADR-0014) already is the seat list. Inviting a member increases billed seats; removing one decreases them. - Personal vs business is a self-declared workspace attribute at creation time (
workspace.plan:personal|business), not a verified business-entity check. No KYB, no automated detection. This is a trust-based boundary: a business could create solo "personal" workspaces to avoid billing. Accepted as a known, bounded risk at this stage — there is no paying customer yet to defraud, and building fraud detection ahead of having a single subscriber would be exactly the over-engineering this project's staged-scoping approach exists to avoid. Revisit if abuse is actually observed post-launch, not before. - Enforcement point: the V10 "Deploy" action (KAN-1438). Deploying a live workflow on a business-plan workspace requires an active subscription in good standing; personal workspaces deploy freely.
Consequence for KAN-1436's deployment-record schema¶
The deployment record does not need run-count or usage-metering columns for pricing purposes.
This supersedes ADR-0022's "shaped to allow counting" framing, which assumed usage metering would
be the pricing lever. deployments stays scoped to lifecycle only: workspace/spec reference,
webhook URL, status (live/paused), timestamps. Billing state lives in a separate subscriptions
table (new, this ADR), not folded into the deployment record.
New component: billing integration (not previously scoped anywhere)¶
This is real new scope, not assembly of existing V5/V9 primitives like the rest of V10:
- A payment provider integration — Stripe Billing (per-seat subscriptions, Customer Portal for self-serve plan management) is the natural default given this is a solo-operator project; no other provider was evaluated because Stripe's per-seat subscription primitive is a direct match and there's no existing reason (data residency, an existing vendor relationship) to look further.
- New tables:
workspace.plan(personal|business) andsubscriptions(workspace_id, stripe_customer_id, stripe_subscription_id, seat_count, status, current_period_end). - A webhook handler syncing Stripe subscription events (created/updated/canceled) into
subscriptions, and syncingworkspace_memberschanges (invite/remove) into the Stripe subscription's quantity. - This needs its own card(s) under EPIC-215 (V10), not folded into KAN-1436/1437/1438's existing scope.
Alternatives considered¶
| Option | Why not |
|---|---|
| Runs/month metering | Doesn't match the chosen B2B-seat positioning; usage-based pricing punishes exactly the heavy-usage customers a per-seat model wants to keep happy, and requires building usage tracking/rating infrastructure this project doesn't have. |
| Live-workflow-count metering | Simpler than usage metering but still the wrong lever for a per-seat B2B pitch — a 50-person company running 2 workflows shouldn't pay less than a 2-person company running 10. |
| Flat-rate SaaS fee regardless of seats | Leaves revenue on the table for larger customers and doesn't match "per seat" as stated; per-seat billing was the explicit ask. |
| Automated business-entity verification (KYB) at signup | Meaningful engineering effort with no fraud observed yet to justify it; self-declared plan choice is the appropriate v1 answer, revisited only if abuse actually appears. |
Consequences¶
packages/servergains a new external dependency (Stripe SDK) and a webhook endpoint, alongside Better Auth and the LiteLLM gateway it already depends on.- KAN-1514 is resolved by this ADR and should be closed with a reference to it.
- A new billing-integration epic/cards are needed under V10 before the "Deploy" action (KAN-1438) can actually enforce the paid tier; KAN-1436/1437 (webhook ingress, connector secrets) are unaffected and can proceed independently, since they don't depend on billing state.
workspace.planbecomes a new authorization dimension alongside V6's RBAC roles (ADR-0017) — V6's RBAC middleware and this ADR's plan-gating middleware are separate concerns that happen to both gate on workspace context, the same pattern ADR-0015 established for auth and RLS sharing oneworkspace_idseam.
Open questions for implementation to revisit¶
- Whether a personal workspace can later be converted to a business workspace in place (adding a
second member) versus requiring a new workspace — affects whether
workspace.planneeds to be mutable post-creation. - Grace-period behavior when a subscription lapses (payment failure): read-only degrade vs hard lock, and how long before a lapsed business workspace's live deployments are paused.
- Whether yearly billing gets a discount versus monthly, and what the actual per-seat price point is — pure pricing/GTM decisions out of scope for this ADR.