← ALL THINKING
BUILD NOTESJUL 2026

Customer portals in minutes, not months

There's a conversation that happens in every enterprise B2B deal, usually right after the handshake. The customer says some version of: "This is great. Now, can we have our own one?"

Their own storefront. Their logo in the corner, their colours, their negotiated range and nothing else, their approval rules, their teams, and login through their own identity provider because their IT team stopped approving new passwords in 2019. Every serious B2B customer wants a walled garden, and they're right to want it. A hospital group ordering scrubs and a mining contractor ordering PPE should not be squinting at the same generic storefront.

The problem is what it costs to say yes. The traditional answers are a bespoke build per customer, which means every new logo is a project with a timeline and an invoice, or a single shared portal with a customer dropdown, which is nobody's walled garden at all. We wanted a third answer: a platform where the business can launch a fully walled-off, fully branded customer portal in minutes, without a developer in the loop.

This is how we designed it.

Stop building portals. Build the thing that builds portals.

The mental shift that unlocks everything: the deliverable isn't a portal, it's a portal factory. One baseline repo, built once and improved forever, that can be stamped into any number of customer instances. The moment you frame it that way, the design questions get sharp. What has to be different per customer? What must never be duplicated? And what turns a new customer from a code change into a row of configuration?

Our answer leans on a stack of commercetools primitives that were practically designed for this job, layered so that each one owns a different part of "their own one".

The anatomy of a walled-off portal: four platform primitives, layered, make one customer's world.

The Store is the walls. Everything commercial about a customer, their carts, orders, channel and pricing, is scoped to their store. Tenant data never brushes against another tenant's, not because application code filters carefully, but because the platform's own boundary makes crossing impossible. Walls you get from the platform beat walls you have to remember to enforce.

The Product Selection is their range. The master catalogue might hold thousands of products; this customer sees the curated slice they've agreed with you, managed centrally or self-served by their own admins. Their staff never scroll past a product that isn't theirs, which sounds cosmetic and is actually the whole point of a curated program.

The Business Units are their organisation. Teams, departments and divisions, with roles and permissions per person: who can browse, who can buy, who approves, who administers. Approval chains, spend limits and entitlements all hang off an org tree the customer manages themselves.

And the fourth layer is where it gets interesting.

The feature flag problem, and the object that solved it

Here's the trap hiding inside "their own one": enterprise customers don't just differ in branding, they differ in behaviour. One runs points-based entitlements and must never show a dollar price. Another wants open self-registration for casual staff. One needs buy-on-behalf with cost-centre fields, another bans it. Multiply that by every customer and you have the classic feature flag problem, with a twist: the flags aren't per release or per experiment, they're per organisation, forever.

We considered the usual suspects. Environment variables per deployment mean a redeploy every time a customer changes their mind, which is hostile to the whole minutes-not-months ambition. A commercial flag service does per-org targeting fine, but adds a subscription, an SDK and an external dependency for what is, honestly, structured data about a tenant. A separate config database means new infrastructure to host, secure and back up alongside a platform that was supposed to be composable.

Then the observation that made it simple: commercetools already ships a place to put arbitrary structured data. Custom objects. So each tenant gets exactly one portal-config object, keyed by their slug, and that object is the portal's personality: branding, feature flags, homepage content, menus and footers, shipping methods, department options, buy-on-behalf field visibility, page routes, registration rules, even the maintenance-mode switch.

Feature flags without a flag service: one config object per tenant — same build, different portal.

Here's the part of the architecture people assume wrong, so it's worth being precise. There is one codebase, but not one deployment. Every customer gets their own project on Vercel, stamped from the same baseline repo, bound to their slug and their domain. At boot it knows exactly who it is, loads that tenant's config (cached aggressively, invalidated the moment an admin saves a change), and renders their world. Flip "hide pricing" for the mining customer and their portal changes behaviour in seconds. No deploy, no release train, no flag-service bill. And if the config ever can't be fetched, the portal fails open to sensible defaults, because a degraded portal beats a down one.

That per-customer deployment is a deliberate choice, and it's what makes the whole thing feel premium rather than templated. Each portal lives exactly as if a developer had custom-built it for that customer: its own infrastructure, its own domain, its own scaling, its own environment, no noisy neighbours sharing its runtime. When their IT team asks "is this a dedicated instance?", the answer is simply yes. Bespoke in every way the customer can see and touch; industrialised in every way that keeps it fast and cheap to operate.

The quiet superpower is that you get that bespoke feel and the fleet effect. Because every project is stamped from the same code, a bug fixed once is fixed everywhere the moment the pipeline rolls the release across the fleet, and a feature shipped once reaches every customer, gated behind their config. The portals improve in lockstep, which is the exact opposite of the true bespoke-build world, where every customer's fork slowly rots at its own speed.

Identity is a wall too

The walls aren't finished until login is walled. Each customer gets their own organisation in Kinde, our identity layer, which means their staff sign in through their front door: their SSO, their identity provider, their user lifecycle. When the customer's IT team deprovisions a departing employee in their own directory, that person's portal access dies with it, which is precisely what an enterprise security review wants to hear.

On the commerce side, each signed-in user maps to an associate in the customer's business unit tree, carrying their roles: buyer, approver, admin. Identity says who you are; the org tree says what you may do. Two systems, one clean seam.

The provisioner: launch as a form, not a project

So a customer's world is: a store, a product selection, a business unit root, a Kinde organisation, and a portal-config object, plus a deployment. None of those steps needs judgement. All of them need care. That combination, boring but exacting, is exactly what you automate.

Launch as an operations task: from new customer to a live, walled-off portal, in minutes.

So the launch experience is a form inside a commercetools custom application, in the Merchant Center the operations team already lives in. That custom app is the window onto the whole machine: it fronts the CI/CD pipeline the same way a checkout fronts a warehouse. Customer name, slug, starting range, domain, the features they've signed up for. Submit. The provisioner creates the store, seeds the product selection, plants the business unit root, creates the Kinde organisation, and writes the portal-config object. Then the pipeline takes over: it stamps a brand-new Vercel project from the baseline repo, binds the tenant slug, syncs the environment, attaches the domain, and deploys. Minutes later, portal.acmemining.com.au is live, branded, walled off, and standing on its own infrastructure, ready for their admin to invite the first user.

One engineering rule made the whole thing trustworthy: the provisioner is idempotent. Re-running it against a half-created tenant repairs and completes; it never duplicates. That single property turns provisioning from a scary script someone runs carefully into a button anyone can press twice.

Launching a customer portal stops being a project with a kickoff meeting. It becomes a task someone does between other tasks, the same morning the contract is signed.

What this shape gives you

Step back and the wins compound. Sales can promise "your own portal, live this week" and mean today. Operations owns launches without engineering in the critical path. Engineering ships one codebase with one test suite instead of shepherding a herd of forks. Each customer gets genuine isolation: catalogue walls from product selections, data walls from stores, identity walls from Kinde organisations, behaviour walls from config. And the platform gets better for everyone at once, every release.

The deeper pattern is the same one we keep finding across B2B: take the primitives your platform already gives you, stores, business units, product selections, custom objects, and layer them deliberately instead of building beside them. Then automate the layering until launch is boring. The businesses that win enterprise B2B won't be the ones that can build a beautiful portal. They'll be the ones that can build the hundredth portal before lunch.

AFKN designs and builds multi-tenant B2B portal platforms on commercetools, Kinde and Vercel. If your enterprise customers keep asking for "their own one" and every yes costs you a project, we should talk: hello@afkn.com.au