pl8ypus
Systems / Client Portal AI Build

Case Study / Build 03

Client Portal AI Build

A production-shaped support portal for client operations, retainer visibility, documentation, billing, and controlled Cloudflare migration.

Migration in progress Cloudflare D1 Workers API Access/JWT Cloudflare Pages

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

Cloudflare Pages HTML / Tailwind Vanilla JS

API

Cloudflare Workers REST routes

Database

Cloudflare D1 SQLite-compatible

Identity

Cloudflare Access JWT validation

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.

1

Static prototype - localStorage

Completed

Full 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.

2

Cloudflare Access / JWT identity layer

Completed

Cloudflare 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.

3

D1 database schema and base Workers API

Completed

D1 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.

4

Request and comment migration to D1/API

Completed

Request 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.

5

Hours logging and documentation endpoints

Completed

Hours 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.

6

Production hardening pass

Completed

Hardening 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.

7

R2 file attachments and admin audit log

Planned

R2 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.

View all systems