ProjectsWork with meWritingAboutSay hi →
ON THIS PAGE01/07
  1. 01The problem
  2. 02Constraints
  3. 03What I shipped
  4. 04The hard parts
  5. 05Result
  6. 06What I'd do differently
  7. 07Takeaway
ON THIS PAGE01/07
  1. 01The problem
  2. 02Constraints
  3. 03What I shipped
  4. 04The hard parts
  5. 05Result
  6. 06What I'd do differently
  7. 07Takeaway
All writing
Client Portal — one branded hub for an agency drowning in tabs
Case study

Client Portal — one branded hub for an agency drowning in tabs

Leads in one tool, chats in another, campaigns somewhere else. The agency needed a single branded place that answered "what's actually happening this week?" So I built it.

eBy eloi·July 20, 2026·3 min read
SHARE
Client
Private — agency (SaaS · white-label)
Role
Design · Full-stack · Deploy
Year
2023–present
Stack
Next.js · Supabase · Stripe · AWS

The problem

The agency was drowning in tabs. Leads lived in one CRM, client chats in another tool, campaigns across a third. Every time an account manager needed to answer “what's happening for this client this week?” it meant opening five tabs and piecing the story together.

They needed one place — per client — where everyone (agency-side and client-side) could see the same picture in real time.

Constraints

  • Multi-role from day one. Agency owners, account managers, client admins, client viewers. Each needed a different view. Nobody should ever see anything they shouldn't.
  • Fully white-labelled. Each client's own subdomain, logo, colors, and email sender. Not the agency's.
  • Live CRM data, not exports. Stale weekly reports were part of the original problem.
  • Real billing. Subscription tiers, prorations, upgrades. Not a stub.

What I shipped

  • Multi-role access with row-level permissions. Every seat sees only what it should.
  • Live dashboard pulling directly from the CRM API. Refreshes on its own.
  • In-app messaging, booking, and file exchange — the three most common reasons to leave the portal, now inside the portal.
  • Stripe subscription billing with a customer portal for upgrades, downgrades, and card management.
  • Automated onboarding email sequences triggered by Stripe events, not by someone remembering to send them.
  • Self-serve white-labelling — clients paste a DNS record, portal detects it, SSL gets auto-provisioned.
  • Deployed on AWS + Vercel with staging and production separation, backups, and uptime monitoring.

The hard parts

Row-level security done properly. Multi-tenant SaaS with four role types is where “we'll figure out permissions later” becomes an incident report. I built the permission model in Supabase RLS policies before the first UI screen. Every query gets automatically scoped. The app literally can't leak data because the database refuses to serve it.

White-label DNS and SSL that clients set up themselves. Each agency client needed their own domain pointing at the portal. I built a self-serve verification flow — paste a domain, get a DNS record, portal auto-provisions SSL via Let's Encrypt. No support ticket needed for new client onboarding.

Keeping billing state in sync. Stripe subscriptions can pass through a dozen statuses. I use webhooks with retries and idempotency keys, plus a nightly reconciliation job that re-syncs subscription state as a belt-and-braces. Never had a “paying customer locked out” incident.

Result

Live since 2023, in daily use by the agency. New client onboarding went from half a day of manual setup to something the client does themselves in under 15 minutes. They've since added tiers, iterated on the dashboard, mostly without touching the codebase.

What I'd do differently

Invest in a design system earlier. The early screens got iterated so much that by v1 half the design tokens were duplicated across components. Proper Figma library + code tokens on day one would've saved a full week of consolidation later.

Takeaway

The value in a portal like this isn't any individual feature. It's the removed switching cost. Every tab you can collapse into one place is a small productivity win — but the aggregate is a genuinely different way of running client work.

NEWER POST

How I ship end-to-end products solo without dropping quality

Article
OLDER POST

Local Business POS — built for Filipino retailers, priced for them too

Case study
KEEP READING

You might also like

  • Case studyJun 30, 2026

    Local Business POS — built for Filipino retailers, priced for them too

    Filipino retailers were stuck between global POS tools that don't fit and paper ledgers that don't scale. So I built one that speaks their language — GCash, Maya, BIR receipts.

  • Case studyMar 25, 2026

    Membership & Shop — memberships, merch, and donations on PH-native rails

    A community needed to grow up fast — memberships, a merch shop, recurring donations. Standard checkout tools don't play nicely in the Philippines, so I built one that does.

designed, built & shipped
by one person.
Site
ProjectsWritingWork with meProcessAbout
Where
Philippines
Working worldwide
GMT+8 · async-friendly
Talk
eloi · made with care · © 2026
Privacy·Terms·Sub-processors
Available for new projects