How I turned my SaaS's client portal into a white-label kit other devs can resell
DEV Community

How I turned my SaaS's client portal into a white-label kit other devs can resell

Running a healthcare SaaS (CareRoots) meant building a client-facing portal - a place where each clinic could log in, see their own patients/orders, and get notified when something needed attention. Straightforward enough.

The catch: the portal's "admin" side had zero independent identity. It fully piggybacked on the main app's own session. Which was fine, right up until I decided to pull it out into a standalone white-label kit other developers could install for their own clients - and realized the admin panel couldn't exist without the rest of CareRoots running underneath it.

What had to be built from scratch

Turning a tightly-coupled internal feature into something that stands on its own meant building, essentially from zero:

  • A real admin login system - its own session, its own cookie, its own config file, fails closed if unconfigured
  • A portal-local Cloudflare analytics library (previously reached across into the parent app's own integration)
  • Its own SMTP mailer (same story - used to reach into the site root)
  • Its own push-notification helper, so a client with a new order gets a real browser push, installable as an app via "Add to Home Screen," no App Store review needed

None of this is exotic engineering. It's just the unglamorous work every "extract this into a product" project runs into: the thing that worked because it was one system has to be re-taught how to be two systems that don't trust each other by default.

3 real bugs the extraction surfaced

Worth sharing because none of them were "hard" bugs - they were the kind that hide in code that's never been forced to run standalone before:

  • The auto-slug generator lowercased after stripping non-alphanumerics, not before. So "Test Client" became "est-lient" instead of "test-client" - the uppercase T and C got treated as different characters than their lowercase selves during the strip step, and vanished. Any business name with capital letters got mangled.
  • A failed reply-email was computed but never rendered anywhere. The code correctly detected that sending failed - it just never told the user. A client hitting "reply" during an outage would see... nothing. No error, no success message. Looked like the button did nothing.
  • A login page called a helper function (e(), an HTML-escaping shortcut) without requiring the file that defines it. This one's on me - introduced during the extraction itself, not a pre-existing bug. Caught it because the page threw a fatal error the first time it actually ran standalone, isolated from the rest of the app where that helper happened to already be loaded by something else.

The white-label problem nobody warns you about

Here's the one that isn't a "bug" exactly: the kit's shipped defaults were, at first, literally CareRoots' own brand colors and fonts. Made sense - I copy-pasted the real thing to start from. But shipping a white-label product with your other product's exact visual identity baked in as the default undercuts both: it dilutes the original brand, and it tells the kit's actual buyer "here's someone else's colors, good luck making this look like yours."

Redesigned it to a distinct palette (charcoal + ochre/amber, Sora + Work Sans) that's recognizably its own thing - not a reskin excuse, an actual identity.

What it does today

  • Your own admin panel, session fully separate from every client's login
  • Each client gets their own portal: orders inbox, reply-by-email
  • Real push notifications via the Web Push API - installable, no App Store
  • Per-client Cloudflare traffic analytics (degrades gracefully if a client has no zone configured)
  • Document storage with tiered limits and virus scanning
  • Bilingual EN/PT client-facing UI
  • Plain PHP, no build tools, no framework, runs on SQLite - any standard PHP host

First 5 buyers get it for $79 (then $249), one-time payment, 30-day money-back guarantee.

👉 https://fromkitchentocode.com/go.php?to=chk-devto

Happy to go deeper on the session-isolation approach, the push/PWA setup, or anything else in the comments.

Comments

No comments yet. Start the discussion.