Skip to content

fix(billing): distinguish team seats from AI model access - #3415

Merged
benjaminshafii merged 1 commit into
devfrom
feat/billing-seats-vs-ai-clarity
Aug 1, 2026
Merged

fix(billing): distinguish team seats from AI model access#3415
benjaminshafii merged 1 commit into
devfrom
feat/billing-seats-vs-ai-clarity

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

Why

Seats and AI model access both cost $10 per user and were rendered as two near-identical cards. The two products bill completely differently:

  • Team seats — Stripe mode: "setup". Charges $0 today; only bills for users beyond the free 5.
  • AI model access — Stripe mode: "subscription". Bills immediately for every active member.

The page never said this, so a user could buy model access believing they had bought "5 seats" and be surprised by the charge. Nothing on the page stated the whole-team total before checkout.

What changed

One statement instead of two lookalike cards. Billing now opens on Your subscriptions, listing Team seats and AI model access as separate line items — each with its own status badge and price — summed into a Total. The fraimz asserts the two rows arithmetically equal the total, so no charge can appear unaccounted for.

Every action says when to use it. Bare buttons are replaced by action rows pairing each control with a plain-language sentence, e.g. Add paid seats now reads "Only needed once you pass 5 users. Saves a card now, then charges $10.00 per extra user each month" — which is the honest description of Stripe setup mode.

The whole-team price is stated before checkout. When model access is off, the CTA guidance reads "Turning this on costs $20.00 per month for your 2 members, not $10.00."

Unconfigured deployments no longer lie. Without Stripe keys the page previously still quoted $10/user and offered a seat checkout the operator could never complete. It now says seat billing is not set up and there is no member limit.

Seat usage is visual. A segmented meter replaces the flat "1 free seat left" sentence, with overflow shown proportionally once a team passes the free tier.

New primitives

Built on existing Den primitives (DenCard, DenBadge, DenSectionHeader, DenButton); added where none existed:

  • DenUsageMeter — segmented allowance meter for "x of y included" limits
  • DenActionRow / DenActionList — control paired with when-to-use guidance
  • DenLineItemRow / DenMarkTile — read-only statement rows (the counterpart to DenSelectableRow)
  • DenNotice extended with neutral and warning tones

The page also drops its custom rounded-2xl + drop-shadow sections for DenCard, which forbids shadows.

Tests

  • pnpm typecheck (den-web) — clean, no any/casts added
  • pnpm fraimz --flow billing-seats-vs-ai --stack denPASSED, 3/3 frames, driven through the real den stack (MySQL + den-api + den-web) via CDP against the seeded alex@acme.test org

Proof posted as a follow-up comment.

Reviewer note

The guidance copy asserts two Stripe behaviors I inferred from stripe-billing.ts but did not exercise against live Stripe: that cancelling model access runs to period end, and that removing a member lowers the AI bill at renewal rather than immediately. Both are worth confirming against actual proration settings before this ships to hosted.

Made with Cursor

Seats and model access both cost $10 per user and were rendered as two
near-identical cards, so people bought one believing they had bought the
other — and seat checkout runs in Stripe setup mode ($0 today) while model
access charges the whole team immediately, which the page never said.

Billing now opens on a single statement that names both line items with
their own status and price and sums them into a total, so no charge is
unaccounted for. Each card states who is billed, and every action is paired
with a sentence describing when to use it instead of standing alone as a
bare button. Deployments without Stripe keys no longer quote prices or
offer a checkout the operator cannot complete.

Adds DenUsageMeter, DenActionRow/DenActionList, and DenLineItemRow/DenMarkTile,
and extends DenNotice with neutral and warning tones.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview Aug 1, 2026 4:26pm
openwork-den Ready Ready Preview Aug 1, 2026 4:26pm
openwork-den-worker-proxy Ready Ready Preview Aug 1, 2026 4:26pm
openwork-diagnostics Ready Ready Preview Aug 1, 2026 4:26pm
openwork-landing Ready Ready Preview, v0 Aug 1, 2026 4:26pm

@benjaminshafii

Copy link
Copy Markdown
Member Author

fraimz — ✅ PASSED

1 passed · 0 failed · 0 skipped — run 2026-08-01T16-25-21-444Z

Full frame proof with validated screenshots: evals/results/2026-08-01T16-25-21-444Z/fraimz.html (re-run: pnpm fraimz --flow billing-seats-vs-ai)

✅ billing-seats-vs-ai — Billing separates Team seats from AI model access and explains every action

User-facing flow demo

  1. Billing opens on a single statement that lists Team seats and AI model access as separate line items and sums them into one total

    🎙 Alex opens Billing and sees one statement instead of two lookalike cards. Team seats and AI model access are listed as separate line items, each with its own status and its own price, and the two add up to a single total.

    • ✅ Admin token can load the organization billing summary
    • ✅ Billing payload exposes the stripe summary
    • ✅ Admin API sign-in minted a den-web browser session
    • ✅ The statement names the Team seats line item
    • ✅ The statement names the AI model access line item
    • ✅ The statement carries a Total row
    • ✅ The statement renders exactly three rows: seats, AI, and the total
    • ✅ The Total equals Team seats plus AI model access
    • 📸 billing-seats-vs-ai-01-billing-statement.png — 8 validations passed
    billing-seats-vs-ai-01-billing-statement.png
  2. The Team seats card explains this deployment's seat charge in plain language and never implies that seats include model access

    🎙 Alex reads the Team seats card. It says in plain language what this deployment charges for seats, and it never promises that seats include model access.

    • ✅ The card is titled Team seats
    • ✅ The seats card explicitly distinguishes itself from AI model access
    • ✅ An unconfigured deployment says seat billing is not set up and there is no member limit
    • ✅ An unconfigured deployment never offers a seat checkout the operator cannot complete
    • 📸 billing-seats-vs-ai-02-seats-card.png — 6 validations passed
    billing-seats-vs-ai-02-seats-card.png
  3. The AI model access card names who is billed, and every button on the page is paired with a sentence describing when to press it

    🎙 Alex scrolls to AI model access. Every action on the page now comes with a sentence saying when to use it, so no button can be pressed without knowing what it does or who gets billed.

    • ✅ The card is titled AI model access
    • ✅ The AI card states that it is separate from seats
    • ✅ A configured deployment warns that every active member is billed
    • ✅ Every action button in the seats and AI cards is paired with guidance text
    • 📸 billing-seats-vs-ai-03-ai-card-actions.png — 5 validations passed
    billing-seats-vs-ai-03-ai-card-actions.png
    • ✅ Script frame 1 narrated: "Alex opens Billing and sees one statement instead of two lookalike cards. Team seats and"
    • ✅ Script frame 2 narrated: "Alex reads the Team seats card. It says in plain language what this deployment charges f"
    • ✅ Script frame 3 narrated: "Alex scrolls to AI model access. Every action on the page now comes with a sentence sayi"

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warden security clearance: clear. No new security issues found in this diff (354ba2f4d90e78375e81713899f7ee59bbbb795e). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

@benjaminshafii
benjaminshafii merged commit 77e3e86 into dev Aug 1, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant