Case Study / Build 03
Client Portal AI Build
A production-shaped support portal for client operations, retainer visibility, documentation, billing, and controlled Cloudflare migration.
Greg's take
A support portal for client retainers sounds simple. It is not. It is: request tracking, shared visibility across a client account, billing transparency, documentation versioning, admin oversight, audit trails, identity control, and a reliable data layer underneath all of it.
Most teams solve parts of this with separate tools. A spreadsheet handles billing. Email handles requests. Notion handles documentation. The result is a client relationship managed across five disconnected systems with no single view.
This build started as a lightweight prototype and moved toward a real Cloudflare-backed architecture - slice by slice, with a QA gate at each step.
01 / The problem
Support managed by email. No shared visibility.
No client visibility
Clients had no shared view of open requests, progress, or history. Every status update required an email exchange.
No billing transparency
Hours logged, budget consumed, and remaining retainer balance were only visible in spreadsheets that had to be shared manually.
No documentation layer
SOPs, process notes, and technical references existed in files and Notion - not in a client-facing, versioned system.
No shared client instance
Multiple stakeholders from the same client organisation had no shared workspace to see common requests and shared resources.
02 / The system
A business system. Not a portal UI.
The system was built to provide genuine operational value - shared visibility, structured data, identity control, and operational controls - not just a frontend that looks like a portal.
Multi-client shared instance model
Everyone attached to the same client account sees the same requests, status updates, billing summary, documentation, and downloads. No separate admin panel required to share visibility.
Request lifecycle with structured status
Requests move through defined states: submitted, in review, in progress, blocked, completed. Each state change is recorded. Comments are threaded against requests, not floating in email.
Billing and hours visibility
Hours logged against requests are visible to clients in context. Retainer balance, hours consumed, and hours remaining are surfaced at the account level without a finance email.
Documentation system
Versioned documentation with metadata and body endpoints. SOPs, technical references, and process notes live in the portal, not in a shared drive.
Admin controls and audit logging
Admin view with full request management, status override, hours adjustment, and activity log. Every change is timestamped and attributed.
03 / Architecture
Cloudflare-native stack with controlled migration path.
System architecture
Identity layer
Cloudflare Access
SSO / JWT issuance
JWT validation
Workers middleware
Client identity
org_id + user_id
API layer (Cloudflare Workers)
/requests
CRUD + status
/comments
Threaded
/hours
Logging + billing
/docs
Metadata + body
/admin
Controls
/audit
Activity log
Data layer
Cloudflare D1
Structured data
Cloudflare R2
File attachments (planned)
Cloudflare Pages
Frontend hosting
Frontend
API
Database
Identity
04 / Build progression
Slice-by-slice migration. QA gate at every step.
The build started as a localStorage static prototype and is progressively migrated toward Cloudflare-backed architecture. Each slice has defined acceptance criteria before the next slice starts.
Static prototype - localStorage
CompletedFull UI built on localStorage. All request, comment, billing, and documentation functionality working in-browser. No backend dependency. Rapid validation of the UX model and data structure.
Cloudflare Access / JWT identity layer
CompletedCloudflare Access added as the identity provider for portal.greg-staunton.com. JWT validation middleware built into Workers. Test auth flows verified before any data APIs were wired.
D1 database schema and base Workers API
CompletedD1 schema defined for requests, comments, hours, users, and documents tables. Base Workers API with routing and JWT middleware. Data migration from localStorage state verified in test environment.
Request and comment migration to D1/API
CompletedRequest creation, status changes, and threaded comments moved from localStorage writes to D1 API writes. Frontend updated to use fetch() against Workers API with JWT in headers. QA pass on create, read, update, and delete paths.
Hours logging and documentation endpoints
CompletedHours logging against requests moved to API/D1. Documentation metadata and body endpoints added. Client-facing billing summary calculated server-side and returned via API rather than computed from localStorage.
Production hardening pass
CompletedHardening pass on portal.greg-staunton.com: test auth bypass removed, JWT expiry handling, error states, loading states, empty states, and edge case handling across all request flows.
R2 file attachments and admin audit log
PlannedR2 storage for file attachments on requests and documentation. Full audit log export for admin review. Structured activity feed per client account.
05 / AI-assisted delivery
How AI was used in this build.
AI assistance was used throughout, but with clear human decision points at architecture, QA, and deployment. The Tanya Build Cockpit provided the structured scaffolding that made each session productive.
Architecture design
AI used for architecture option comparison: localStorage-only vs. D1-backed vs. full Cloudflare stack. Trade-offs documented. Human decision made before build started.
API implementation
Workers API routes generated with AI assistance using structured implementation packets - each route specified with method, auth requirement, request shape, and response contract before code generation.
D1 schema and migration
Schema design reviewed against the data model and access patterns before finalisation. Migration scripts generated and verified in test D1 environment before production deployment.
QA and hardening
AI used to generate QA checklists for each build slice, then human-run against the actual system. Issues found were fixed before the slice was marked complete.
06 / Production hardening
What was hardened before calling it production-shaped.
The distinction between migration-ready and production-shaped is a deliberate one. The hardening pass addressed the specific gaps between a working prototype and a system operators can rely on.
Test auth bypass removed
Development-only authentication shortcuts removed before portal.greg-staunton.com was opened to actual clients.
JWT expiry handling
Expired or invalid JWT tokens result in a clear session refresh prompt rather than silent failure or broken state.
Error states on every API call
All fetch() calls have explicit error handling. No silent failures. Operators see a clear error message when an API call does not succeed.
Loading states throughout
Every async operation shows a loading indicator. Buttons disable while submission is in progress to prevent duplicate requests.
Empty states for all views
Every list or data view handles the empty case - no blank screens when there are no requests, comments, or documents yet.
Org-level data isolation verified
Each API request validates that the requesting JWT org_id matches the resource org_id. Cross-client data access not possible.
07 / Why it matters
A real business system with real operational constraints.
The Client Portal is the most complex build in the pl8ypus portfolio. It is not a demo application or a proof of concept. It is a working system being progressively migrated to production with real clients using it.
It demonstrates multi-layered system thinking
Identity, API, database, file storage, admin controls, audit logging, and frontend UI all have to work together. The architecture decisions at each layer have to account for the layers above and below.
It demonstrates controlled migration discipline
Moving from localStorage to Cloudflare D1 in slices, with working state at every step, is the same migration discipline needed inside real customer environments. Big-bang rewrites are not an option with live users.
It demonstrates AI-assisted delivery at scale
A system of this complexity - six build slices, multiple data models, Cloudflare infrastructure, identity layer - was built with AI assistance throughout. The Tanya Build Cockpit system was the operational scaffold that made it possible.
It is honest about its current state
R2 file attachments and the full audit log export are planned, not built. The system is described as production-shaped, not production-complete. That distinction is intentional and important.
Real client workflow, not a demo shell
Built around Greg's real support and retainer model.
The Client Portal is not a portfolio piece designed to look like a real system. It is built around Greg's real support and retainer workflow - the one used with actual clients. The scope, the feature set, and the data model came from real operational needs, not from a case study brief.
That means the portal handles real requests, real documentation, real billing context, shared account visibility for real client contacts, and a real migration path toward the Cloudflare infrastructure that will support it at production scale. It is being hardened slice by slice because real clients are in the workflow - not because a demo needed to look more impressive.
Requests come from real clients
The request lifecycle - submit, in review, in progress, blocked, completed - was designed around how Greg's client retainer support actually works, not a hypothetical workflow.
Billing context is real
Hours logged, retainer balance, and consumed time are surfaced for real client accounts - not placeholder numbers in a mockup designed to look believable.
Documentation is real
SOPs, process notes, and technical references served through the portal are the same documents that would otherwise live in email attachments and shared drives.
Multiple client contacts on the same account
The shared client instance model was designed because real client organisations have more than one person who needs visibility. That constraint shaped the data model from day one.
Migration is driven by real operational pressure
Each D1 migration slice was prioritised by what a real client workflow actually needed - not what made a good portfolio story. Request writes came first because that is what clients use most.
Hardening was done for real users
The production hardening pass on portal.greg-staunton.com - JWT expiry handling, error states, empty states, org-level data isolation - was done because real users would encounter these paths.
Greg's take
A client portal sounds simple until it has to behave like a real support system. Requests, updates, documents, billing visibility, attachments, admin controls, audit trails, identity, and multiple people from the same organisation who all expect to see the same data.
That is where the toy version breaks. The toy version looks good in a demo, handles one user on a good day, stores everything in localStorage, and has no plan for when the session expires, the attachment needs to persist, or someone from finance asks why their hours do not match the invoice.
This build started as a lightweight prototype and moved slice by slice toward a Cloudflare-backed operating system. Each slice had acceptance criteria. Each migration step had a verification gate. The point was not to fake production. The point was to earn it - one layer at a time, with the system working at every step of the journey.
The test of a portal is not whether it looks professional in a demo. The test is whether a real client with a real question can find their answer without sending an email. That is the bar this build was designed to clear.
08 / What this proves
Capabilities demonstrated by this build.
Architecture
- Cloudflare-native stack design (Pages + Workers + D1 + R2 + Access)
- Multi-tenant data model with org-level isolation
- JWT-based identity with middleware validation pattern
Build discipline
- Slice-by-slice migration with working state at every step
- Acceptance criteria defined before each migration slice starts
- Production hardening pass as a distinct build phase, not an afterthought
AI-assisted delivery
- AI used at architecture, implementation, QA, and documentation layers
- Structured prompt patterns and project memory maintained across sessions
- Human decision points at every architectural and deployment choice
Operator readiness
- Non-technical clients can manage requests and view billing without training
- Admin controls and audit log designed for operational oversight, not developer use
- System state honest and visible - no hidden complexity or magic behaviour
Interested in a similar build?
For enterprise portal builds, Cloudflare infrastructure, applied AI systems, or marketing operations workflow design.