We Built a Gated Training Portal. The Second Tenant Is One JSON File.
Delivering AI tools to a client is half the job. The other half is making sure they actually use them.
We keep landing here: a workflow gets built, a prompt library gets written, and then the client is left with a folder of Google Docs and a Loom recording. A month later they have forgotten half of it and the folder is buried under everything else.
So we built a training portal. Not a course platform, not a white-labeled SaaS subscription. A clean, gated page that lives inside our site and belongs to the client who paid for it.
What the Portal Does
Each tenant gets a password-protected index page with two things: a module player and a prompt library. The modules are short video walkthroughs with full transcripts. The prompt library is a list of ready-to-use prompts with copy buttons. No account creation, no email, no onboarding flow. One password, one page, everything they need.
The first client in the system is an agricultural input supplier. Their portal opens with a nine-minute module covering Claude plan selection, Chat vs. Cowork, and how to build a Project from real company files. The prompt library starts with a structured interview prompt that walks them through setting up their own AI workspace, pre-loaded with everything we already know about their business so Claude stops asking about it.
The Architecture Decision That Made This Scale
The session authentication is a signed cookie: tenant|expiresAt|version with HMAC-SHA256 over those three fields, verified against the requested tenant on every request. If we need to revoke every active session for one client, we increment their accessVersion integer in the JSON file and every outstanding cookie immediately fails verification. That's it. No database, no session store.
The design rule we set early was: a second client requires no code change. The entire tenant is one JSON file. It holds the client name, the module list, the prompt library, and the accessVersion integer. The proxy reads it at startup and fails the build on a malformed file, so a typo in the content file never becomes a runtime error.
Adding client two is: write the JSON, set the password in the environment, deploy. Everything else already exists.
Why This Lives in Our Own Site
We could have built this inside a course tool. We didn't because this isn't a course. The portal is a deliverable extension of the engagement. It lives next to the audit deliverable pattern we already use for PDF scopes. Clients don't need to log into a third service. We don't need to pay a seat fee or wrestle with a platform's branding constraints.
The robots.txt disallows the /training/ path. The portal isn't for SEO. It's for the client who built their business around a product and now needs to make their team fluent in the AI layer we added to it.
Training is the last mile of the build. We stopped skipping it.