---
title: n1.care Subscription Model — Product Spec Sheet
---

# n1.care Subscription Model — Product Spec Sheet

## Context

n1.care is moving from a prepaid-balance / pay-as-you-go model (bill per LLM call, wallet top-ups via Stripe one-off checkout) to a **subscription model with metered page and report budgets**. The intent is predictable monthly revenue, simpler customer mental model ("you get X pages and Y reports per month"), and a cleaner pricing story on the website.

This document is a **product spec** — it defines the commercial rules, not the implementation. A separate engineering plan will follow once the spec is approved. The spec also includes a **phased modular rollout** so we can ship incrementally rather than one big switchover.

---

## 1. Tiers & Pricing

| | Free Trial | Starter | Professional | Practice | Enterprise |
|---|---|---|---|---|---|
| **Monthly price** | $0 | $149 | $349 | $799 | Custom |
| **Credit card required** | No | Yes | Yes | Yes | Contract |
| **Time limit** | None (lifetime cap) | — | — | — | — |
| **Record parsing** | 100 pages (lifetime) | 500 pages/mo | 2,000 pages/mo | 8,000 pages/mo | Unlimited |
| **Parsing overage** | Not available | $0.20/page | $0.15/page | $0.10/page | — |
| **Reports included** | 1 (lifetime) | 5/mo | 20/mo | 60/mo | Custom |
| **Report overage** | Not available | $29 each | $22 each | $18 each | Custom |
| **Patient management** | Unlimited | Unlimited | Unlimited | Unlimited | Unlimited |
All plans are billed monthly. No annual commitment at launch — we want room to evolve pricing as the model matures.

---

## 2. Subscription Ownership

- **The doctor holds the subscription.** One subscription per doctor account.
- **Quota is shared** across all the doctor's patients. A 500-page/mo plan means 500 pages total across every patient the doctor manages.
- Patient count is unlimited on every tier.
- Billing resolution stays aligned with the existing `get_billing_user_id_sync` pattern in `api-backend`.

---

## 3. Quota Units & Counting

### Page count (record parsing)
- A "page" is **one page of the uploaded file**, counted uniformly across formats:
  - **PDF**: native page count.
  - **DOCX / ODT / RTF** and other reflowable formats: convert to PDF first (existing upload flow already does this), count the resulting PDF pages.
  - **Single image** (JPG, PNG, HEIC, etc.): 1 page.
  - **Image-merge record** (2–50 images via `/records/add/images`): N images = N pages.
- **Password-protected / unreadable file**: rejected on upload before reaching the review step, so never charged (see §5).

### Report count
- One report generation (any CHR workflow) = 1 report against the monthly quota, charged at the moment the doctor clicks **Generate** (see §5).
- Failed generations are recovered via free reprocessing (see §5) — the charge is tied to the click, not the outcome.

---

## 4. Billing Cycle & Quota Reset

- **Monthly, anniversary-based.** A doctor who subscribes on the 15th has their quota reset on the 15th of each month.
- **No rollover.** Unused pages and reports are forfeited at period end — "use it or lose it."

---

## 5. Overage (Soft Cap)

- When a doctor exhausts their page or report quota, behaviour depends on an **"Allow extra usage beyond my plan" toggle** in billing settings.
- **Default: OFF.** New subscribers start with a hard cap until they explicitly opt in. No surprise invoices.
- **When OFF and quota exhausted:** extract/report-generate is blocked with a "Quota exceeded — upgrade plan or enable extra usage" modal.
- **When ON and quota exhausted:** work continues, overage is metered at the per-tier rate (see pricing table), charged on the next invoice.
- **Toggle effect is forward-only.** Flipping the toggle takes effect immediately for subsequent extracts. Disabling mid-period stops further overage from accruing, but **does not reverse or refund overage already accrued earlier in the same billing period** — those charges stand and bill at period end. The T&C names this explicitly.
- **Free Trial has no overage option** regardless of toggle — a hard cap at 100 pages / 1 report, lifetime.

### When quota is charged

- Upload lands a file in a "ready to extract" state — no pages are charged yet. The doctor sees each file's page count on a review screen before committing.
- Quota is charged at the instant the doctor clicks **Extract**. The charge is immutable: one append-only event row per extract click, covering the pages they authorized.
- Report generation follows the same two-step pattern — a review + explicit generate click charges 1 report.
- An unreadable or password-protected file is rejected at upload time (before the review screen) and is therefore never charged.

### Parse failures are recovered via free reprocessing, not refunds

- If parse fails after the charge (LLM timeout, parser crash, transient error), the doctor hits **Reprocess** on the failed record. Reprocess replays the original charge idempotency key — no second charge, no quota refunded or released. The parse runs again against the same record, at no additional cost.
- There is no limit on reprocess attempts per record. A record that keeps failing is a system bug, handled by support — not a commercial refund negotiation.
- Same applies to report generation: `Reprocess` on a failed report is free and does not re-bill.
- This applies equally to in-plan usage and overage. An overage extract that fails still records the overage charge (matching what the doctor authorized); reprocess is free.

---

## 6. Plan Changes (Upgrade / Downgrade)

### Customer-initiated
- **Upgrade: immediate.** New quota available instantly. Stripe issues a prorated charge on the next invoice for the remainder of the current period.
- **Downgrade: at period end.** Doctor keeps their current plan and quota until the renewal date, then switches.
- Upgrades handled via Stripe Customer Portal (no custom UI required) or an in-app "Upgrade" button.
- Overage toggle carries across plan changes.

### N1-initiated price changes
- **New prices apply from the next billing cycle** for existing subscribers — no mid-cycle price increases, ever. Applies equally to base plan pricing and overage rates.
- Existing subscribers receive at least **30 days' advance notice** by email before the new price applies to their renewal. The T&C names this notice window explicitly (§7 of the T&C-rewrite bundle in phase-6).
- Doctors who cancel before their next renewal avoid the new price entirely — the paid period continues at the old price.
- Mechanism: Stripe Price versioning. A new `Price` object is created for the new amount; existing subscribers migrate at their next renewal with `proration_behavior="none"` and the billing-cycle anchor preserved. Old Price objects are retained until no subscribers reference them — never deleted while live.
- No code change required in this rollout — the subscription row already references a Stripe price id, which is what changes. The commitment is operational (procedure + notice) and contractual (T&C clause), not code.

---

## 7. Payment Failure & Cancellation

### Payment failure
- On a declined payment, the account becomes **read-only immediately**. No grace period.
- Read-only = can view existing data, cannot upload records or generate reports.
- Stripe Customer Portal handles payment method updates; account unlocks as soon as payment succeeds.

### Cancellation
- Doctor keeps full access through the end of the paid period.
- After period end: account is **read-only indefinitely** — all data retained, no auto-archive, no deletion.
- Returning doctors reactivate by picking a plan again.
- **No prorated refunds** on cancellation.

### Data export on read-only accounts
- **Data export remains available** on any read-only account (cancelled, payment-failed, or Free Trial exhausted). Doctors always retain the ability to pull their own data out. No time limit on this access.

---

## 8. Free Trial

- No credit card required.
- Lifetime cap: 100 pages (record parsing) and 1 report.
- No time limit — account stays on Free Trial until caps are hit.
- Once either cap is hit: account becomes **read-only, must upgrade to continue.**
- No overage option, no reset. Parse failures are recovered via free reprocessing per §5 (no page/report refund; reprocess replays the same charge).
- Free Trial does **not** count as an Enterprise or paid tier for any entitlement purposes.

---

## 9. Existing User Migration (Prepaid → Subscription)

- All current users are trial-level — none are paying customers with meaningful prepaid balance. **No balance-credit logic required.**
- **Hard migrate every existing user to the Starter plan at cutover** with a `charge_exempt` flag flipped on. They live in the system as **full Starter subscribers with all normal user functionality** — real subscription row, real quota enforcement, full app access, standard billing UI. Migrated users happen to have no Stripe customer/subscription yet because they haven't gone through Checkout (no payment method at cutover); that absence is orthogonal to `charge_exempt`.
- `charge_exempt` is a **narrow flag on the overage billing path only**. When true, billing-service suppresses the Stripe metered-usage post for overage charges. It does not disable base-plan invoicing, quota enforcement, or any user-facing functionality — a `charge_exempt=true` user with a live Stripe subscription continues to be invoiced for the base plan as normal; only overages are waived.
- `charge_exempt` is an admin-controlled flag (not time-bounded, no auto-expiry). Ops flips it off in the admin dashboard when they want a specific user to start being billed for overage usage. For migrated users (no Stripe subscription yet) flipping it off also sets `status = incomplete`, the `ReadOnlyBanner` surfaces with a "Finish setting up your subscription" CTA, and the normal pick-a-plan → Stripe Checkout flow kicks in — without Checkout there's no Stripe subscription item to post overages against, so enforcement wouldn't be meaningful yet. No automated cohort-wide trigger; individual, admin-driven transitions only.
- Any residual prepaid-wallet balance is zeroed out at cutover. (If this changes later — e.g. if we acquire paying customers before cutover — we'll revisit credit mechanics.)
- Legacy pay-as-you-go is retired at cutover (no parallel systems).
- `charge_exempt` is also the mechanism for **ongoing overage-waived accounts** (internal team, partners, demo accounts) — keep it as a permanent feature, not a migration-only hack. Note: it waives overage only, not the base plan. A fully-comped account (no base charge) requires a separate mechanism (Stripe coupon, custom plan) and is out of scope for this rollout.

---

## 10. Enterprise Tier

- Enterprise is **"Contact sales" only** at launch — no published quota, overage, or price.
- Each Enterprise deal is negotiated individually (quota, overage treatment, contract terms, invoicing cadence).
- A published Enterprise spec will only be defined after the first 2–3 real Enterprise deals, once patterns emerge.

---

## 11. Notifications & UX

### Always-on counters
- Persistent in-app indicators visible wherever upload/generate actions live:
  - **"X pages left this month"**
  - **"Y reports left this month"**
- Counters update in real time as uploads complete.

### In-app modals
- **Quota exceeded, overage OFF:** "You've hit your page/report limit. Upgrade your plan or enable extra usage to continue."
- **Quota exceeded, overage ON:** One-time confirmation "You've hit your limit. Additional usage is billed at $X/page and $Y/report. Continue?" — then continues silently.
- **Read-only (payment failed or cancelled):** Banner across the app with update-payment CTA.

### Email notifications (minimum viable set)
- Payment failed.
- Quota reset (receipt for the new period).
- Upgrade / downgrade confirmation.
- Invoice with overage charges (itemised).

---

## 12. Rollout — Dependency Stages, Single Cutover

The rollout is decomposed into five phases as a **dependency structure**, not a release schedule. All five ship together in one deploy — phases describe what builds on what, so the work has a clean order within the delivery.

Detailed engineering specs: [subscription-rollout-master.md](/guides/subscription-rollout/subscription-rollout-master/) and `phase-1.md` … `phase-5.md`.

### Phase 1 — Usage counters + upload review
Page count on every upload, stored on `record_requests.page_count`. Parse decoupled from upload — the doctor sees a review screen with per-file page counts and explicitly clicks Extract to commit. Foundation for every downstream phase.

### Phase 2 — Plan & Subscription model + usage ledger + quota display
`plans` + `subscriptions` + `quota_events` tables. Every extract click calls `/quota/charge`, which writes one immutable `quota_events` row. Billing-service owns usage totals via `SUM(quota_events)` — no cross-service aggregation call. All existing users are hard-migrated to Starter with `charge_exempt = true` at cutover — full subscription row, quota enforced normally; the flag suppresses overage Stripe posts only. Admin endpoint lets ops flip `charge_exempt` off per-user in the admin dashboard. Frontend renders "X of Y" from a unified Contract B.

### Phase 3 — Paid plan enforcement + Stripe + overage
Paid-plan branches added to `/quota/charge` (quota check under `SELECT FOR UPDATE`, 429 on exhaustion, overage when toggled on). New signups land on a pick-a-plan screen → Stripe Checkout. Stripe webhooks keep the subscription row in sync with state changes + period rolls. Overage posted to Stripe metered billing at period end. Parse failures recovered via free reprocessing (idempotency-key replay). Existing migrated users continue to hit the Starter quota branch — the `charge_exempt` flag narrowly suppresses the overage Stripe post; in-quota enforcement and every other path run identically.

### Phase 4 — Portal + emails + read-only banner
Stripe Customer Portal for self-serve plan changes. Four transactional email templates (receipt, payment-failed, plan-changed, subscription-canceled) wired into the Stripe webhook handlers. `ReadOnlyBanner` explains non-active subscription states with a one-click Portal CTA.

### Phase 5 — Public Free Trial
Public landing "Start Free Trial" (no card) → 100 pages + 1 report lifetime. Lifetime-cap branch added to `/quota/charge`. Trial banner + upgrade-on-cap modal drop users into the Phase 3 Checkout flow on cap-hit, preserving their data. Abuse guards: email verification, per-IP signup rate limit, 1 concurrent parse per trial user, daily LLM-spend alert.

---

## 13. Systems Touched (High Level)

For reference only — detailed engineering plan comes later.

- **billing-service** — new Plan/Subscription models, append-only `quota_events` table, Stripe subscription mode, overage metering, single `/quota/charge` API, payment-failure webhook handling.
- **authentication-service** — registration webhook provisions a default subscription (Starter, `charge_exempt = false` for new signups); no JWT claim changes.
- **admin-dashboard** — checkbox on the subscription detail view for flipping `charge_exempt` per user (Phase 2 work).
- **api-backend** — page counting on `/records/add` and `/records/add/images` (Phase 1), decouple parse-queue from upload endpoint, new `/records/batch-extract` that calls `/quota/charge` then queues parse, `/records/{id}/reprocess` that replays the same charge idempotency key. Same pattern on `/reports/generate`.
- **parser-router** — no changes required; optional future enhancement to return MinerU-measured page count for reconciliation.
- **react-frontend** — new Subscription page, plan picker modal, persistent pages-left/reports-left counters, overage toggle, payment-failed banner, quota-exceeded modal.

---

## 14. Outstanding Items (Non-Blocking)

All initial open questions have been resolved and folded into the spec above. Remaining items are process/validation, not product decisions:

1. **Legal / ToS review** — overage disclosure wording, data retention on cancellation.
2. **Finance review** — overage rates vs real LLM cost per page, collection risk on immediate read-only policy.
3. **Support runbook** — draft responses for: payment failed, wrong quota shown, cancelled-but-charged, upgrade not reflected, data-export-on-read-only.

---

## 15. Verification

This spec is verified complete when:

- [x] Every cell of the pricing table is filled with a committed number.
- [x] All initial product decisions resolved (subscription ownership, cap behaviour, overage opt-in, quota units, reset cadence, rollover, failure refunds, plan changes, payment failure, cancellation, free trial, migration, notifications, data export, Enterprise handling).
- [ ] Legal / finance review completed on items in §14.
- [ ] Stripe products and prices defined in sandbox matching the pricing table (dry run only, no production wiring yet).
- [ ] A 5-doctor shadow run through Phase 1 (counters only) shows accurate page and report counts for 2 weeks of real traffic.
- [ ] Support runbook drafted per §14 item 4.
