FM Consulting
Customer Portal Improvement Program
A self-service account portal for team, plan, and billing management — paired with an internal account-health view that scores churn risk from real usage data, not guesswork.
Case study
The problem
Two related problems, one portal. Customers routed basic account changes — adding a teammate, upgrading a plan, understanding what a plan change would actually cost mid-cycle — through support tickets, because the portal could display account state but not change it. On the other side, the first real signal of churn risk was almost always the cancellation itself: an account with a shrinking, disengaged team could be paying for far more seats than it used for months, with nothing in any internal view surfacing that until the account left and someone asked why.
The solution
The portal became genuinely self-service: a team page with seat-limit-aware invites and role changes, and a billing page where changing plans shows the actual prorated credit and charge for the rest of the current cycle before the customer confirms anything — not a vague "new price starting next month." Canceling requires picking a reason from a fixed list rather than a free-text box nobody reads later. On the internal side, every active account gets a single health score built from two real signals — what share of the team logged in recently, and how much of the plan's seat capacity is actually provisioned — so a support or success lead can see which accounts are quietly drifting before renewal, not after.
How it was built
Proration is a real, deterministic calculation: the unused days left in the current billing cycle are credited at the old plan's per-seat rate, the same days are charged at the new plan's rate, both scaled by actual seat count — checked by hand against known-good numbers (an upgrade nets positive, a downgrade nets an exact negative, and changing to the same plan nets exactly zero) before any UI was built on top of it. The health score deliberately combines two signals that aren't the same thing wearing two hats: engagement (is the team logging in) and plan fit (is the account sized to what it's actually using) — an account with eight people on a two-hundred-fifty-seat plan is a downgrade risk independent of how often those eight people log in, and that distinction is what makes the score a real diagnostic instead of a single reused number. There's no real login system — a lightweight account switcher stands in for auth so every seeded account's state is explorable directly — but every customer-facing page reads the active account from a cookie, which has a useful side effect: pages that read a cookie are rendered per-request automatically, so they can't silently go stale the way a statically-prerendered admin page can. That exact staleness bug was caught in a sibling project on this site; the admin pages here carry an explicit override for the same reason, applied from the start rather than rediscovered. The seed data surfaced a smaller, honest bug during development, too: an early version of the random name generator could assign the same person's name to two different seats on one account, caught by actually reading the rendered team page rather than trusting the row counts.
More projects

- Customer Experience
- Platform
Knowledge Portal
A self-service help center with real full-text search (not AI) — publish Markdown articles, search them by BM25 relevance ranking, and let every zero-result search and helpfulness vote surface exactly which articles are missing or failing.

- Platform
- Customer Experience
Community Event Coordination Platform
A private, invite-only event coordination platform for a closed group — poll-first event planning with real-time collaborative voting, a venue leaderboard, and an installable mobile experience.

- AI
- Customer Experience
AI Support Workflow Optimization
A platform-agnostic AI triage layer for support teams — connects to Zendesk, Salesforce, or any ticketing system, classifying tickets and drafting ready-to-send responses with a human review step before anything goes out.